ci: gated release pipeline (npm Trusted Publishing + GitHub Release) - #5
Conversation
Tag-triggered release workflow. Pushing a vX.Y.Z tag runs a verify job (tag/version match, refuse-republish guard, build, typecheck, lint, test, pack) then a publish job gated behind a protected `npm-production` Environment. Publish uses npm Trusted Publishing (OIDC, provenance) with no stored token, promotes the tarball built in verify, and creates a matching GitHub Release with notes extracted from CHANGELOG.md. Changelog extraction is literal-substring (regex-free) so it behaves the same across awk flavors and won't pull in the trailing link-reference block. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a tag-triggered GitHub Actions release workflow for this npm package, building and verifying once, then using a protected Environment as a manual approval gate before publishing via npm Trusted Publishing (OIDC + provenance) and creating a matching GitHub Release.
Changes:
- Introduces a
verifyjob that validates tag/version alignment, checks npm for an existing version, runs CI steps, and uploads a single built tarball artifact. - Introduces a gated
publishjob (environment:npm-production) that downloads the promoted tarball, publishes to npm with provenance, and creates a GitHub Release with notes extracted fromCHANGELOG.md(or autogenerated as fallback).
| verify: | ||
| runs-on: ubuntu-latest | ||
| steps: |
| permissions: | ||
| contents: write # create the GitHub Release | ||
| id-token: write # OIDC for npm Trusted Publishing + provenance |
| set -euo pipefail | ||
| tgz=$(ls release-artifact/*.tgz) | ||
| echo "Publishing $tgz" | ||
| npm publish "$tgz" --provenance --access public |
| flag && substr($0, 1, 1) == "[" { exit } | ||
| flag { print } | ||
| ' CHANGELOG.md) | ||
| tgz=$(ls release-artifact/*.tgz) |
| - name: Use an npm new enough for OIDC trusted publishing | ||
| run: npm install -g npm@latest | ||
|
|
Builds on 8596269, which resolved the review feedback. Three follow-ups: - Drop the `actions:` scopes. Within one run, upload/download-artifact authenticate with the Actions runtime token rather than GITHUB_TOKEN; the `actions` permission is only required to reach artifacts across runs or repos. `actions: write` in particular also grants run cancellation and artifact deletion, which the verify job should not hold. - Treat 11.5.1 as an npm floor instead of installing a flat 11.5.2. Node 24 already bundles a newer npm, so the pin was a downgrade on every release; now the install runs only when the bundled version is genuinely older. - Resolve the tarball once into $GITHUB_ENV. The publish step and the release step were each running their own copy of the same guard, which is exactly the kind of pair that drifts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Follow-up on the review feedback. The two The npm pin was a downgrade. The tarball guard was duplicated. Both the publish step and the release step ran their own copy of the same nullglob-and-count check. Resolved once into |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Suppressed comments (2)
.github/workflows/release.yml:149
npm publishwill fail if this job is re-run after a successful publish (e.g., if the GitHub Release step failed and you re-run onlypublish). Adding a quick "already on npm" guard here makes the job idempotent and allows the GitHub Release creation to proceed on reruns.
- name: Publish to npm (Trusted Publishing / provenance)
run: |
set -euo pipefail
echo "Publishing $TGZ"
npm publish "$TGZ" --provenance --access public
.github/workflows/release.yml:73
- The npm package name is hardcoded as
dfhack-remote-nodehere. Ifpackage.json#nameever changes (e.g., scope move), this republish check and its error message will silently drift from whatnpm publishactually targets.
This issue also appears on line 145 of the same file.
pkg=$(node -p "require('./package.json').version")
if npm view "dfhack-remote-node@$pkg" version >/dev/null 2>&1; then
echo "::error::dfhack-remote-node@$pkg is already on npm — nothing to publish."
exit 1
What
Adds
.github/workflows/release.yml— a tag-triggered release pipeline that publishes to npm and cuts a matching GitHub Release, with an Azure-DevOps-style manual approval gate before publish.How you cut a release
The
v*tag push triggers the workflow.Flow
Job 1 —
verify(no special perms):package.jsonversion (fails otherwise).npm ci→ build → typecheck → lint → test.npm pack→ upload the tarball as an artifact (built once, promoted).Job 2 —
publish(needs: verify,environment: npm-production):id-token: write) — no storedNPM_TOKEN— with a provenance attestation.CHANGELOG.mdsection (regex-free extraction; autogenerated notes as fallback).One-time setup (required before the first tag)
npm-production→ enable Required reviewers, add yourself.alexanderolvera/dfhack-remote-node, workflowrelease.yml, environmentnpm-production.Provenance requires the repo to stay public (it is).
GitHub vs Azure DevOps note
GitHub has no separate "Releases pipeline" entity — this is a normal workflow. The ADO release-approval gate maps to a protected Environment with required reviewers; artifact promotion maps to
upload-artifact→download-artifactacross jobs.🤖 Generated with Claude Code