Skip to content

ci(release): split publish into three steps to force trusted publishing#11496

Merged
zkochan merged 1 commit into
mainfrom
chore/release-isolate-trusted-publishing
May 6, 2026
Merged

ci(release): split publish into three steps to force trusted publishing#11496
zkochan merged 1 commit into
mainfrom
chore/release-isolate-trusted-publishing

Conversation

@zkochan

@zkochan zkochan commented May 6, 2026

Copy link
Copy Markdown
Member

Summary

Splits the single Publish Packages step in release.yml into three sequential publish steps, isolating the packages we want to ship via npm's trusted publishing (pnpm and @pnpm/exe) into steps that don't have NPM_TOKEN set.

Why

pnpm publish currently bails out of OIDC as soon as a static _authToken is configured (see #11495 for the longer-term fix). Our release workflow writes NPM_TOKEN into pnpm's config before pn release, so OIDC is never attempted — even for packages where npm's trusted-publishing config would issue a token. The result is visible in the registry metadata for recent releases: pnpm@11.0.6 shows _npmUser: pnpmuser and no attestations.provenance, while the manually-published pnpm@11.0.0-alpha.5 shows _npmUser.trustedPublisher and a SLSA provenance block.

#11495 will fix the precedence rule properly, but until then we need a way to ship pnpm and @pnpm/exe via trusted publishing on the next release. The workaround: don't put a static token in their environment at all.

Changes

.github/workflows/release.yml — replace the Publish Packages step with three:

  1. Publish @pnpm/exe (trusted publishing) — no NPM_TOKEN. Builds the exe artifacts and publishes @pnpm/exe. With no token configured, pnpm publish reaches its OIDC code path and exchanges the GitHub Actions ID token at npm's trusted-publishing endpoint.
  2. Publish internal workspace packages (static token)NPM_TOKEN is set, written into pnpm config, the publish runs, then pn config delete removes the token. The workspace's internal packages don't have trusted publishing configured on npm yet, so they still need the static token.
  3. Publish pnpm CLI (trusted publishing) — no NPM_TOKEN. Same rationale as step 1. Must come after step 2 because step 2's pn config delete is what guarantees pnpm doesn't see a stale _authToken here.

pn release script in root package.json is left in place — unused after this change but harmless. Removing it is a separable cleanup.

Caveats

  • Trusted publishing must be configured on npm for both pnpm and @pnpm/exe (mapped to this repo + the release GitHub environment + this workflow file name). If either isn't set up, that step's OIDC exchange will return a 4xx and the publish will fail. Worth verifying before tagging the next release.
  • The internal-package step's pn config delete runs after the publish; if the publish itself fails the delete won't run, but the step then fails and the workflow stops, so the stale token can't leak.

Test plan

  • Verify trusted publishing is configured on npm for pnpm and @pnpm/exe.
  • On the first tagged release after merge, confirm the published metadata shows _npmUser.trustedPublisher and an attestations.provenance block for both pnpm and @pnpm/exe. The internal workspace packages should continue to show _npmUser: pnpmuser (no change there until they're onboarded to trusted publishing too).

Written by an agent (Claude Code, claude-opus-4-7).

Summary by CodeRabbit

  • Chores
    • Updated release workflow to split publish operations into distinct steps with improved token management and clearer publishing strategy documentation.

The previous "Publish Packages" step ran `pn release` after writing
NPM_TOKEN into pnpm's config. With a static `_authToken` configured,
`pnpm publish` bails out of OIDC entirely (see #11495 for the longer-
term fix), so every package — including `pnpm` and `@pnpm/exe` — was
silently being published with the legacy token instead of using npm's
trusted publishing. The result: published metadata showed
`_npmUser: pnpmuser` and no provenance attestation.

Until #11495 ships, work around the precedence bug by structuring the
job so the packages we *want* trusted publishing for never see a
static token at all:

1. `@pnpm/exe` — published in a step with no NPM_TOKEN. pnpm has no
   token to short-circuit on, performs OIDC, gets a `trustedPublisher`
   entry on npm.
2. Internal workspace packages — these don't have trusted publishing
   configured on npm, so they still need the static token. The token
   is written, the publish runs, then `pn config delete` removes the
   token before the next step.
3. `pnpm` — published in a step with no NPM_TOKEN, same rationale as
   step 1.

CI-only change; no changeset needed.
Copilot AI review requested due to automatic review settings May 6, 2026 15:04
@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown

Caution

Review failed

Failed to post review comments

📝 Walkthrough

Walkthrough

The release workflow now publishes three distinct packages in separate steps using either trusted publishing or static tokens, replacing the previous combined publish step with added explanatory comments clarifying the authentication strategy.

Changes

Release Workflow Publishing

Layer / File(s) Summary
Publish Steps Reorganization
.github/workflows/release.yml (lines 34–63)
The single Publish Packages step is split into three discrete steps: Publish @pnpm/exe, Publish internal workspace packages, and Publish pnpm CLI, each with tailored authentication handling.
Strategy Documentation
.github/workflows/release.yml (comments)
Explanatory comments are added to clarify the distinction between trusted publishing (for @pnpm/exe and pnpm CLI) and static token-based publishing (for internal workspace packages).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 Three hops instead of one—the release now hops with grace,
Each package finds its trusted path or token-sealed space,
No more combined confusion, just clarity in the flow,
Comments light the publish way, so reviewers all will know! 📦✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the primary change: splitting the publish step into three distinct steps to enable trusted publishing in the CI/release workflow.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/release-isolate-trusted-publishing

Comment @coderabbitai help to get the list of available commands and usage tips.

@zkochan zkochan merged commit d98ac7e into main May 6, 2026
14 checks passed
@zkochan zkochan deleted the chore/release-isolate-trusted-publishing branch May 6, 2026 15:19
zkochan added a commit that referenced this pull request May 6, 2026
…ng (#11496)

The previous "Publish Packages" step ran `pn release` after writing
NPM_TOKEN into pnpm's config. With a static `_authToken` configured,
`pnpm publish` bails out of OIDC entirely (see #11495 for the longer-
term fix), so every package — including `pnpm` and `@pnpm/exe` — was
silently being published with the legacy token instead of using npm's
trusted publishing. The result: published metadata showed
`_npmUser: pnpmuser` and no provenance attestation.

Until #11495 ships, work around the precedence bug by structuring the
job so the packages we *want* trusted publishing for never see a
static token at all:

1. `@pnpm/exe` — published in a step with no NPM_TOKEN. pnpm has no
   token to short-circuit on, performs OIDC, gets a `trustedPublisher`
   entry on npm.
2. Internal workspace packages — these don't have trusted publishing
   configured on npm, so they still need the static token. The token
   is written, the publish runs, then `pn config delete` removes the
   token before the next step.
3. `pnpm` — published in a step with no NPM_TOKEN, same rationale as
   step 1.

CI-only change; no changeset needed.
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