Releasing
September 19, 2026 ยท View on GitHub
Publishing a GitHub Release for tag vX.Y.Z runs .github/workflows/release.yml,
which publishes stanley-code to npm with trusted publishing (OIDC) and provenance. There is no npm token in
this repository or its secrets, and none should ever be added.
CI (.github/workflows/ci.yml) runs npm run check on Node 22.18.0, 22 and 24
for every pull request and push to main, plus npm run check:package, the same package check the release uses.
One-time setup: npm trusted publisher
You need owner access to stanley-code on npmjs.com, with 2FA on your account. At
https://www.npmjs.com/package/stanley-code/access, under Trusted Publisher, choose GitHub Actions and enter
exactly (case-sensitive):
| Field | Value |
|---|---|
| Organization or user | devagrawal09 |
| Repository | stanley-code |
| Workflow filename | release.yml |
| Environment name | npm |
The workflow filename is the file name only, not the full path. The environment must match environment: npm
on the publish job. Equivalent CLI (npm >= 11.10.0, run by a package owner):
npm trust github stanley-code --repository devagrawal09/stanley-code --file release.yml --environment npm --allow-publish
npm trust list stanley-code
npm allows one trusted publisher per package; run npm trust revoke stanley-code --id <id> before changing it.
After the first successful trusted publish: under Settings โ Publishing access, select Require
two-factor authentication and disallow tokens, and revoke any npm automation or publish tokens used for
0.0.1. Trusted publishing uses no tokens, so it keeps working.
One-time setup: GitHub environment npm
Configure it at https://github.com/devagrawal09/stanley-code/settings/environments before the first release:
- Deployment branches and tags: "Selected branches and tags", with a tag rule
v*. - Required reviewers (optional): add yourself to approve each publish before the job gets its OIDC token.
- No environment secrets. Also no repository secrets,
NPM_TOKENorNODE_AUTH_TOKEN.
Cutting a release
-
Bump the version in a pull request and merge it once CI is green:
npm version 0.1.0 --no-git-tag-version # updates package.json and package-lock.json -
Rehearse locally from the merged
main(optional; publishes nothing):npm ci && npm run check npm run check:package # manifest + tarball file list node scripts/release.ts registry # version is not on npm and is newer than latest -
Tag the merged commit and publish the release. The tag must be exactly
v+ thepackage.jsonversion, on a commit reachable frommain. Do not mark it as a pre-release. A saved draft does nothing; only publishing starts the workflow.git fetch origin git tag -a v0.1.0 origin/main -m "v0.1.0" git push origin v0.1.0 gh release create v0.1.0 --verify-tag --title v0.1.0 --generate-notes -
Watch the Release workflow. Approve the
publishjob if reviewers are required. When it finishes, https://www.npmjs.com/package/stanley-code/v/0.1.0 shows the version with a provenance badge.
What the workflow checks
Job verify (contents: read, no OIDC token) stops the release before anything is published unless:
- The tag is
v+ a stableX.Y.Zthat equalsv+package.jsonversion. package.jsonnameisstanley-codeandrepository.urlishttps://github.com/devagrawal09/stanley-code(npm rejects provenance otherwise).HEADis the tag commit (GITHUB_SHA, refrefs/tags/<tag>), the tree is clean, and the commit is reachable fromorigin/main.- The GitHub Release is not a pre-release.
- npm is >= 11.5.1 and points at
https://registry.npmjs.org/. - The version is not on npm yet and is greater than the current
latest. npm ciandnpm run checkpass, and the tree is still clean afterwards.npm packcontains onlypackage.json,README.md,LICENSE, and adist/**/*.jsanddist/**/*.d.tsfor everysrc/**/*.ts;dist/cli.jsis executable with a node shebang.
Job publish (contents: read, id-token: write, environment npm) installs no dependencies and runs no
package scripts. It re-checks the tag, checkout, npm CLI and registry, confirms the verified tarball's SHA-256
and contents, then runs:
npm publish <verified tarball> --provenance --access public --ignore-scripts
Check logic lives in scripts/release-checks.ts (tested in
test/release.test.ts); I/O is in scripts/release.ts. Runs for the same tag queue
(cancel-in-progress: false) instead of racing; a second run fails the "not on npm" check.
When something fails
npm publish is the last step: a failed run published nothing unless its log shows npm publish succeeded.
| Failure | Published? | Fix |
|---|---|---|
| Tag format, tag/version mismatch, package name or repository | No | Delete the GitHub Release and tag, then tag the right commit/version. |
Tag not on main, or release marked pre-release | No | Delete the release (and the tag if wrong) and recreate. Editing a release does not re-trigger the run. |
Version already on npm, or not newer than latest | No | Bump the version in a PR and release a new tag. npm versions can never be reused. |
npm run check, dirty tree or package contents | No | Fix on main, then delete the release and tag and re-tag the fixed commit, or bump the version. |
| Environment approval rejected | No | Re-run the workflow when ready. |
npm publish E404/E403 (trusted publisher mismatch) or OIDC | No | Fix the trusted publisher fields above, then Re-run failed jobs. The tarball artifact is kept 30 days. |
npm publish E422 provenance/repository mismatch | No | Fix package.json repository in a PR and release a new version. |
Run fails or times out after npm publish reported success | Yes | Do not re-tag. Re-running is safe (it stops at "not on npm"). Confirm with npm view stanley-code@X.Y.Z. |
Re-runs use the workflow and scripts from the tagged commit, so fixes to release.yml or scripts/ apply only
to a new tag. Never move or reuse a tag whose version is on npm. Deleting a GitHub Release does not unpublish
anything from npm.