diff --git a/.github/workflows/build-docs.yaml b/.github/workflows/build-docs.yaml index 6909665..d7dd430 100644 --- a/.github/workflows/build-docs.yaml +++ b/.github/workflows/build-docs.yaml @@ -16,22 +16,21 @@ concurrency: jobs: build-docs: + if: ${{ !github.event.release.prerelease }} runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 10.15.1 + uses: pnpm/action-setup@v5 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: 18 cache: pnpm cache-dependency-path: pnpm-lock.yaml - name: Setup Pages - uses: actions/configure-pages@v5 + uses: actions/configure-pages@v6 - name: Install dependencies run: pnpm install --frozen-lockfile - name: Build docs @@ -42,6 +41,7 @@ jobs: path: docs deploy: + if: ${{ !github.event.release.prerelease }} environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} @@ -50,4 +50,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e0ba1e0 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,51 @@ +name: CI + +on: + push: + branches: [master, beta] + pull_request: + branches: [master, beta] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + ci: + name: CI (Node ${{ matrix.node-version }}) + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [18, 20, 22] + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup pnpm + uses: pnpm/action-setup@v5 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: ${{ matrix.node-version }} + cache: pnpm + cache-dependency-path: pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Lint + run: pnpm lint:ci + + - name: Test (unit only) + run: pnpm test:ci + + - name: Build + run: pnpm tsup diff --git a/.github/workflows/release-please-beta.yml b/.github/workflows/release-please-beta.yml new file mode 100644 index 0000000..d47c753 --- /dev/null +++ b/.github/workflows/release-please-beta.yml @@ -0,0 +1,235 @@ +name: Beta Release + +on: + workflow_run: + workflows: ["CI"] + branches: [beta] + types: [completed] + +permissions: + contents: write + pull-requests: write + issues: write + actions: write + statuses: write + id-token: write + +concurrency: + group: release-please-beta + cancel-in-progress: false + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + release-please: + name: Release Please (Beta) + if: >- + github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.event == 'push' + runs-on: ubuntu-latest + outputs: + release_created: ${{ steps.release.outputs.release_created }} + tag_name: ${{ steps.release.outputs.tag_name }} + pr_head_sha: ${{ steps.pr-sha.outputs.sha }} + steps: + - name: Release Please + id: release + uses: googleapis/release-please-action@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + target-branch: beta + config-file: release-please-config-beta.json + manifest-file: .release-please-manifest-beta.json + + - name: Update release title with date + if: ${{ steps.release.outputs.release_created }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ steps.release.outputs.tag_name }} + REPO: ${{ github.repository }} + run: | + DATE=$(date -u +"%Y/%m/%d") + gh release edit "$TAG" --repo "$REPO" --title "$TAG ($DATE)" + + - name: Get PR head SHA + id: pr-sha + if: ${{ !steps.release.outputs.release_created }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + run: | + SHA=$(gh pr list --repo "$REPO" --head release-please--branches--beta --state open --json headRefOid --jq '.[0].headRefOid // empty') + echo "sha=${SHA:-}" >> "$GITHUB_OUTPUT" + + test-release-pr: + name: Test (Beta Release PR) + needs: release-please + if: ${{ !needs.release-please.outputs.release_created && needs.release-please.outputs.pr_head_sha != '' }} + runs-on: ubuntu-latest + steps: + - name: Set pending status + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SHA: ${{ needs.release-please.outputs.pr_head_sha }} + REPO: ${{ github.repository }} + run: | + gh api "repos/$REPO/statuses/$SHA" \ + -f state=pending -f context="Test (Beta)" -f description="Running tests..." \ + || echo "::warning::Failed to set pending status on $SHA" + + - name: Checkout + uses: actions/checkout@v6 + with: + ref: ${{ needs.release-please.outputs.pr_head_sha }} + + - name: Setup pnpm + uses: pnpm/action-setup@v5 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 18 + cache: pnpm + cache-dependency-path: pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Test (unit only) + run: pnpm test:ci + + - name: Build + run: pnpm tsup + + - name: Report success + if: success() + continue-on-error: true + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SHA: ${{ needs.release-please.outputs.pr_head_sha }} + REPO: ${{ github.repository }} + run: | + gh api "repos/$REPO/statuses/$SHA" \ + -f state=success -f context="Test (Beta)" -f description="Tests passed" \ + || echo "::warning::Failed to report success status on $SHA" + + - name: Report failure + if: failure() + continue-on-error: true + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SHA: ${{ needs.release-please.outputs.pr_head_sha }} + REPO: ${{ github.repository }} + run: | + gh api "repos/$REPO/statuses/$SHA" \ + -f state=failure -f context="Test (Beta)" -f description="Tests failed" \ + || echo "::warning::Failed to report failure status on $SHA" + + lint-release-pr: + name: Lint (Beta Release PR) + needs: release-please + if: ${{ !needs.release-please.outputs.release_created && needs.release-please.outputs.pr_head_sha != '' }} + runs-on: ubuntu-latest + steps: + - name: Set pending status + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SHA: ${{ needs.release-please.outputs.pr_head_sha }} + REPO: ${{ github.repository }} + run: | + gh api "repos/$REPO/statuses/$SHA" \ + -f state=pending -f context="Lint (Beta)" -f description="Running lint..." \ + || echo "::warning::Failed to set pending status on $SHA" + + - name: Checkout + uses: actions/checkout@v6 + with: + ref: ${{ needs.release-please.outputs.pr_head_sha }} + + - name: Setup pnpm + uses: pnpm/action-setup@v5 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 18 + cache: pnpm + cache-dependency-path: pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Lint + run: pnpm lint:ci + + - name: Report success + if: success() + continue-on-error: true + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SHA: ${{ needs.release-please.outputs.pr_head_sha }} + REPO: ${{ github.repository }} + run: | + gh api "repos/$REPO/statuses/$SHA" \ + -f state=success -f context="Lint (Beta)" -f description="Lint passed" \ + || echo "::warning::Failed to report success status on $SHA" + + - name: Report failure + if: failure() + continue-on-error: true + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SHA: ${{ needs.release-please.outputs.pr_head_sha }} + REPO: ${{ github.repository }} + run: | + gh api "repos/$REPO/statuses/$SHA" \ + -f state=failure -f context="Lint (Beta)" -f description="Lint failed" \ + || echo "::warning::Failed to report failure status on $SHA" + + publish: + name: Publish to npm (Beta) + needs: release-please + if: ${{ needs.release-please.outputs.release_created == 'true' }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + ref: ${{ needs.release-please.outputs.tag_name }} + + - name: Setup pnpm + uses: pnpm/action-setup@v5 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 18 + cache: pnpm + cache-dependency-path: pnpm-lock.yaml + registry-url: https://registry.npmjs.org + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build + run: pnpm tsup + + - name: Publish with provenance (beta) + run: npm publish --provenance --access public --tag beta + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Report publish failure + if: failure() + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ needs.release-please.outputs.tag_name }} + REPO: ${{ github.repository }} + run: | + echo "::error::npm publish failed for $TAG" + gh release edit "$TAG" --repo "$REPO" \ + --notes "$(gh release view "$TAG" --repo "$REPO" --json body --jq .body) + + --- + > **WARNING**: npm publish failed. Package was NOT published to npm." || true diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 0000000..ba0a5ab --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,210 @@ +name: Release + +on: + workflow_run: + workflows: ["CI"] + branches: [master] + types: [completed] + +permissions: + contents: write + pull-requests: write + issues: write + actions: write + statuses: write + id-token: write + +concurrency: + group: release-please-master + cancel-in-progress: false + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + release-please: + name: Release Please + if: >- + github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.event == 'push' + runs-on: ubuntu-latest + outputs: + release_created: ${{ steps.release.outputs.release_created }} + tag_name: ${{ steps.release.outputs.tag_name }} + pr_head_sha: ${{ steps.pr-sha.outputs.sha }} + steps: + - name: Release Please + id: release + uses: googleapis/release-please-action@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Update release title with date + if: ${{ steps.release.outputs.release_created }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ steps.release.outputs.tag_name }} + REPO: ${{ github.repository }} + run: | + DATE=$(date -u +"%Y/%m/%d") + gh release edit "$TAG" --repo "$REPO" --title "$TAG ($DATE)" + + - name: Get PR head SHA + id: pr-sha + if: ${{ !steps.release.outputs.release_created }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + run: | + SHA=$(gh pr list --repo "$REPO" --head release-please--branches--master --state open --json headRefOid --jq '.[0].headRefOid // empty') + echo "sha=${SHA:-}" >> "$GITHUB_OUTPUT" + + test-release-pr: + name: Test (Release PR) + needs: release-please + if: ${{ !needs.release-please.outputs.release_created && needs.release-please.outputs.pr_head_sha != '' }} + runs-on: ubuntu-latest + steps: + - name: Set pending status + continue-on-error: true + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SHA: ${{ needs.release-please.outputs.pr_head_sha }} + REPO: ${{ github.repository }} + run: | + gh api "repos/$REPO/statuses/$SHA" \ + -f state=pending -f context="Test" -f description="Running tests..." + + - name: Checkout + uses: actions/checkout@v6 + with: + ref: ${{ needs.release-please.outputs.pr_head_sha }} + + - name: Setup pnpm + uses: pnpm/action-setup@v5 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 18 + cache: pnpm + cache-dependency-path: pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Test (unit only) + run: pnpm test:ci + + - name: Build + run: pnpm tsup + + - name: Report success + if: success() + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SHA: ${{ needs.release-please.outputs.pr_head_sha }} + REPO: ${{ github.repository }} + run: | + gh api "repos/$REPO/statuses/$SHA" \ + -f state=success -f context="Test" -f description="Tests passed" + + - name: Report failure + if: failure() + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SHA: ${{ needs.release-please.outputs.pr_head_sha }} + REPO: ${{ github.repository }} + run: | + gh api "repos/$REPO/statuses/$SHA" \ + -f state=failure -f context="Test" -f description="Tests failed" + + lint-release-pr: + name: Lint (Release PR) + needs: release-please + if: ${{ !needs.release-please.outputs.release_created && needs.release-please.outputs.pr_head_sha != '' }} + runs-on: ubuntu-latest + steps: + - name: Set pending status + continue-on-error: true + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SHA: ${{ needs.release-please.outputs.pr_head_sha }} + REPO: ${{ github.repository }} + run: | + gh api "repos/$REPO/statuses/$SHA" \ + -f state=pending -f context="Lint" -f description="Running lint..." + + - name: Checkout + uses: actions/checkout@v6 + with: + ref: ${{ needs.release-please.outputs.pr_head_sha }} + + - name: Setup pnpm + uses: pnpm/action-setup@v5 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 18 + cache: pnpm + cache-dependency-path: pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Lint + run: pnpm lint:ci + + - name: Report success + if: success() + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SHA: ${{ needs.release-please.outputs.pr_head_sha }} + REPO: ${{ github.repository }} + run: | + gh api "repos/$REPO/statuses/$SHA" \ + -f state=success -f context="Lint" -f description="Lint passed" + + - name: Report failure + if: failure() + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SHA: ${{ needs.release-please.outputs.pr_head_sha }} + REPO: ${{ github.repository }} + run: | + gh api "repos/$REPO/statuses/$SHA" \ + -f state=failure -f context="Lint" -f description="Lint failed" + + publish: + name: Publish to npm + needs: release-please + if: ${{ needs.release-please.outputs.release_created == 'true' }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + ref: ${{ needs.release-please.outputs.tag_name }} + + - name: Setup pnpm + uses: pnpm/action-setup@v5 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 18 + cache: pnpm + cache-dependency-path: pnpm-lock.yaml + registry-url: https://registry.npmjs.org + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build + run: pnpm tsup + + - name: Publish with provenance + run: npm publish --provenance --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.release-please-manifest-beta.json b/.release-please-manifest-beta.json new file mode 100644 index 0000000..1f30756 --- /dev/null +++ b/.release-please-manifest-beta.json @@ -0,0 +1,3 @@ +{ + ".": "5.5.4" +} diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..1f30756 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "5.5.4" +} diff --git a/package.json b/package.json index 1332454..013eeb3 100644 --- a/package.json +++ b/package.json @@ -34,8 +34,10 @@ "build": "pnpm lint && tsup", "docs": "typedoc --entryPointStrategy expand ./src", "lint": "biome check --write", + "lint:ci": "biome check", "dev": "tsup --watch", "test": "vitest run", + "test:ci": "vitest run --exclude '**/*.e2e.test.ts'", "test:watch": "vitest" }, "dependencies": { diff --git a/release-please-config-beta.json b/release-please-config-beta.json new file mode 100644 index 0000000..adca849 --- /dev/null +++ b/release-please-config-beta.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "packages": { + ".": { + "release-type": "node", + "prerelease": true, + "prerelease-type": "beta", + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": true, + "changelog-sections": [ + { "type": "feat", "section": "Features" }, + { "type": "fix", "section": "Bug Fixes" }, + { "type": "perf", "section": "Performance Improvements" }, + { "type": "deps", "section": "Dependencies" }, + { "type": "docs", "section": "Documentation", "hidden": true }, + { "type": "style", "section": "Styles", "hidden": true }, + { "type": "refactor", "section": "Code Refactoring", "hidden": true }, + { "type": "test", "section": "Tests", "hidden": true }, + { "type": "ci", "section": "CI/CD", "hidden": true }, + { "type": "chore", "section": "Miscellaneous", "hidden": true } + ], + "exclude-paths": [ + "README.md", + "CLAUDE.md", + "AGENTS.md", + "CHANGELOG.md", + ".github", + "examples", + "release-please-config.json", + "release-please-config-beta.json", + ".release-please-manifest.json", + ".release-please-manifest-beta.json" + ] + } + } +} diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..549bcb8 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "packages": { + ".": { + "release-type": "node", + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": true, + "changelog-sections": [ + { "type": "feat", "section": "Features" }, + { "type": "fix", "section": "Bug Fixes" }, + { "type": "perf", "section": "Performance Improvements" }, + { "type": "deps", "section": "Dependencies" }, + { "type": "docs", "section": "Documentation", "hidden": true }, + { "type": "style", "section": "Styles", "hidden": true }, + { "type": "refactor", "section": "Code Refactoring", "hidden": true }, + { "type": "test", "section": "Tests", "hidden": true }, + { "type": "ci", "section": "CI/CD", "hidden": true }, + { "type": "chore", "section": "Miscellaneous", "hidden": true } + ], + "exclude-paths": [ + "README.md", + "CLAUDE.md", + "AGENTS.md", + "CHANGELOG.md", + ".github", + "examples", + "release-please-config.json", + "release-please-config-beta.json", + ".release-please-manifest.json", + ".release-please-manifest-beta.json" + ] + } + } +}