npm publish #712
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: npm publish | |
| on: | |
| # Publish only after the canonical CI workflow completes for the exact main | |
| # commit. The former duplicate gate omitted validators and native checks. | |
| workflow_run: | |
| workflows: [CI] | |
| types: [completed] | |
| branches: [main] | |
| concurrency: | |
| group: npm-publish | |
| cancel-in-progress: false | |
| jobs: | |
| publish: | |
| # workflow_run can represent PR CI as well. Expose npm credentials only | |
| # after a successful push CI run on main. | |
| if: >- | |
| ${{ github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.event == 'push' && | |
| github.event.workflow_run.head_branch == 'main' }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ github.event.workflow_run.head_sha }} | |
| fetch-depth: 2 | |
| persist-credentials: false | |
| - uses: actions/setup-node@v7 | |
| with: | |
| # Node 20 matches the engines field: build + publish on the minimum | |
| # supported runtime so the published artifact is verified against it. | |
| node-version: 20 | |
| registry-url: https://registry.npmjs.org | |
| # Only the commit that actually changed package.json may publish. This | |
| # prevents a later same-version main commit from becoming the package | |
| # provenance when the version commit's CI finishes earlier or later. | |
| - name: Verify version commit and check npm | |
| id: version-check | |
| run: | | |
| set -euo pipefail | |
| PACKAGE_VERSION=$(node -p "require('./package.json').version") | |
| echo "version=$PACKAGE_VERSION" >> "$GITHUB_OUTPUT" | |
| PARENT_VERSION=$(git show HEAD^:package.json 2>/dev/null | node -e \ | |
| "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>{const v=JSON.parse(s).version;if(typeof v!=='string'||!v)throw new Error('parent package version is missing');process.stdout.write(v)})") | |
| node -e "const {assertReleaseVersion}=require('./scripts/version-files.cjs');assertReleaseVersion(process.argv[1],'package.json version');assertReleaseVersion(process.argv[2],'parent package.json version')" \ | |
| "$PACKAGE_VERSION" "$PARENT_VERSION" | |
| if [ "$PARENT_VERSION" = "$PACKAGE_VERSION" ]; then | |
| echo "eligible=false" >> "$GITHUB_OUTPUT" | |
| echo "package.json version did not change in this tested commit; skipping publish." | |
| exit 0 | |
| fi | |
| echo "eligible=true" >> "$GITHUB_OUTPUT" | |
| npm_result=$(mktemp) | |
| npm_error=$(mktemp) | |
| trap 'rm -f "$npm_result" "$npm_error"' EXIT | |
| if npm view tokentracker-cli@"$PACKAGE_VERSION" version --json >"$npm_result" 2>"$npm_error"; then | |
| echo "exists=true" >> "$GITHUB_OUTPUT" | |
| else | |
| lookup_code=$(node -e "const fs=require('fs');try{const r=JSON.parse(fs.readFileSync(process.argv[1],'utf8'));process.stdout.write(r?.error?.code||'')}catch{}" "$npm_result") | |
| if [ "$lookup_code" = "E404" ]; then | |
| echo "exists=false" >> "$GITHUB_OUTPUT" | |
| else | |
| cat "$npm_error" >&2 | |
| echo "npm registry lookup failed without a confirmed E404" >&2 | |
| exit 1 | |
| fi | |
| fi | |
| - name: Install root dependencies | |
| if: steps.version-check.outputs.eligible == 'true' && steps.version-check.outputs.exists == 'false' | |
| run: npm ci | |
| - name: Install dashboard dependencies | |
| if: steps.version-check.outputs.eligible == 'true' && steps.version-check.outputs.exists == 'false' | |
| run: npm ci --prefix dashboard | |
| # InsForge URL + anon key are public client-side values shipped in every | |
| # dashboard bundle. They MUST be present at build time or Vite inlines | |
| # empty strings — breaks leaderboard fetch and disables cloud OAuth. | |
| # `dashboard/.env.local` is gitignored so CI cannot read it. | |
| - name: Build dashboard | |
| if: steps.version-check.outputs.eligible == 'true' && steps.version-check.outputs.exists == 'false' | |
| env: | |
| VITE_INSFORGE_BASE_URL: https://srctyff5.us-east.insforge.app | |
| VITE_INSFORGE_ANON_KEY: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3OC0xMjM0LTU2NzgtOTBhYi1jZGVmMTIzNDU2NzgiLCJlbWFpbCI6ImFub25AaW5zZm9yZ2UuY29tIiwicm9sZSI6ImFub24iLCJpYXQiOjE3ODExNDU5NDd9.T0auta_IrVIh0uXW1bob5QSnzvsnJmN28r5XkSGEuQY | |
| run: npm run dashboard:build | |
| - name: Publish to npm | |
| if: steps.version-check.outputs.eligible == 'true' && steps.version-check.outputs.exists == 'false' | |
| run: npm publish | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| - name: Skip unchanged-version commit | |
| if: steps.version-check.outputs.eligible != 'true' | |
| env: | |
| PACKAGE_VERSION: ${{ steps.version-check.outputs.version }} | |
| run: printf 'v%s was not changed by this commit; skipping publish.\n' "$PACKAGE_VERSION" | |
| - name: Skip already-published version | |
| if: steps.version-check.outputs.eligible == 'true' && steps.version-check.outputs.exists == 'true' | |
| env: | |
| PACKAGE_VERSION: ${{ steps.version-check.outputs.version }} | |
| run: printf 'v%s is already published; skipping.\n' "$PACKAGE_VERSION" | |
| # Notify homebrew-tokentracker tap to bump the Formula immediately. | |
| # If HOMEBREW_DISPATCH_TOKEN is not set, this step silently no-ops and | |
| # the tap's own hourly cron picks up the new version within ~1 hour. | |
| - name: Dispatch homebrew tap update | |
| if: steps.version-check.outputs.eligible == 'true' && steps.version-check.outputs.exists == 'false' | |
| env: | |
| HOMEBREW_DISPATCH_TOKEN: ${{ secrets.HOMEBREW_DISPATCH_TOKEN }} | |
| VERSION: ${{ steps.version-check.outputs.version }} | |
| run: | | |
| if [ -z "${HOMEBREW_DISPATCH_TOKEN:-}" ]; then | |
| echo "HOMEBREW_DISPATCH_TOKEN not set — tap will update via hourly cron." | |
| exit 0 | |
| fi | |
| curl -sSL -X POST \ | |
| -H "Accept: application/vnd.github+json" \ | |
| -H "Authorization: Bearer $HOMEBREW_DISPATCH_TOKEN" \ | |
| -H "X-GitHub-Api-Version: 2022-11-28" \ | |
| https://api.github.com/repos/xiufengsun/homebrew-tokentracker/dispatches \ | |
| -d "{\"event_type\":\"tokentracker-release\",\"client_payload\":{\"source\":\"npm\",\"version\":\"$VERSION\"}}" |