Skip to content

ci(publish): single Trusted Publisher entrypoint + reusable workflows - #367

Merged
codewizdave merged 2 commits into
stagingfrom
feat/publish-entrypoint
Aug 3, 2026
Merged

ci(publish): single Trusted Publisher entrypoint + reusable workflows#367
codewizdave merged 2 commits into
stagingfrom
feat/publish-entrypoint

Conversation

@martyy-code

Copy link
Copy Markdown
Contributor

Summary

Refactors the publish pipeline to use a single Trusted Publisher entrypoint (publish.yml) dispatching to three reusable workflows. This is the canonical 2026 pattern for npm Trusted Publishing, since npm allows exactly one Trusted Publisher configuration per package.

The Problem

Verified against the npm docs (2026-08-03): "Each package can only have one trusted publisher configured at a time."

The current pipeline (after #365 and #366) registers three publish workflows — release.yml, hotfix.yml, canary.yml — each attempting to publish via OIDC. With the 1-per-package limit, only one of the three can be registered as a Trusted Publisher, leaving the other two without OIDC trust and unable to publish.

The Solution: Entrypoint + Reusable Workflows

Pattern documented by Paige Niedringhaus and used in production by Vite, Cloudflare SDK, and others. npm validates the entrypoint (publish.yml), not the reusable workflows it dispatches to.

Files

  • .github/workflows/publish.yml (new) — The Trusted Publisher entrypoint. Registered on npmjs.com with workflow filename = publish.yml, environment = release. Routes events to the right reusable workflow:

    • push to main → release path
    • push tag vX.Y.Z on main → hotfix path
    • pull_request to staging → canary path
    • Declares id-token: write (the only file that does).
  • .github/workflows/_publish-release.yml (renamed from release.yml) — Reusable workflow (workflow_call trigger). Performs the stable release: anti-republish guard, build, test, smoke test, changeset publish --provenance --tag latest, tag creation, GitHub Release.

  • .github/workflows/_publish-hotfix.yml (renamed from hotfix.yml) — Reusable workflow. Tag-driven hotfix path. Verifies that package.json version matches the tag, anti-republish guard, build, test, smoke test, publish, GitHub Release.

  • .github/workflows/_publish-canary.yml (renamed from canary.yml) — Reusable workflow. Per-PR snapshot publishes. Detects pending changesets (skip cleanly when none), snapshots, publishes to dist-tag canary, comments on PR.

Removed

  • .github/workflows/release.yml
  • .github/workflows/hotfix.yml
  • .github/workflows/canary.yml

Runbook Update

docs/engineering/plans/release-pipeline-github-ui-setup.md is updated to:

  • Remove the hotfix and canary GitHub Environment sections. The single release environment covers all three paths.
  • Register exactly one Trusted Publisher on npmjs.com, pointing to publish.yml. Cite npm docs and the reusable-workflow pattern article.
  • Update all cross-references from release.yml to publish.yml.

Why this is the senior choice

  • One trust root for the package, not three. Easier to audit, easier to revoke, easier to reason about.
  • Reusable workflows isolate concerns. Each path has its own clear shape; the entrypoint is just routing.
  • Future-proof. If we add a fourth publish path (e.g. nightly.yml), it's a new reusable workflow + a 5-line dispatch block in publish.yml. No npm registration change.
  • Same OIDC strength. The reusable workflows inherit id-token: write from the entrypoint. Each publish still requires a fresh OIDC token, no long-lived secrets.

Test plan

  • pnpm turbo type-check passes.
  • pnpm turbo lint passes.
  • First push to staging after merge: _publish-canary.yml runs (via the entrypoint dispatch).
  • First Version Packages PR merged into main: _publish-release.yml runs and attempts Trusted Publishing.
  • Hotfix tag pushed on main: _publish-hotfix.yml runs and attempts Trusted Publishing.

Risk

Low. No publish has happened yet (Trusted Publisher not registered on npmjs.com), so no production impact. All three publish paths remain reachable through the entrypoint. The workflows are reorganized but functionally equivalent to their standalone counterparts.

Rollback

Restore the three original files (release.yml, hotfix.yml, canary.yml) from git history. The dispatch in publish.yml references the new reusable paths, so reverting publish.yml alone is not sufficient — restore all four files.

🤖 Generated with Claude Code

npm Trusted Publishing allows exactly ONE configuration per
package (verified against npm docs 2026-08-03). The previous
design assumed three entries could be registered (release.yml /
hotfix.yml / canary.yml) — this is not possible.

Refactor to a single entrypoint pattern:

publish.yml
  The Trusted Publisher. Registered on npmjs.com with
  workflow filename = 'publish.yml', environment = 'release'.
  Dispatches to reusable workflows based on event type.
  Triggers:
    - push to main           -> release path
    - push tag vX.Y.Z        -> hotfix path
    - pull_request to staging -> canary path
  Declares id-token: write (the only file that does).

_publish-release.yml
  Reusable workflow (workflow_call trigger). Performs the stable
  release: anti-republish guard, build, test, smoke test,
  changeset publish --provenance --tag latest, tag creation,
  GitHub Release.

_publish-hotfix.yml
  Reusable workflow for tag-driven hotfix path. Verifies that
  package.json version matches the tag, anti-republish guard,
  build, test, smoke test, publish --tag latest, GitHub Release.

_publish-canary.yml
  Reusable workflow for per-PR snapshot publishes. Detects
  pending changesets (skip cleanly when none), snapshots, publishes
  to dist-tag 'canary', comments on PR.

Design notes:
- Single environment ('release') covers all three flows.
  npm validates the entrypoint (publish.yml), not the
  reusable workflows.
- Reusable workflows declare permissions but do NOT request
  id-token themselves — that is inherited from the entrypoint.
- File naming: leading underscore ('_publish-*') signals that
  the file is not standalone; only publish.yml can be triggered
  by an event.
- workflow_dispatch inputs (the old release.yml 'reason' field)
  are removed; hotfix is now exclusively tag-driven per the
  pipeline design.

Removes:
- .github/workflows/release.yml (replaced by _publish-release.yml)
- .github/workflows/hotfix.yml  (replaced by _publish-hotfix.yml)
- .github/workflows/canary.yml  (replaced by _publish-canary.yml)

No behavioral change for end users; the publish path is
unchanged in shape, only the underlying files are reorganized.
Aligns the runbook with the publish.yml entrypoint refactor
implemented in 9beadbb / b0f3c0b.

§1 Environments:
- Drop the 'hotfix' and 'canary' environment sections. The
  single 'release' environment now covers all three publish paths
  (release / hotfix / canary), dispatched via reusable workflows.
- Optionally allow v* tags to be deployable to 'release'.

§5 Trusted Publisher:
- Replace the multi-entry (release.yml + hotfix.yml + canary.yml)
  guidance with a single entry pointing to publish.yml.
- Cite npm docs 2026-08-03 ('one trusted publisher per package')
  and the Paige Niedringhaus reusable-workflow article.
- Revocation note: if a previous entry exists, revoke it first
  via npm trust CLI or the npmjs.com UI.
- Verification: confirm exactly one entry on npmjs.com.

§6 Workflow permissions:
- Update reference from 'release.yml' to 'publish.yml' for the
  id-token: write override.

§7.2 Code Owners:
- Update file references from release.yml to publish.yml and the
  reusable workflows.

§8 Verification Checklist:
- Replace 'feat/release-pipeline' with 'feat/publish-entrypoint'.
- Replace 'release.yml' with 'publish.yml' as the file that must
  exist in main.

§9 Rollback:
- Add npm trust CLI command for revocation.

No code changes.
@codewizdave
codewizdave merged commit deccc68 into staging Aug 3, 2026
7 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.

2 participants