ci(release): inherit secrets so the npm environment token reaches the publisher - #60
Merged
Merged
Conversation
… publisher The publish job names `environment: npm` and reads NPM_BOOTSTRAP_TOKEN from it, but ci.yml calls that workflow without passing secrets. A called workflow does not fail on an environment-scoped secret it was never passed — it resolves the secret to an empty string. So the job authenticated with nothing and stopped at `NPM_BOOTSTRAP_TOKEN is required for the first package publication`. That is what split the first 0.3.1 run: six images promoted to GHCR, nothing on npm, and no tag, because record-release needs both publishers. `inherit` does not widen the blast radius here. The environment still gates the token — it materializes only in the job that names the environment — and the repository and organization have no Actions secrets for `inherit` to forward. The test asserts both halves, since either alone silently reproduces the split. Verified by removing each in turn: both make it fail.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7f6cf4a97b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ase hit The bootstrap publisher refuses to run without NODE_AUTH_TOKEN, and nothing asserted it. That refusal is what the first 0.3.1 attempt actually executed: an environment secret the caller never passed arrives as the empty string, not as an error, so the step reached npm with no credential. Cover both shapes of "no token" — empty and unset — against the existing fake registry, and assert the refusal happens before any write reaches it, so the guard cannot degrade into failing after exposing a request. Verified by deleting the guard in release.mjs: the test fails.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The 0.3.1 run got further than the last one —
package-releasepassed, so #59's fix works — and then split:0.3.1andsha-73f88778a5a3@theagilemonkeys/facilityis absent from the registryrecord-releaseneeds both publisherspublish-npm / publishfailed at Bootstrap the package once with a granular token withNPM_BOOTSTRAP_TOKEN is required for the first package publication, andNODE_AUTH_TOKENprinted blank rather than***— an empty value, not a masked one.Why
The token is fine.
NPM_BOOTSTRAP_TOKENexists in thenpmenvironment (created 2026-07-31), the environment allows onlymain, and the publish job correctly declaresenvironment: npm.The gap is the reusable-workflow boundary:
ci.ymlcallsrelease.ymlwith nosecrets:key. A called workflow does not receive environment-scoped secrets by declaring the environment alone — withoutsecrets: inheriton the caller, every environment-scoped secret resolves to an empty string rather than erroring. GitHub's docs omit this; it is tracked in actions/runner#1490 and github/docs#44458.So the job authenticated with nothing and failed the way it would if the secret had never been created.
Why
inheritis not a widening hereenvironment: npm, which keeps the branch policy and approval as the real control.gh api repos/theam/facility/actions/secretsand the org-secrets endpoint both return empty — there is nothing else forinheritto forward.NPM_BOOTSTRAP_TOKEN:from the caller cannot work: the calling job does not declare the environment, so it would forward an empty string too.publish-imagesneeds no change — it usesGITHUB_TOKEN, which called workflows always receive.Verification
node --test scripts/*.test.mjs→ 88/88. The new test assertssecrets: inheriton the caller andenvironment: npmon the callee; removing either makes it fail, checked both ways. Both workflows parse, andsecretsis accepted on thepublish-npmjob.I cannot prove this end to end from a PR —
publish-npmonly runs on a push tomain, so the release run is again the first real exercise.Before merging
The six GHCR images already carry
0.3.1from73f8877.scripts/images.mjs:160refuses to move an existing tag to a different digest, and.dockerignoredoes not exclude.github, so rebuilding after this commit may produce different digests and wedgepromote. Delete the six0.3.1package versions first, so this merge can publish a coherent 0.3.1 to both registries.Follow-up worth considering, not included here
docs/releasing.mdtells maintainers to retry the failed jobs in the same run. That cannot work when the failure is in the workflow itself, since a re-run uses the workflow files from the run's own commit — true for both 0.3.1 attempts. The runbook deserves that caveat.