From d849ce64c16076a62c14f859385d0d758be114a3 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Fri, 20 Mar 2026 10:35:37 -0600 Subject: [PATCH 1/5] feat: add release infrastructure with changesets Add the complete release pipeline: - Changesets for versioning (stable, canary, snapshot channels) - Releaser script for npm publish, git tags, and GitHub Releases - CI workflows for stable/canary releases and PR snapshot comments --- .changeset/README.md | 11 ++ .changeset/config.json | 14 +++ .github/workflows/release.yml | 188 ++++++++++++++++++++++++++++++ .github/workflows/snapshot.yml | 179 +++++++++++++++++++++++++++++ bun.lock | 203 ++++++++++++++++++++++++++++++++- package.json | 7 ++ scripts/check-release.ts | 22 ++++ scripts/lib/npm.ts | 18 +++ scripts/releaser/index.ts | 148 ++++++++++++++++++++++++ scripts/snapshot.ts | 59 ++++++++++ 10 files changed, 846 insertions(+), 3 deletions(-) create mode 100644 .changeset/README.md create mode 100644 .changeset/config.json create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/snapshot.yml create mode 100644 scripts/check-release.ts create mode 100644 scripts/lib/npm.ts create mode 100644 scripts/releaser/index.ts create mode 100644 scripts/snapshot.ts diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 000000000..5d1b929f2 --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,11 @@ +# Changesets + +This directory is used by [Changesets](https://github.com/changesets/changesets) to manage versioning and changelogs. + +To add a changeset, run: + +```sh +bunx changeset +``` + +This will prompt you to select the packages that changed and the type of change (patch, minor, major). A markdown file will be created in this directory describing the change — commit it with your PR. diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 000000000..3f9f18799 --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json", + "changelog": "@changesets/cli/changelog", + "commit": false, + "linked": [], + "access": "public", + "baseBranch": "origin/main", + "updateInternalDependencies": "patch", + "ignore": ["@clerk/cli-core"], + "snapshot": { + "useCalculatedVersion": true, + "prereleaseTemplate": "{tag}.v{datetime}" + } +} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..c9a2eb3fc --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,188 @@ +name: Release + +on: + push: + branches: [main] + +concurrency: + group: release + cancel-in-progress: false + +permissions: + contents: read + +jobs: + versioning: + runs-on: blacksmith-2vcpu-ubuntu-2404 + timeout-minutes: 5 + permissions: + contents: write + pull-requests: write + outputs: + release_created: ${{ steps.check.outputs.release_created }} + version: ${{ steps.check.outputs.version }} + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + - run: bun install --frozen-lockfile + + - id: changesets + uses: changesets/action@v1 + with: + version: bun run version-packages + commit: "ci(repo): version packages" + title: "ci(repo): Version Packages" + env: + GITHUB_TOKEN: ${{ github.token }} + + - name: Check if release needed + id: check + run: bun run scripts/check-release.ts + + # ─── Stable release ──────────────────────────────────────────────── + + build: + needs: versioning + if: ${{ needs.versioning.outputs.release_created == 'true' }} + uses: ./.github/workflows/build-binaries.yml + with: + version: ${{ needs.versioning.outputs.version }} + ref: ${{ github.sha }} + artifact-prefix: clerk + + smoke-test: + needs: [versioning, build] + uses: ./.github/workflows/smoke-test.yml + with: + version: ${{ needs.versioning.outputs.version }} + artifact-prefix: clerk + preset: stable + + publish-npm: + needs: [versioning, build, smoke-test] + # Must run on GitHub-hosted runner for npm OIDC trusted publishing + runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + contents: write + id-token: write + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + - uses: actions/setup-node@v4 + with: + node-version: "22" + registry-url: "https://registry.npmjs.org" + - name: Upgrade npm for OIDC trusted publishing + run: npm install -g npm@11 + - run: bun install --frozen-lockfile + + - uses: actions/download-artifact@v4 + with: + pattern: clerk-* + path: dist/artifacts + + - name: Publish packages + run: bun run release + env: + ARTIFACTS_DIR: ${{ github.workspace }}/dist/artifacts + GH_TOKEN: ${{ github.token }} + + upload-github-assets: + needs: [versioning, build, smoke-test, publish-npm] + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: write + steps: + - uses: actions/download-artifact@v4 + with: + pattern: clerk-* + path: dist/artifacts + + - name: Upload binaries to GitHub Release + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + run: | + tag="v${{ needs.versioning.outputs.version }}" + for dir in dist/artifacts/clerk-*/; do + target=${dir#dist/artifacts/clerk-} && target=${target%/} + ext=""; [[ "$target" == win32-* ]] && ext=".exe" + gh release upload "$tag" "${dir}clerk${ext}#clerk-${target}${ext}" + done + + # ─── Canary release ──────────────────────────────────────────────── + + canary-version: + needs: versioning + if: ${{ needs.versioning.outputs.release_created != 'true' }} + runs-on: blacksmith-2vcpu-ubuntu-2404 + timeout-minutes: 5 + outputs: + version: ${{ steps.version.outputs.version }} + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + - run: bun install --frozen-lockfile + + - name: Version packages for canary + id: version + run: | + bun run version-packages:canary + version=$(jq -r '.version' packages/cli/package.json) + echo "version=${version}" >> "$GITHUB_OUTPUT" + + canary-build: + needs: canary-version + uses: ./.github/workflows/build-binaries.yml + with: + version: ${{ needs.canary-version.outputs.version }} + ref: ${{ github.sha }} + artifact-prefix: clerk-canary + + canary-smoke-test: + needs: [canary-version, canary-build] + uses: ./.github/workflows/smoke-test.yml + with: + version: ${{ needs.canary-version.outputs.version }} + artifact-prefix: clerk-canary + preset: canary + + canary-publish: + needs: [canary-version, canary-build, canary-smoke-test] + # Must run on GitHub-hosted runner for npm OIDC trusted publishing + runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + - uses: actions/setup-node@v4 + with: + node-version: "22" + registry-url: "https://registry.npmjs.org" + - name: Upgrade npm for OIDC trusted publishing + run: npm install -g npm@11 + - run: bun install --frozen-lockfile + + - uses: actions/download-artifact@v4 + with: + pattern: clerk-canary-* + path: dist/artifacts + + - name: Rename artifact directories + run: | + cd dist/artifacts + for dir in clerk-canary-*/; do + target=${dir#clerk-canary-} && target=${target%/} + mv "$dir" "clerk-${target}" + done + + - name: Publish canary packages + run: bun run release:canary --version "$CANARY_VERSION" + env: + CANARY_VERSION: ${{ needs.canary-version.outputs.version }} + ARTIFACTS_DIR: ${{ github.workspace }}/dist/artifacts diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml new file mode 100644 index 000000000..076864c1f --- /dev/null +++ b/.github/workflows/snapshot.yml @@ -0,0 +1,179 @@ +name: Snapshot Release + +on: + issue_comment: + types: [created] + +permissions: + contents: read + pull-requests: write + +jobs: + snapshot: + # Intentionally restricted to MEMBER and OWNER — external collaborators + # (COLLABORATOR association) cannot trigger snapshots. This is a deliberate + # security boundary: snapshot publishing has npm write access via OIDC. + if: >- + github.event.issue.pull_request && + startsWith(github.event.comment.body, '!snapshot') && + contains(fromJSON('["MEMBER","OWNER"]'), github.event.comment.author_association) + runs-on: blacksmith-2vcpu-ubuntu-2404 + timeout-minutes: 5 + outputs: + version: ${{ steps.version.outputs.version }} + sha: ${{ steps.pr.outputs.sha }} + steps: + - name: React to comment + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + run: | + gh api "repos/${GH_REPO}/issues/comments/${{ github.event.comment.id }}/reactions" \ + -f content=eyes --silent + + - name: Get PR head SHA + id: pr + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + run: | + sha=$(gh api "repos/${GH_REPO}/pulls/${{ github.event.issue.number }}" --jq '.head.sha') + echo "sha=${sha}" >> "$GITHUB_OUTPUT" + + - uses: actions/checkout@v4 + with: + ref: ${{ steps.pr.outputs.sha }} + - uses: oven-sh/setup-bun@v2 + - run: bun install --frozen-lockfile + + - name: Compute snapshot version + id: version + env: + COMMENT_BODY: ${{ github.event.comment.body }} + run: | + # Extract optional name from "!snapshot " — default to "snapshot" + name=$(echo "$COMMENT_BODY" | sed 's/^!snapshot[[:space:]]*//' | tr -cd 'a-zA-Z0-9-') + if [ -z "$name" ]; then name="snapshot"; fi + bun run version-packages:snapshot -- "$name" + version=$(jq -r '.version' packages/cli/package.json) + echo "version=${version}" >> "$GITHUB_OUTPUT" + + build: + needs: snapshot + uses: ./.github/workflows/build-binaries.yml + with: + version: ${{ needs.snapshot.outputs.version }} + ref: ${{ needs.snapshot.outputs.sha }} + artifact-prefix: clerk-snapshot + + smoke-test: + needs: [snapshot, build] + uses: ./.github/workflows/smoke-test.yml + with: + version: ${{ needs.snapshot.outputs.version }} + artifact-prefix: clerk-snapshot + preset: snapshot + + publish: + needs: [snapshot, build, smoke-test] + # Must run on GitHub-hosted runner for npm OIDC trusted publishing + runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + contents: read + pull-requests: write + id-token: write + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ needs.snapshot.outputs.sha }} + - uses: oven-sh/setup-bun@v2 + - uses: actions/setup-node@v4 + with: + node-version: "22" + registry-url: "https://registry.npmjs.org" + - name: Upgrade npm for OIDC trusted publishing + run: npm install -g npm@11 + - run: bun install --frozen-lockfile + + - uses: actions/download-artifact@v4 + with: + pattern: clerk-snapshot-* + path: dist/artifacts + + - name: Rename artifact directories + run: | + cd dist/artifacts + for dir in clerk-snapshot-*/; do + target=${dir#clerk-snapshot-} && target=${target%/} + mv "$dir" "clerk-${target}" + done + + - name: Publish snapshot packages + run: bun run release:snapshot --version "$SNAPSHOT_VERSION" + env: + SNAPSHOT_VERSION: ${{ needs.snapshot.outputs.version }} + ARTIFACTS_DIR: ${{ github.workspace }}/dist/artifacts + + - name: React with rocket + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + run: | + gh api "repos/${GH_REPO}/issues/comments/${{ github.event.comment.id }}/reactions" \ + -f content=rocket --silent + + - name: Post installation comment + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + SNAPSHOT_VERSION: ${{ needs.snapshot.outputs.version }} + SNAPSHOT_SHA: ${{ needs.snapshot.outputs.sha }} + PR_NUMBER: ${{ github.event.issue.number }} + run: | + short_sha=$(echo "${SNAPSHOT_SHA}" | cut -c1-7) + { + echo '## Snapshot published' + echo '' + echo '```sh' + echo "npm install -g clerk@${SNAPSHOT_VERSION}" + echo '```' + echo '' + echo '| Package | Version |' + echo '|---------|---------|' + echo "| \`clerk\` | \`${SNAPSHOT_VERSION}\` |" + echo '' + echo "> Published from ${short_sha}" + } > /tmp/comment-body.md + gh pr comment "${PR_NUMBER}" --repo "${GH_REPO}" --body-file /tmp/comment-body.md + + notify-failure: + needs: [snapshot, build, smoke-test, publish] + if: failure() + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + pull-requests: write + steps: + - name: React with confused emoji + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + run: | + gh api "repos/${GH_REPO}/issues/comments/${{ github.event.comment.id }}/reactions" \ + -f content=confused --silent + + - name: Post failure comment + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + PR_NUMBER: ${{ github.event.issue.number }} + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + run: | + { + echo '## Snapshot failed' + echo '' + echo "The snapshot publish workflow failed. [View the workflow run](${RUN_URL}) for details." + } > /tmp/comment-body.md + gh pr comment "${PR_NUMBER}" --repo "${GH_REPO}" --body-file /tmp/comment-body.md diff --git a/bun.lock b/bun.lock index c19825091..2a043c81b 100644 --- a/bun.lock +++ b/bun.lock @@ -5,6 +5,7 @@ "": { "name": "marseille", "devDependencies": { + "@changesets/cli": "^2.29.4", "@types/bun": "^1.3.9", "nano-staged": "^0.9.0", "oxfmt": "^0.36.0", @@ -33,6 +34,42 @@ }, }, "packages": { + "@babel/runtime": ["@babel/runtime@7.29.2", "", {}, "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g=="], + + "@changesets/apply-release-plan": ["@changesets/apply-release-plan@7.1.0", "", { "dependencies": { "@changesets/config": "^3.1.3", "@changesets/get-version-range-type": "^0.4.0", "@changesets/git": "^3.0.4", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "detect-indent": "^6.0.0", "fs-extra": "^7.0.1", "lodash.startcase": "^4.4.0", "outdent": "^0.5.0", "prettier": "^2.7.1", "resolve-from": "^5.0.0", "semver": "^7.5.3" } }, "sha512-yq8ML3YS7koKQ/9bk1PqO0HMzApIFNwjlwCnwFEXMzNe8NpzeeYYKCmnhWJGkN8g7E51MnWaSbqRcTcdIxUgnQ=="], + + "@changesets/assemble-release-plan": ["@changesets/assemble-release-plan@6.0.9", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.3", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "semver": "^7.5.3" } }, "sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ=="], + + "@changesets/changelog-git": ["@changesets/changelog-git@0.2.1", "", { "dependencies": { "@changesets/types": "^6.1.0" } }, "sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q=="], + + "@changesets/cli": ["@changesets/cli@2.30.0", "", { "dependencies": { "@changesets/apply-release-plan": "^7.1.0", "@changesets/assemble-release-plan": "^6.0.9", "@changesets/changelog-git": "^0.2.1", "@changesets/config": "^3.1.3", "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.3", "@changesets/get-release-plan": "^4.0.15", "@changesets/git": "^3.0.4", "@changesets/logger": "^0.1.1", "@changesets/pre": "^2.0.2", "@changesets/read": "^0.6.7", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@changesets/write": "^0.4.0", "@inquirer/external-editor": "^1.0.2", "@manypkg/get-packages": "^1.1.3", "ansi-colors": "^4.1.3", "enquirer": "^2.4.1", "fs-extra": "^7.0.1", "mri": "^1.2.0", "package-manager-detector": "^0.2.0", "picocolors": "^1.1.0", "resolve-from": "^5.0.0", "semver": "^7.5.3", "spawndamnit": "^3.0.1", "term-size": "^2.1.0" }, "bin": { "changeset": "bin.js" } }, "sha512-5D3Nk2JPqMI1wK25pEymeWRSlSMdo5QOGlyfrKg0AOufrUcjEE3RQgaCpHoBiM31CSNrtSgdJ0U6zL1rLDDfBA=="], + + "@changesets/config": ["@changesets/config@3.1.3", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.3", "@changesets/logger": "^0.1.1", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "fs-extra": "^7.0.1", "micromatch": "^4.0.8" } }, "sha512-vnXjcey8YgBn2L1OPWd3ORs0bGC4LoYcK/ubpgvzNVr53JXV5GiTVj7fWdMRsoKUH7hhhMAQnsJUqLr21EncNw=="], + + "@changesets/errors": ["@changesets/errors@0.2.0", "", { "dependencies": { "extendable-error": "^0.1.5" } }, "sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow=="], + + "@changesets/get-dependents-graph": ["@changesets/get-dependents-graph@2.1.3", "", { "dependencies": { "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "picocolors": "^1.1.0", "semver": "^7.5.3" } }, "sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ=="], + + "@changesets/get-release-plan": ["@changesets/get-release-plan@4.0.15", "", { "dependencies": { "@changesets/assemble-release-plan": "^6.0.9", "@changesets/config": "^3.1.3", "@changesets/pre": "^2.0.2", "@changesets/read": "^0.6.7", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3" } }, "sha512-Q04ZaRPuEVZtA+auOYgFaVQQSA98dXiVe/yFaZfY7hoSmQICHGvP0TF4u3EDNHWmmCS4ekA/XSpKlSM2PyTS2g=="], + + "@changesets/get-version-range-type": ["@changesets/get-version-range-type@0.4.0", "", {}, "sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ=="], + + "@changesets/git": ["@changesets/git@3.0.4", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@manypkg/get-packages": "^1.1.3", "is-subdir": "^1.1.1", "micromatch": "^4.0.8", "spawndamnit": "^3.0.1" } }, "sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw=="], + + "@changesets/logger": ["@changesets/logger@0.1.1", "", { "dependencies": { "picocolors": "^1.1.0" } }, "sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg=="], + + "@changesets/parse": ["@changesets/parse@0.4.3", "", { "dependencies": { "@changesets/types": "^6.1.0", "js-yaml": "^4.1.1" } }, "sha512-ZDmNc53+dXdWEv7fqIUSgRQOLYoUom5Z40gmLgmATmYR9NbL6FJJHwakcCpzaeCy+1D0m0n7mT4jj2B/MQPl7A=="], + + "@changesets/pre": ["@changesets/pre@2.0.2", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "fs-extra": "^7.0.1" } }, "sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug=="], + + "@changesets/read": ["@changesets/read@0.6.7", "", { "dependencies": { "@changesets/git": "^3.0.4", "@changesets/logger": "^0.1.1", "@changesets/parse": "^0.4.3", "@changesets/types": "^6.1.0", "fs-extra": "^7.0.1", "p-filter": "^2.1.0", "picocolors": "^1.1.0" } }, "sha512-D1G4AUYGrBEk8vj8MGwf75k9GpN6XL3wg8i42P2jZZwFLXnlr2Pn7r9yuQNbaMCarP7ZQWNJbV6XLeysAIMhTA=="], + + "@changesets/should-skip-package": ["@changesets/should-skip-package@0.1.2", "", { "dependencies": { "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3" } }, "sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw=="], + + "@changesets/types": ["@changesets/types@6.1.0", "", {}, "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA=="], + + "@changesets/write": ["@changesets/write@0.4.0", "", { "dependencies": { "@changesets/types": "^6.1.0", "fs-extra": "^7.0.1", "human-id": "^4.1.1", "prettier": "^2.7.1" } }, "sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q=="], + "@clerk/cli-core": ["@clerk/cli-core@workspace:packages/cli-core"], "@commander-js/extra-typings": ["@commander-js/extra-typings@14.0.0", "", { "peerDependencies": { "commander": "~14.0.0" } }, "sha512-hIn0ncNaJRLkZrxBIp5AsW/eXEHNKYQBh0aPdoUqNgD+Io3NIykQqpKFyKcuasZhicGaEZJX/JBSIkZ4e5x8Dg=="], @@ -49,7 +86,7 @@ "@inquirer/expand": ["@inquirer/expand@5.0.4", "", { "dependencies": { "@inquirer/core": "^11.1.1", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-0I/16YwPPP0Co7a5MsomlZLpch48NzYfToyqYAOWtBmaXSB80RiNQ1J+0xx2eG+Wfxt0nHtpEWSRr6CzNVnOGg=="], - "@inquirer/external-editor": ["@inquirer/external-editor@2.0.3", "", { "dependencies": { "chardet": "^2.1.1", "iconv-lite": "^0.7.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-LgyI7Agbda74/cL5MvA88iDpvdXI2KuMBCGRkbCl2Dg1vzHeOgs+s0SDcXV7b+WZJrv2+ERpWSM65Fpi9VfY3w=="], + "@inquirer/external-editor": ["@inquirer/external-editor@1.0.3", "", { "dependencies": { "chardet": "^2.1.1", "iconv-lite": "^0.7.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA=="], "@inquirer/figures": ["@inquirer/figures@2.0.3", "", {}, "sha512-y09iGt3JKoOCBQ3w4YrSJdokcD8ciSlMIWsD+auPu+OZpfxLuyz+gICAQ6GCBOmJJt4KEQGHuZSVff2jiNOy7g=="], @@ -69,6 +106,10 @@ "@inquirer/type": ["@inquirer/type@4.0.3", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw=="], + "@manypkg/find-root": ["@manypkg/find-root@1.1.0", "", { "dependencies": { "@babel/runtime": "^7.5.5", "@types/node": "^12.7.1", "find-up": "^4.1.0", "fs-extra": "^8.1.0" } }, "sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA=="], + + "@manypkg/get-packages": ["@manypkg/get-packages@1.1.3", "", { "dependencies": { "@babel/runtime": "^7.5.5", "@changesets/types": "^4.0.1", "@manypkg/find-root": "^1.1.0", "fs-extra": "^8.1.0", "globby": "^11.0.0", "read-yaml-file": "^1.1.0" } }, "sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A=="], + "@napi-rs/keyring": ["@napi-rs/keyring@1.2.0", "", { "optionalDependencies": { "@napi-rs/keyring-darwin-arm64": "1.2.0", "@napi-rs/keyring-darwin-x64": "1.2.0", "@napi-rs/keyring-freebsd-x64": "1.2.0", "@napi-rs/keyring-linux-arm-gnueabihf": "1.2.0", "@napi-rs/keyring-linux-arm64-gnu": "1.2.0", "@napi-rs/keyring-linux-arm64-musl": "1.2.0", "@napi-rs/keyring-linux-riscv64-gnu": "1.2.0", "@napi-rs/keyring-linux-x64-gnu": "1.2.0", "@napi-rs/keyring-linux-x64-musl": "1.2.0", "@napi-rs/keyring-win32-arm64-msvc": "1.2.0", "@napi-rs/keyring-win32-ia32-msvc": "1.2.0", "@napi-rs/keyring-win32-x64-msvc": "1.2.0" } }, "sha512-d0d4Oyxm+v980PEq1ZH2PmS6cvpMIRc17eYpiU47KgW+lzxklMu6+HOEOPmxrpnF/XQZ0+Q78I2mgMhbIIo/dg=="], "@napi-rs/keyring-darwin-arm64": ["@napi-rs/keyring-darwin-arm64@1.2.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-CA83rDeyONDADO25JLZsh3eHY8yTEtm/RS6ecPsY+1v+dSawzT9GywBMu2r6uOp1IEhQs/xAfxgybGAFr17lSA=="], @@ -95,6 +136,12 @@ "@napi-rs/keyring-win32-x64-msvc": ["@napi-rs/keyring-win32-x64-msvc@1.2.0", "", { "os": "win32", "cpu": "x64" }, "sha512-xFlx/TsmqmCwNU9v+AVnEJgoEAlBYgzFF5Ihz1rMpPAt4qQWWkMd4sCyM1gMJ1A/GnRqRegDiQpwaxGUHFtFbA=="], + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + "@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.36.0", "", { "os": "android", "cpu": "arm" }, "sha512-Z4yVHJWx/swHHjtr0dXrBZb6LxS+qNz1qdza222mWwPTUK4L790+5i3LTgjx3KYGBzcYpjaiZBw4vOx94dH7MQ=="], "@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.36.0", "", { "os": "android", "cpu": "arm64" }, "sha512-3ElCJRFNPQl7jexf2CAa9XmAm8eC5JPrIDSjc9jSchkVSFTEqyL0NtZinBB2h1a4i4JgP1oGl/5G5n8YR4FN8Q=="], @@ -175,10 +222,20 @@ "@types/node": ["@types/node@25.2.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ=="], - "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + "ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="], + + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "array-union": ["array-union@2.1.0", "", {}, "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="], + + "better-path-resolve": ["better-path-resolve@1.0.0", "", { "dependencies": { "is-windows": "^1.0.0" } }, "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + "bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="], "chardet": ["chardet@2.1.1", "", {}, "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ=="], @@ -189,42 +246,182 @@ "commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "detect-indent": ["detect-indent@6.1.0", "", {}, "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA=="], + + "dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="], + "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + "enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="], + "env-paths": ["env-paths@4.0.0", "", { "dependencies": { "is-safe-filename": "^0.1.0" } }, "sha512-pxP8eL2SwwaTRi/KHYwLYXinDs7gL3jxFcBYmEdYfZmZXbaVDvdppd0XBU8qVz03rDfKZMXg1omHCbsJjZrMsw=="], + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + + "extendable-error": ["extendable-error@0.1.7", "", {}, "sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg=="], + + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], + + "fs-extra": ["fs-extra@7.0.1", "", { "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw=="], + "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="], + "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "human-id": ["human-id@4.1.3", "", { "bin": { "human-id": "dist/cli.js" } }, "sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q=="], + "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + "is-safe-filename": ["is-safe-filename@0.1.1", "", {}, "sha512-4SrR7AdnY11LHfDKTZY1u6Ga3RuxZdl3YKWWShO5iyuG5h8QS4GD2tOb04peBJ5I7pXbR+CGBNEhTcwK+FzN3g=="], + "is-subdir": ["is-subdir@1.2.0", "", { "dependencies": { "better-path-resolve": "1.0.0" } }, "sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw=="], + + "is-windows": ["is-windows@1.0.2", "", {}, "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + + "jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], + + "locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], + + "lodash.startcase": ["lodash.startcase@4.4.0", "", {}, "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], + "mute-stream": ["mute-stream@3.0.0", "", {}, "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw=="], "nano-staged": ["nano-staged@0.9.0", "", { "dependencies": { "picocolors": "^1.0.0" }, "bin": { "nano-staged": "lib/bin.js" } }, "sha512-0JfyX4i0Vp5HhC9RDtJ1kp7psz8CFuS3Gya3Z6WZv//QCwA9dPzi1S803VdR0c0P6R7sSvweZ5mSJmYQ/N+loQ=="], + "outdent": ["outdent@0.5.0", "", {}, "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q=="], + "oxfmt": ["oxfmt@0.36.0", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.36.0", "@oxfmt/binding-android-arm64": "0.36.0", "@oxfmt/binding-darwin-arm64": "0.36.0", "@oxfmt/binding-darwin-x64": "0.36.0", "@oxfmt/binding-freebsd-x64": "0.36.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.36.0", "@oxfmt/binding-linux-arm-musleabihf": "0.36.0", "@oxfmt/binding-linux-arm64-gnu": "0.36.0", "@oxfmt/binding-linux-arm64-musl": "0.36.0", "@oxfmt/binding-linux-ppc64-gnu": "0.36.0", "@oxfmt/binding-linux-riscv64-gnu": "0.36.0", "@oxfmt/binding-linux-riscv64-musl": "0.36.0", "@oxfmt/binding-linux-s390x-gnu": "0.36.0", "@oxfmt/binding-linux-x64-gnu": "0.36.0", "@oxfmt/binding-linux-x64-musl": "0.36.0", "@oxfmt/binding-openharmony-arm64": "0.36.0", "@oxfmt/binding-win32-arm64-msvc": "0.36.0", "@oxfmt/binding-win32-ia32-msvc": "0.36.0", "@oxfmt/binding-win32-x64-msvc": "0.36.0" }, "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-/ejJ+KoSW6J9bcNT9a9UtJSJNWhJ3yOLSBLbkoFHJs/8CZjmaZVZAJe4YgO1KMJlKpNQasrn/G9JQUEZI3p0EQ=="], "oxlint": ["oxlint@1.51.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.51.0", "@oxlint/binding-android-arm64": "1.51.0", "@oxlint/binding-darwin-arm64": "1.51.0", "@oxlint/binding-darwin-x64": "1.51.0", "@oxlint/binding-freebsd-x64": "1.51.0", "@oxlint/binding-linux-arm-gnueabihf": "1.51.0", "@oxlint/binding-linux-arm-musleabihf": "1.51.0", "@oxlint/binding-linux-arm64-gnu": "1.51.0", "@oxlint/binding-linux-arm64-musl": "1.51.0", "@oxlint/binding-linux-ppc64-gnu": "1.51.0", "@oxlint/binding-linux-riscv64-gnu": "1.51.0", "@oxlint/binding-linux-riscv64-musl": "1.51.0", "@oxlint/binding-linux-s390x-gnu": "1.51.0", "@oxlint/binding-linux-x64-gnu": "1.51.0", "@oxlint/binding-linux-x64-musl": "1.51.0", "@oxlint/binding-openharmony-arm64": "1.51.0", "@oxlint/binding-win32-arm64-msvc": "1.51.0", "@oxlint/binding-win32-ia32-msvc": "1.51.0", "@oxlint/binding-win32-x64-msvc": "1.51.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.15.0" }, "optionalPeers": ["oxlint-tsgolint"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-g6DNPaV9/WI9MoX2XllafxQuxwY1TV++j7hP8fTJByVBuCoVtm3dy9f/2vtH/HU40JztcgWF4G7ua+gkainklQ=="], + "p-filter": ["p-filter@2.1.0", "", { "dependencies": { "p-map": "^2.0.0" } }, "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw=="], + + "p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], + + "p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], + + "p-map": ["p-map@2.1.0", "", {}, "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw=="], + + "p-try": ["p-try@2.2.0", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="], + + "package-manager-detector": ["package-manager-detector@0.2.11", "", { "dependencies": { "quansync": "^0.2.7" } }, "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ=="], + + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "pify": ["pify@4.0.1", "", {}, "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="], + + "prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], + + "quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "read-yaml-file": ["read-yaml-file@1.1.0", "", { "dependencies": { "graceful-fs": "^4.1.5", "js-yaml": "^3.6.1", "pify": "^4.0.1", "strip-bom": "^3.0.0" } }, "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA=="], + + "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + "slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], + + "spawndamnit": ["spawndamnit@3.0.1", "", { "dependencies": { "cross-spawn": "^7.0.5", "signal-exit": "^4.0.1" } }, "sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg=="], + + "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], + "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], - "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], + + "term-size": ["term-size@2.2.1", "", {}, "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg=="], "tinypool": ["tinypool@2.1.0", "", {}, "sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw=="], + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], + + "@inquirer/editor/@inquirer/external-editor": ["@inquirer/external-editor@2.0.3", "", { "dependencies": { "chardet": "^2.1.1", "iconv-lite": "^0.7.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-LgyI7Agbda74/cL5MvA88iDpvdXI2KuMBCGRkbCl2Dg1vzHeOgs+s0SDcXV7b+WZJrv2+ERpWSM65Fpi9VfY3w=="], + + "@manypkg/find-root/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "@manypkg/find-root/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], + + "@manypkg/get-packages/@changesets/types": ["@changesets/types@4.1.0", "", {}, "sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw=="], + + "@manypkg/get-packages/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], + + "read-yaml-file/js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], + + "string-width/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + + "wrap-ansi/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + + "read-yaml-file/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + + "string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + + "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], } } diff --git a/package.json b/package.json index a7ef7a66b..c8c974d03 100644 --- a/package.json +++ b/package.json @@ -12,11 +12,18 @@ "format": "bun run --filter @clerk/cli-core format && oxfmt --write scripts/", "format:check": "bun run --filter @clerk/cli-core format:check && oxfmt --check scripts/", "build:compile": "bun run --filter @clerk/cli-core build:compile", + "version-packages": "bun changeset version", + "version-packages:canary": "bun run scripts/snapshot.ts -- canary", + "version-packages:snapshot": "bun run scripts/snapshot.ts", + "release": "bun run scripts/releaser/index.ts", + "release:canary": "bun run scripts/releaser/index.ts --tag canary", + "release:snapshot": "bun run scripts/releaser/index.ts --tag snapshot", "build:compile:all": "bun run scripts/build.ts", "start": "bun run build:compile && ./packages/cli-core/dist/clerk", "prepare": "git config core.hooksPath .hooks" }, "devDependencies": { + "@changesets/cli": "^2.29.4", "@types/bun": "^1.3.9", "nano-staged": "^0.9.0", "oxfmt": "^0.36.0", diff --git a/scripts/check-release.ts b/scripts/check-release.ts new file mode 100644 index 000000000..aeb3597d9 --- /dev/null +++ b/scripts/check-release.ts @@ -0,0 +1,22 @@ +import { join } from "node:path"; +import { appendFile } from "node:fs/promises"; +import { isPublished } from "./lib/npm.ts"; + +const WRAPPER_PKG_PATH = join(import.meta.dir, "../packages/cli/package.json"); +const GITHUB_OUTPUT = process.env.GITHUB_OUTPUT; + +const pkg = await Bun.file(WRAPPER_PKG_PATH).json(); +const version: string = pkg.version; +const published = isPublished("clerk", version); + +if (published) { + console.log(`Version ${version} is already published — skipping stable release.`); +} else { + console.log(`Version ${version} is not published — triggering stable release.`); +} + +if (GITHUB_OUTPUT) { + const lines = [`release_created=${!published}`]; + if (!published) lines.push(`version=${version}`); + await appendFile(GITHUB_OUTPUT, lines.join("\n") + "\n"); +} diff --git a/scripts/lib/npm.ts b/scripts/lib/npm.ts new file mode 100644 index 000000000..5363266e6 --- /dev/null +++ b/scripts/lib/npm.ts @@ -0,0 +1,18 @@ +/** + * Check if a package version is published on npm. + * Distinguishes "not found" (E404) from real errors (network, auth). + */ +export function isPublished(name: string, version: string): boolean { + const result = Bun.spawnSync(["npm", "view", `${name}@${version}`, "version"], { + stdio: ["ignore", "pipe", "pipe"], + }); + + if (result.exitCode === 0) return true; + + const stderr = result.stderr.toString(); + if (stderr.includes("E404") || stderr.includes("is not in this registry")) { + return false; + } + + throw new Error(`npm view ${name}@${version} failed (exit ${result.exitCode}): ${stderr.trim()}`); +} diff --git a/scripts/releaser/index.ts b/scripts/releaser/index.ts new file mode 100644 index 000000000..71b0f648f --- /dev/null +++ b/scripts/releaser/index.ts @@ -0,0 +1,148 @@ +import { mkdir, cp, rm, chmod, copyFile } from "node:fs/promises"; +import { join } from "node:path"; +import { parseArgs } from "node:util"; +import { type Target, targets, SCOPE, PKG_PREFIX } from "./targets.ts"; +import { isPublished } from "../lib/npm.ts"; + +const DIST_DIR = join(import.meta.dir, "../../dist/platform-packages"); +const ARTIFACTS_DIR = process.env.ARTIFACTS_DIR ?? join(import.meta.dir, "../../dist/artifacts"); +const WRAPPER_PKG_PATH = join(import.meta.dir, "../../packages/cli/package.json"); + +function run(cmd: string[], opts?: { cwd?: string }): void { + const result = Bun.spawnSync(cmd, { + cwd: opts?.cwd, + stdio: ["ignore", "pipe", "pipe"], + }); + if (result.exitCode !== 0) { + const stderr = result.stderr.toString().trim(); + throw new Error( + `${cmd.join(" ")} failed (exit ${result.exitCode})${stderr ? `: ${stderr}` : ""}`, + ); + } +} + +function parseCliArgs(): { dryRun: boolean; tag?: string; versionOverride?: string } { + const { values } = parseArgs({ + args: Bun.argv.slice(2), + options: { + "dry-run": { type: "boolean", default: false }, + tag: { type: "string" }, + version: { type: "string" }, + }, + }); + return { dryRun: values["dry-run"]!, tag: values.tag, versionOverride: values.version }; +} + +function packageName(targetName: string): string { + return `${SCOPE}/${PKG_PREFIX}-${targetName}`; +} + +async function generatePlatformPackage(target: Target, version: string): Promise { + const dir = join(DIST_DIR, target.name); + const binDir = join(dir, "bin"); + + await mkdir(binDir, { recursive: true }); + + const binaryName = `clerk${target.ext}`; + const artifactPath = join(ARTIFACTS_DIR, `clerk-${target.name}`, binaryName); + const destPath = join(binDir, binaryName); + await cp(artifactPath, destPath); + await chmod(destPath, 0o755); + + const pkg: Record = { + name: packageName(target.name), + version, + description: `Clerk CLI binary for ${target.name}`, + license: "MIT", + repository: { type: "git", url: "https://github.com/clerk/cli.git" }, + homepage: "https://clerk.com/docs", + os: [target.os], + cpu: [target.cpu], + preferUnplugged: true, + files: ["bin"], + }; + if (target.libc) { + pkg.libc = [target.libc]; + } + await Bun.write(join(dir, "package.json"), JSON.stringify(pkg, null, 2) + "\n"); + + // Include the LICENSE file in each platform package. + const licensePath = join(import.meta.dir, "../../LICENSE"); + await copyFile(licensePath, join(dir, "LICENSE")); + + return dir; +} + +function publish(dir: string, dryRun: boolean, tag?: string): void { + const flags = ["npm", "publish", "--access", "public", "--provenance", "--ignore-scripts"]; + if (tag) flags.push("--tag", tag); + if (dryRun) flags.push("--dry-run"); + run(flags, { cwd: dir }); +} + +const { dryRun, tag, versionOverride } = parseCliArgs(); +const version = versionOverride ?? (await Bun.file(WRAPPER_PKG_PATH).json()).version; +console.log( + `Publishing version ${version}${tag ? ` (tag: ${tag})` : ""}${dryRun ? " (dry run)" : ""}`, +); + +await rm(DIST_DIR, { recursive: true, force: true }); + +for (const target of targets) { + const name = packageName(target.name); + if (isPublished(name, version)) { + console.log(`Skipping ${name}@${version} (already published)`); + continue; + } + console.log(`Publishing ${name}@${version}...`); + const dir = await generatePlatformPackage(target, version); + publish(dir, dryRun, tag); +} + +// Build wrapper package.json for publishing: add optionalDependencies from targets and remove private flag. +// This mutation is intentional — the repo omits optionalDependencies while the published package includes them. +// We restore the original file after publishing (or on failure) so the working tree stays clean. +const wrapperRaw = await Bun.file(WRAPPER_PKG_PATH).text(); +try { + const wrapperPkg = JSON.parse(wrapperRaw); + wrapperPkg.version = version; + wrapperPkg.optionalDependencies = Object.fromEntries( + targets.map((t) => [packageName(t.name), version]), + ); + delete wrapperPkg.private; + await Bun.write(WRAPPER_PKG_PATH, JSON.stringify(wrapperPkg, null, 2) + "\n"); + + const wrapperName = "clerk"; + if (isPublished(wrapperName, version)) { + console.log(`Skipping ${wrapperName}@${version} (already published)`); + } else { + console.log(`Publishing ${wrapperName}@${version}...`); + publish(join(import.meta.dir, "../../packages/cli"), dryRun, tag); + } +} finally { + await Bun.write(WRAPPER_PKG_PATH, wrapperRaw); +} + +// Create git tag and GitHub Release for stable releases (no --tag flag). +// Canary (--tag canary) and snapshot (--tag snapshot) skip this. +if (!tag && !dryRun) { + const tagName = `v${version}`; + + // Check if tag already exists (idempotency) + const tagCheck = Bun.spawnSync(["git", "rev-parse", tagName], { + stdio: ["ignore", "pipe", "pipe"], + }); + + if (tagCheck.exitCode === 0) { + console.log(`Tag ${tagName} already exists, skipping.`); + } else { + console.log(`Creating tag ${tagName}...`); + run(["git", "tag", tagName]); + run(["git", "push", "origin", tagName]); + + console.log(`Creating GitHub Release for ${tagName}...`); + run(["gh", "release", "create", tagName, "--generate-notes"]); + } +} + +console.log("Done!"); diff --git a/scripts/snapshot.ts b/scripts/snapshot.ts new file mode 100644 index 000000000..11832cad9 --- /dev/null +++ b/scripts/snapshot.ts @@ -0,0 +1,59 @@ +import { join } from "node:path"; +import { parseArgs } from "node:util"; + +const CHANGESET_CONFIG = join(import.meta.dir, "../.changeset/config.json"); +const WRAPPER_PKG = join(import.meta.dir, "../packages/cli/package.json"); + +const { values, positionals } = parseArgs({ + args: Bun.argv.slice(2), + options: { + name: { type: "string" }, + }, + allowPositionals: true, +}); + +const name = values.name || positionals[0] || "snapshot"; + +// Validate kebab-case +if (!/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(name)) { + throw new Error(`Invalid snapshot name: ${name} (must be kebab-case)`); +} + +// Temporarily disable changelog generation +const configRaw = await Bun.file(CHANGESET_CONFIG).text(); +const config = JSON.parse(configRaw); +config.changelog = false; +await Bun.write(CHANGESET_CONFIG, JSON.stringify(config, null, 2) + "\n"); + +try { + // Exit prerelease mode if active + Bun.spawnSync(["bunx", "changeset", "pre", "exit"], { + stdio: ["ignore", "pipe", "pipe"], + }); + + // Bump versions to clear pre state + Bun.spawnSync(["bunx", "changeset", "version"], { + stdio: ["ignore", "pipe", "pipe"], + }); + + // Create temp changeset + const snapshot = `---\n"clerk": patch\n---\n\nSnapshot release\n`; + await Bun.write(join(import.meta.dir, "../.changeset/snapshot-temp.md"), snapshot); + + // Run changeset version --snapshot + const result = Bun.spawnSync(["bunx", "changeset", "version", "--snapshot", name], { + stdio: ["ignore", "pipe", "pipe"], + }); + + if (result.exitCode !== 0) { + throw new Error(`changeset version failed: ${result.stderr.toString().trim()}`); + } + + const pkg = await Bun.file(WRAPPER_PKG).json(); + console.log(`Snapshot version: ${pkg.version}`); +} finally { + // Restore config + Bun.spawnSync(["git", "checkout", "HEAD", "--", CHANGESET_CONFIG], { + stdio: ["ignore", "pipe", "pipe"], + }); +} From 3d5e9abfc2925e9803570af05899bcb2eadfa145 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Fri, 20 Mar 2026 15:08:04 -0600 Subject: [PATCH 2/5] fix: harden release infrastructure - Remove LICENSE copy from platform packages (added in PR #46) - Make stable release tag/release creation idempotent (remote-aware tag check, independent release existence check) - Add --clobber to GitHub Release asset uploads for safe reruns - Block snapshot publish on fork PRs to prevent OIDC token exposure - Fix notify-failure to fire even when publish job is skipped --- .github/workflows/release.yml | 2 +- .github/workflows/snapshot.yml | 17 +++++++++++++-- scripts/releaser/index.ts | 39 ++++++++++++++++++++++------------ 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c9a2eb3fc..1aeb118e5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -109,7 +109,7 @@ jobs: for dir in dist/artifacts/clerk-*/; do target=${dir#dist/artifacts/clerk-} && target=${target%/} ext=""; [[ "$target" == win32-* ]] && ext=".exe" - gh release upload "$tag" "${dir}clerk${ext}#clerk-${target}${ext}" + gh release upload "$tag" "${dir}clerk${ext}#clerk-${target}${ext}" --clobber done # ─── Canary release ──────────────────────────────────────────────── diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 076864c1f..6ec8785bb 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -22,6 +22,7 @@ jobs: outputs: version: ${{ steps.version.outputs.version }} sha: ${{ steps.pr.outputs.sha }} + is_fork: ${{ steps.pr.outputs.is_fork }} steps: - name: React to comment env: @@ -37,8 +38,17 @@ jobs: GH_TOKEN: ${{ github.token }} GH_REPO: ${{ github.repository }} run: | - sha=$(gh api "repos/${GH_REPO}/pulls/${{ github.event.issue.number }}" --jq '.head.sha') + pr_json=$(gh api "repos/${GH_REPO}/pulls/${{ github.event.issue.number }}") + sha=$(echo "$pr_json" | jq -r '.head.sha') + head_repo=$(echo "$pr_json" | jq -r '.head.repo.full_name') + base_repo=$(echo "$pr_json" | jq -r '.base.repo.full_name') + is_fork="false" + if [ "$head_repo" != "$base_repo" ]; then + is_fork="true" + echo "::warning::Snapshot requested on fork PR (head: ${head_repo}) — publish will be skipped." + fi echo "sha=${sha}" >> "$GITHUB_OUTPUT" + echo "is_fork=${is_fork}" >> "$GITHUB_OUTPUT" - uses: actions/checkout@v4 with: @@ -76,6 +86,7 @@ jobs: publish: needs: [snapshot, build, smoke-test] + if: needs.snapshot.outputs.is_fork != 'true' # Must run on GitHub-hosted runner for npm OIDC trusted publishing runs-on: ubuntu-latest timeout-minutes: 15 @@ -149,7 +160,9 @@ jobs: notify-failure: needs: [snapshot, build, smoke-test, publish] - if: failure() + if: >- + always() && + contains(needs.*.result, 'failure') runs-on: ubuntu-latest timeout-minutes: 5 permissions: diff --git a/scripts/releaser/index.ts b/scripts/releaser/index.ts index 71b0f648f..45210cda1 100644 --- a/scripts/releaser/index.ts +++ b/scripts/releaser/index.ts @@ -1,4 +1,4 @@ -import { mkdir, cp, rm, chmod, copyFile } from "node:fs/promises"; +import { mkdir, cp, rm, chmod } from "node:fs/promises"; import { join } from "node:path"; import { parseArgs } from "node:util"; import { type Target, targets, SCOPE, PKG_PREFIX } from "./targets.ts"; @@ -66,10 +66,6 @@ async function generatePlatformPackage(target: Target, version: string): Promise } await Bun.write(join(dir, "package.json"), JSON.stringify(pkg, null, 2) + "\n"); - // Include the LICENSE file in each platform package. - const licensePath = join(import.meta.dir, "../../LICENSE"); - await copyFile(licensePath, join(dir, "LICENSE")); - return dir; } @@ -128,18 +124,35 @@ try { if (!tag && !dryRun) { const tagName = `v${version}`; - // Check if tag already exists (idempotency) - const tagCheck = Bun.spawnSync(["git", "rev-parse", tagName], { - stdio: ["ignore", "pipe", "pipe"], - }); + // Check if tag exists on remote (not just locally) + const remoteTagCheck = Bun.spawnSync( + ["git", "ls-remote", "--tags", "origin", `refs/tags/${tagName}`], + { stdio: ["ignore", "pipe", "pipe"] }, + ); + const remoteTagExists = + remoteTagCheck.exitCode === 0 && remoteTagCheck.stdout.toString().trim().length > 0; - if (tagCheck.exitCode === 0) { - console.log(`Tag ${tagName} already exists, skipping.`); + if (remoteTagExists) { + console.log(`Tag ${tagName} already exists on remote, skipping tag push.`); } else { - console.log(`Creating tag ${tagName}...`); - run(["git", "tag", tagName]); + console.log(`Creating and pushing tag ${tagName}...`); + // Create locally if it doesn't exist yet + const localTagCheck = Bun.spawnSync(["git", "rev-parse", tagName], { + stdio: ["ignore", "pipe", "pipe"], + }); + if (localTagCheck.exitCode !== 0) { + run(["git", "tag", tagName]); + } run(["git", "push", "origin", tagName]); + } + // Create GitHub Release idempotently — skip if it already exists. + const releaseViewCheck = Bun.spawnSync(["gh", "release", "view", tagName], { + stdio: ["ignore", "pipe", "pipe"], + }); + if (releaseViewCheck.exitCode === 0) { + console.log(`GitHub Release for ${tagName} already exists, skipping.`); + } else { console.log(`Creating GitHub Release for ${tagName}...`); run(["gh", "release", "create", tagName, "--generate-notes"]); } From 2f4032d28973264fa3966c9177b4296dcee086e9 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Fri, 20 Mar 2026 16:40:08 -0600 Subject: [PATCH 3/5] ci: declare Blacksmith runner label for actionlint --- .github/actionlint.yaml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .github/actionlint.yaml diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml new file mode 100644 index 000000000..d953b67bc --- /dev/null +++ b/.github/actionlint.yaml @@ -0,0 +1,3 @@ +self-hosted-runner: + labels: + - blacksmith-2vcpu-ubuntu-2404 From 196876b62f8a13c4672422681a9f03416072bbdf Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Fri, 20 Mar 2026 16:40:31 -0600 Subject: [PATCH 4/5] ci: reorder check-release before changesets to prevent premature release --- .github/workflows/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1aeb118e5..322adc231 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,6 +26,10 @@ jobs: - uses: oven-sh/setup-bun@v2 - run: bun install --frozen-lockfile + - name: Check if release needed + id: check + run: bun run scripts/check-release.ts + - id: changesets uses: changesets/action@v1 with: @@ -35,10 +39,6 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} - - name: Check if release needed - id: check - run: bun run scripts/check-release.ts - # ─── Stable release ──────────────────────────────────────────────── build: From 58f09345137995b092380a21aa1851b229fca00f Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Fri, 20 Mar 2026 16:40:42 -0600 Subject: [PATCH 5/5] ci: add fork guards to snapshot build and smoke-test jobs --- .github/workflows/snapshot.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 6ec8785bb..8b1edfe51 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -70,6 +70,7 @@ jobs: build: needs: snapshot + if: needs.snapshot.outputs.is_fork != 'true' uses: ./.github/workflows/build-binaries.yml with: version: ${{ needs.snapshot.outputs.version }} @@ -78,6 +79,7 @@ jobs: smoke-test: needs: [snapshot, build] + if: needs.snapshot.outputs.is_fork != 'true' uses: ./.github/workflows/smoke-test.yml with: version: ${{ needs.snapshot.outputs.version }}