Skip to content

ci(release): publish tokenless on a published release, matching the siblings - #9

Merged
kboshold merged 2 commits into
mainfrom
ci/tokenless-release-on-published
Jul 31, 2026
Merged

ci(release): publish tokenless on a published release, matching the siblings#9
kboshold merged 2 commits into
mainfrom
ci/tokenless-release-on-published

Conversation

@kboshold

Copy link
Copy Markdown
Collaborator

What

Rewrites the release workflow to match sidebase/ssm-secrets and sidebase/nuxt-auth.

  • Trigger is now a published GitHub Release, not a manual dispatch. Publishing the release is the human decision point.
  • No publish secret. Authenticates to npm with the OIDC token from id-token: write, via npm trusted publishing.
  • Drops environment: release, which declared an approval gate that was never configured.
  • The tag is the source of truth for the version. The workflow checks out the tag and refuses when the tag and package.json disagree.
  • npm publish rather than pnpm publish, since the OIDC exchange is implemented in the npm CLI and that is what the siblings use.
  • A vX.Y.Z-rc.1-style tag publishes under the next dist-tag.

Kept, deviating from the siblings on purpose: the full typecheck / test / lint / build gate plus publint and attw, because publishing is the only thing that runs against the tagged tree specifically. Also SHA-pinned actions, which is this repo's convention with dependabot wired for it, and binding the tag to an env var rather than interpolating it into a shell block.

docs/release.md follows: bump and merge, tag, publish the release.

Why

streamctl was the odd one out, and in a way that could not work.

It read secrets.NPM_TOKEN. No such secret exists here, and the org appears to have moved off publish tokens deliberately: neither sibling references a token anywhere, and between them seven releases have shipped tokenless.

It also declared environment: release for an approval gate that does not exist. The repo has zero environments, no rulesets and no branch protection, and GitHub creates a referenced-but-missing environment unprotected and secretless. So the gate would have silently passed while the run went on to fail at publish on auth.

The tag/version check earns its place here more than in the siblings. build.config.ts injects package.json version into the CLI's --version string, so a tag that disagrees ships a CLI that misreports its own version.

Before this can publish

One setup step, on npmjs.com rather than in this repo. @sidebase/streamctl needs a trusted publisher registered against repository sidebase/streamctl and workflow release.yml. Without it the run reaches the publish step and fails on auth, having changed nothing.

kboshold added 2 commits July 31, 2026 09:07
…iblings

streamctl was the odd one out. sidebase/ssm-secrets and sidebase/nuxt-auth both
trigger on a published GitHub Release and publish with no secret at all, using
the OIDC token from `id-token: write` for npm trusted publishing. Four
ssm-secrets releases and three nuxt-auth releases have shipped that way.

This repo instead dispatched manually and read secrets.NPM_TOKEN, which does not
exist here and which the org appears to have moved off deliberately. It also
declared `environment: release` for an approval gate that was never configured:
the repo has zero environments, no rulesets and no branch protection, and GitHub
creates a referenced-but-missing environment unprotected and secretless, so the
gate silently did nothing.

Publishing the GitHub Release is now the human decision point, which is the
sibling convention and needs no configuration to work.

The tag becomes the source of truth for the version, so the workflow checks out
the tag and refuses when the tag and package.json version disagree. That check
matters more here than in the siblings: build.config.ts injects the version into
the CLI's --version string, so a mismatch ships a CLI that misreports itself.

Kept from the old workflow, deviating from the siblings on purpose: the full
typecheck/test/lint/build gate plus publint and attw, because publishing is the
only thing that runs against the tagged tree specifically; SHA-pinned actions,
which is this repo's convention and has dependabot wired for it; and binding the
tag to an env var rather than interpolating it into shell.

One setup step remains before this can publish: @sidebase/streamctl needs a
trusted publisher registered on npmjs.com for sidebase/streamctl + release.yml.
129 lines to 79. The runbook had grown to explain the design rather than the
procedure: how CONFIG_VERSION_MISMATCH is reported, how the version token is
substituted at build time, a paragraph narrowing the affected population of the
.config/ break. All true, none of it something you act on while shipping.

Kept every fact that changes what you do: the blocking setup item, the three
steps, why the tag must match package.json, why v0.1.0 was backfilled, and the
hand-written notes for the next release.

Also restores a citation that never resolved. init.ts quotes docs/release.md for
"no lockstep and no shared version number", and that phrase has only ever existed
in init.ts; it was never in this file. The claim is correct, so the Versioning
section now states it rather than the comment pointing at nothing.
@kboshold
kboshold merged commit eaa614d into main Jul 31, 2026
8 checks passed
@kboshold
kboshold deleted the ci/tokenless-release-on-published branch July 31, 2026 07:13
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