Publish to npm #4
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 | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Version to publish (e.g., v0.1.1). Must already exist as a GitHub Release.' | |
| required: true | |
| type: string | |
| workflow_run: | |
| workflows: ['Release'] | |
| types: [completed] | |
| permissions: | |
| contents: read | |
| id-token: write | |
| # Belt-and-suspenders: release.yml dispatches us explicitly because | |
| # `workflow_run` is unreliable when the upstream `Release` run was itself | |
| # triggered by a GITHUB_TOKEN workflow_dispatch. If both paths happen to | |
| # fire for the same release, this static concurrency group serializes them | |
| # — the first one publishes, the second waits and then no-ops (or errors | |
| # benignly at `npm publish` because the version is already on the registry). | |
| # Releases are version-monotonic so blanket serialisation is safe; there | |
| # isn't a scenario where two concurrent publishes should both succeed. | |
| concurrency: | |
| group: npm-publish | |
| cancel-in-progress: false | |
| jobs: | |
| npm-publish: | |
| if: >- | |
| github.event_name == 'workflow_dispatch' || | |
| (github.event_name == 'workflow_run' && | |
| github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.head_branch == 'main') | |
| runs-on: ubuntu-latest | |
| name: Publish coven-code to npm | |
| steps: | |
| - uses: actions/checkout@v4 | |
| if: github.event_name == 'workflow_dispatch' | |
| - uses: actions/checkout@v4 | |
| if: github.event_name == 'workflow_run' | |
| with: | |
| ref: ${{ github.event.workflow_run.head_sha }} | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '24' | |
| package-manager-cache: false | |
| - name: Set up npm | |
| run: | | |
| npm install -g npm@latest | |
| npm --version | |
| - name: Resolve version | |
| id: version | |
| run: | | |
| set -euo pipefail | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| VERSION="${{ inputs.version }}" | |
| VERSION="${VERSION#v}" | |
| else | |
| VERSION="$(grep '^version' src-rust/Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')" | |
| fi | |
| if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "::error::Invalid npm publish version: $VERSION" | |
| exit 1 | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "tag=v$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "Publishing coven-code@$VERSION" | |
| - name: Verify GitHub Release exists | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: gh release view "${{ steps.version.outputs.tag }}" --repo "${{ github.repository }}" >/dev/null | |
| - name: Verify trusted publisher identity | |
| working-directory: npm | |
| run: | | |
| set -euo pipefail | |
| node <<'NODE' | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8')); | |
| const repo = process.env.GITHUB_REPOSITORY; | |
| const [owner, repoName] = repo.split('/'); | |
| const workflowRef = process.env.GITHUB_WORKFLOW_REF || ''; | |
| const workflowFile = path.basename(workflowRef.split('@')[0]); | |
| const repositoryUrl = typeof pkg.repository === 'string' | |
| ? pkg.repository | |
| : pkg.repository && pkg.repository.url; | |
| const expectedRepositoryUrl = `git+https://github.com/${repo}.git`; | |
| console.log('Expected npm Trusted Publisher configuration:'); | |
| console.log(` Organization or user: ${owner}`); | |
| console.log(` Repository: ${repoName}`); | |
| console.log(` Workflow filename: ${workflowFile}`); | |
| console.log(' Environment name: (empty)'); | |
| if (repositoryUrl !== expectedRepositoryUrl) { | |
| console.error(`::error::npm/package.json repository.url must be ${expectedRepositoryUrl} for trusted publishing; found ${repositoryUrl || '(missing)'}`); | |
| process.exit(1); | |
| } | |
| NODE | |
| - name: Set npm package version | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| node -e " | |
| const fs = require('fs'); | |
| const pkg = JSON.parse(fs.readFileSync('npm/package.json', 'utf8')); | |
| pkg.version = '${VERSION}'; | |
| fs.writeFileSync('npm/package.json', JSON.stringify(pkg, null, 2) + '\n'); | |
| " | |
| echo "Publishing coven-code@${VERSION}" | |
| - name: Publish to npm | |
| working-directory: npm | |
| env: | |
| NODE_AUTH_TOKEN: '' | |
| NPM_TOKEN: '' | |
| run: npm publish --access public --provenance |