Skip to content

fix(ci): always use the publish environment for publish-npm - #74

Merged
albedosehen merged 1 commit into
mainfrom
fix/publish-npm-environment
Jul 31, 2026
Merged

albedosehen merged 1 commit into
mainfrom
fix/publish-npm-environment

Conversation

@albedosehen

Copy link
Copy Markdown
Collaborator

The bug

publish-npm selected its environment conditionally:

environment: ${{ github.event_name == 'release' && 'publish' || '' }}

On a workflow_dispatch run that resolves to no environment. NPM_TOKEN is stored only as an environment secret on publish -- verified there is no repo-level or organization-level fallback (Oneiriq org has only ONEIRIQ_READ_PAT). So dispatch runs reached npm publish with an empty NODE_AUTH_TOKEN and failed on auth, even though the job's if: explicitly permits dispatch with target of npm or both.

The nastier consequence is diagnostic: the release path works while the dispatch path fails for an unrelated reason. Retrying a failed release publish via manual dispatch produces an auth error that looks like the token is still wrong.

The fix

Always use publish.

Trade-off, stated explicitly

The publish environment has a required-reviewer protection rule, so dispatch runs will now wait for approval -- exactly as release runs already do today. That is the right trade: an ad-hoc publish to a public registry should be reviewed. The alternative, adding a repo-level NPM_TOKEN as a fallback, would duplicate the secret into a less protected scope and defeat the gate.

This also matches the sibling repos: cosmiq-graphql and stoat-logger both declare environment: publish unconditionally.

Not changed

publish-jsr deliberately has no environment -- it authenticates to JSR via OIDC and consumes no secret.

Context

npm publishing for this package has failed far more often than it has succeeded (10 failed release runs vs 5 successful across the tag history). Combined with GitHub's ~30-day limit on re-running a workflow run, a missed failure becomes permanent: v1.5.0 and v1.6.0 can no longer be published at all, because their runs are past the re-run window and dispatching against those tags would use the buggy workflow file stored at those tags. npm therefore skips from 1.4.0 to 1.7.0. This fix stops that failure mode recurring for future releases.

The job selected its environment conditionally:

    environment: ${{ github.event_name == 'release' && 'publish' || '' }}

so a workflow_dispatch run resolved to no environment. NPM_TOKEN is
stored only as an environment secret on `publish` -- there is no
repo-level or organization-level fallback -- so dispatch runs reached
`npm publish` with an empty NODE_AUTH_TOKEN and failed on auth, even
though the job's `if:` explicitly permits dispatch with target npm/both.

That made manual publishing impossible and, worse, made a retry look
like a token problem: the release path works while the dispatch path
fails with a different error for an unrelated reason.

Always use `publish`. This does mean dispatch runs now wait on the
environment's required-reviewer gate, the same as release runs already
do. That is the correct trade -- an ad-hoc publish to a public registry
should be reviewed, and the alternative (adding a repo-level NPM_TOKEN
as a fallback) would duplicate the secret and weaken that protection.

Matches cosmiq-graphql and stoat-logger, which both declare
`environment: publish` unconditionally.

publish-jsr is deliberately left without an environment: it
authenticates to JSR via OIDC and consumes no secret.
@albedosehen
albedosehen merged commit cd607ee into main Jul 31, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant