Cut a glab-axi release end to end. Publishing to npm is automated: creating a
GitHub release fires .github/workflows/release.yml, which builds, runs the
test suite, and then runs npm publish. The release notes come from the
CHANGELOG section for the version - that section is the single source of truth
for the notes.
One-time setup (maintainer)
The publish job authenticates with npm Trusted Publishing (OIDC). There is
no npm token: the registry mints a short-lived credential from the workflow's
GitHub Actions identity. Do not create an NPM_TOKEN secret - it is not read,
and a long-lived token is the thing this setup exists to avoid.
Register the trusted publisher once, on npmjs.com:
- Sign in to npmjs.com as a user with publish rights on
glab-axi. - Go to Packages ->
glab-axi-> Settings -> Trusted publishing, and pick GitHub Actions under "Select your publisher". - Fill in the fields to match this repo exactly:
- Organization or user:
karotkriss(no leading@) - Repository:
glab-axi - Workflow filename:
release.yml(filename only, not a path, and it must keep the.ymlextension) - Allowed actions: at least one must be selected;
npm publishis what this workflow runs - Environment name:
npm-publish(npm treats this field as optional, but the publish job declaresenvironment: npm-publish, so filling it in keeps the registration as narrow as the workflow actually is)
- Organization or user:
- Save. Publishing works from that point on; nothing needs to change in the repo.
The match is exact and unforgiving. If the workflow file is ever renamed, or
the job's environment: changes, update this registration or every publish
fails. A leading @ on the org name is a common typo that silently breaks the
match.
Test the pipeline without publishing (dry run)
The workflow's workflow_dispatch trigger has a dry_run input that defaults
to true:
gh workflow run release.yml -f dry_run=true
gh run watch "$(gh run list --workflow=release.yml --limit 1 --json databaseId --jq '.[0].databaseId')"
A dry run does the full checkout, install, build, and test, verifies the
resolved Node and npm satisfy the trusted-publishing floors, then runs
npm publish --dry-run to pack and validate the tarball. Nothing is
uploaded.
A dry run does not verify auth. npm publish --dry-run never contacts the
registry for credentials, so it passes identically whether the trusted
publisher is registered correctly, misconfigured, or absent. Nothing short of a
real publish exercises the OIDC handshake. Read a green dry run as "the code
builds, tests pass, and the tarball is well-formed" and nothing more.
(This is a deliberate correction. The pre-OIDC dry run claimed npm whoami
proved "the token and the publish pipeline are wired correctly". It did not:
whoami needs no OTP, so it went green while the real v0.2.0 publish failed
with EOTP - false confidence on the exact failure the check existed to catch.)
Cut a release
Pick the new version X.Y.Z by semver against what is under ## [Unreleased]
in CHANGELOG.md. Run from a clean checkout of the default branch.
-
Update the CHANGELOG. Rename
## [Unreleased]to## [X.Y.Z] - YYYY-MM-DD(today's date), add a fresh empty## [Unreleased]above it, and add the reference links at the bottom of the file (create the block if the file has none yet):[Unreleased]: https://github.com/karotkriss/glab-axi/compare/vX.Y.Z...HEAD [X.Y.Z]: https://github.com/karotkriss/glab-axi/releases/tag/vX.Y.Z(If a reference-link block already exists from an earlier release, keep its older
[...]link lines.) Drop any empty Added/Changed/Fixed subheadings from the new section. -
Bump the version in
package.json(this also updatespackage-lock.json) and regenerate the skill so its pinnednpx -y glab-axi@X.Y.Zinvocations track the new version (skill:checkfails CI if this is skipped):npm version X.Y.Z --no-git-tag-version npm run skill:build -
Commit the version files, the regenerated skill, and the changelog:
git add CHANGELOG.md package.json package-lock.json skills/glab-axi/SKILL.md git commit -m "release: vX.Y.Z"This commit lands on the default branch through the repo's normal review flow. Tag only after it is on the branch you release from.
-
Tag and push:
git tag vX.Y.Z git push origin HEAD git push origin vX.Y.Z -
Create the GitHub release with notes extracted from the CHANGELOG section for this version. This publishes the release, which triggers the npm publish workflow:
VERSION=X.Y.Z awk -v v="$VERSION" ' $0 ~ "^## \\[" v "\\]" { grab = 1; next } grab && /^## \[/ { exit } grab && /^\[[^]]+\]:/ { exit } grab { print } ' CHANGELOG.md | gh release create "v$VERSION" --title "glab-axi: v$VERSION" --notes-file -The
awkprints exactly the lines between## [X.Y.Z]and the next version heading (or the reference-link block), so the release body mirrors the CHANGELOG section verbatim. -
Verify the publish landed. The workflow run should be green:
gh run list --workflow=release.yml --limit 1And npm must show the new version (may take a minute to propagate):
npm view glab-axi version # -> X.Y.ZIf the run failed at the build or test step, that's an unrelated code issue to fix and re-release. If it failed at the "Verify toolchain" step, the runner resolved a Node or npm below the trusted-publishing floor - fix the pin in the workflow. If it got past those and failed at the publish step, the cause is almost always the trusted-publisher registration not matching this repo, workflow filename, or environment (see one-time setup above). A dry run cannot diagnose that; publishing is the only thing that exercises it.
A failed publish does not consume the version - npm only reserves
X.Y.Zonce a publish actually lands, so a re-run after fixing the registration can reuse the same tag and release.
Notes
- A real
npm publishhappens only onrelease: published, never on push or tag alone - pushing the tag in step 4 does not publish; creating the release does. The manualworkflow_dispatchtrigger cannot publish for real: its dry-run path only packs the tarball, and withdry_rundisabled it is just a build/test smoke run. - Provenance is signed automatically - trusted publishing generates it by
default from GitHub Actions, so the workflow passes no
--provenanceflag. It still relies onid-token: write(already set) and on therepositoryfield inpackage.jsonmatching the GitHub repo. - The workflow pins Node 24 because trusted publishing needs npm >= 11.5.1 and
Node >= 22.14.0, and no Node 22 release bundles an npm that new. This is the
CI publisher's floor only; it is unrelated to the
enginesrange the package supports for its own users. - npm rejects republishing an already-published version, so never reuse
X.Y.Z.