Publish to npm #22
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: Publish to npm | |
| # Runs after the full CI workflow succeeds on main (not on the raw push), so a | |
| # red lint/cost-map-sync/python job blocks an npm release too. The old js/** | |
| # path filter is gone; the version gate below already makes no-op runs cheap. | |
| on: | |
| workflow_run: | |
| workflows: [CI] | |
| types: [completed] | |
| branches: [main] | |
| workflow_dispatch: | |
| concurrency: | |
| group: publish-npm-${{ github.ref }} | |
| cancel-in-progress: false | |
| permissions: | |
| contents: write # push the release tag; npm auth stays the token secret | |
| jobs: | |
| publish: | |
| # workflow_run's `branches` filter matches the HEAD branch NAME of the | |
| # triggering run — a fork PR from a branch named "main" would match it. The | |
| # event/head_repository checks restrict publishing to push-triggered CI on | |
| # this repo's own main. | |
| if: >- | |
| (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main') || | |
| (github.event_name == 'workflow_run' && | |
| github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.event == 'push' && | |
| github.event.workflow_run.head_repository.full_name == github.repository) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| defaults: | |
| run: | |
| working-directory: js | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| persist-credentials: false | |
| # workflow_run checks out the default branch by default; pin to the | |
| # exact commit CI validated. | |
| ref: ${{ github.event.workflow_run.head_sha || github.ref }} | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| # Node 22: current LTS, comfortably covers the JS toolchain (Vite/Vitest). | |
| node-version: '22' | |
| registry-url: 'https://registry.npmjs.org' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Build | |
| run: npm run build | |
| - name: Test | |
| run: npm test --if-present | |
| - name: Check if version is already published | |
| id: check | |
| run: | | |
| set -euo pipefail | |
| PKG_NAME=$(node -p "require('./package.json').name") | |
| PKG_VERSION=$(node -p "require('./package.json').version") | |
| echo "name=$PKG_NAME" >> "$GITHUB_OUTPUT" | |
| echo "version=$PKG_VERSION" >> "$GITHUB_OUTPUT" | |
| if npm view "$PKG_NAME@$PKG_VERSION" version > /dev/null 2>npm_view.err; then | |
| echo "$PKG_NAME@$PKG_VERSION already published on npm, skipping." | |
| echo "should_publish=false" >> "$GITHUB_OUTPUT" | |
| elif grep -q 'E404' npm_view.err; then | |
| echo "$PKG_NAME@$PKG_VERSION not found on npm, will publish." | |
| echo "should_publish=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "npm view failed unexpectedly; aborting publish check." >&2 | |
| cat npm_view.err >&2 | |
| exit 1 | |
| fi | |
| - name: Publish | |
| if: steps.check.outputs.should_publish == 'true' | |
| run: npm publish --access public | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| - name: Tag the release | |
| # Maps the published artifact back to its exact commit ("js-" prefix: | |
| # the PyPI package versions independently and tags "py-v<version>"). | |
| if: steps.check.outputs.should_publish == 'true' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| PKG_VERSION: ${{ steps.check.outputs.version }} | |
| run: | | |
| gh api "repos/${{ github.repository }}/git/refs" \ | |
| -f ref="refs/tags/js-v$PKG_VERSION" \ | |
| -f sha="$(git rev-parse HEAD)" \ | |
| || echo "::warning::tag js-v$PKG_VERSION already exists or could not be created" |