fix(ci): publish to npm with provenance instead of bun publish#12
Conversation
The npm-publish job used `bun publish`, which fails auth here (404 on the scoped package) and cannot emit npm provenance — contrary to the repo standard. - publish via `npm publish --provenance` from a `bun pm pack` tarball - add actions/setup-node (registry auth) and job-level `id-token: write` for OIDC provenance - add the required `repository` field to package.json (provenance needs it) - add a workflow_dispatch trigger so an already-tagged release (v0.3.0) can be (re)published manually
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe release workflow now accepts manual tag/ref input, runs publishing for manual or Release Please releases, checks out the requested ref, packs the release tarball, and publishes it with npm provenance. ChangesRelease publishing workflow
Sequence Diagram(s)sequenceDiagram
participant GitHubActions as GitHub Actions
participant ReleasePleaseWorkflow as release-please workflow
participant ActionsCheckout as actions/checkout
participant ActionsSetupNode as actions/setup-node
participant BunPmPack as bun pm pack
participant NpmPublish as npm publish
GitHubActions->>ReleasePleaseWorkflow: workflow_dispatch with ref
ReleasePleaseWorkflow->>ActionsCheckout: checkout ref
ReleasePleaseWorkflow->>ActionsSetupNode: set up Node.js 22
ReleasePleaseWorkflow->>BunPmPack: pack tarball
ReleasePleaseWorkflow->>NpmPublish: publish with --provenance and NODE_AUTH_TOKEN
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Greptile SummaryThis PR fixes the broken npm publish step by replacing
Confidence Score: 3/5The publish flow itself is correct, but the manual re-publish path can be silently skipped if the upstream release-please job fails, leaving the team with no indication that publish did not run. The npm publish --provenance wiring is sound: correct env var, correct OIDC permission, correct glob. The fragility is in the workflow_dispatch path: because npm-publish depends on release-please without if: always(), a transient failure in the release-please job will silently skip the publish step. For a workflow specifically designed to unblock a stuck release, this silent-skip behaviour is the kind of defect that only surfaces at the worst possible moment. .github/workflows/release-please.yml — the needs/if interaction on the npm-publish job needs attention before relying on the workflow_dispatch path in production. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant GH as GitHub Event
participant RP as release-please job
participant NP as npm-publish job
participant NPM as registry.npmjs.org
participant OIDC as GitHub OIDC
alt push to main
GH->>RP: trigger (push)
RP->>RP: create-github-app-token
RP->>RP: release-please-action (may create release)
RP-->>NP: "output: release_created=true"
NP->>NP: checkout (github.ref)
NP->>NP: setup-bun + setup-node (registry auth)
NP->>NP: bun install + bun run build
NP->>NP: bun pm pack to tgz
NP->>OIDC: request OIDC token (id-token: write)
OIDC-->>NP: OIDC token
NP->>NPM: npm publish tgz --provenance
NPM-->>NP: published with provenance attestation
else "workflow_dispatch (ref=v0.3.0)"
GH->>RP: trigger (dispatch)
RP->>RP: release-please-action (no-op)
RP-->>NP: "release_created='' (falsy)"
Note over NP: github.event_name==workflow_dispatch is true
NP->>NP: "checkout (github.event.inputs.ref = v0.3.0)"
NP->>NP: setup-bun + setup-node
NP->>NP: bun install + bun run build
NP->>NP: bun pm pack to tgz
NP->>OIDC: request OIDC token
OIDC-->>NP: OIDC token
NP->>NPM: npm publish tgz --provenance
NPM-->>NP: published
end
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant GH as GitHub Event
participant RP as release-please job
participant NP as npm-publish job
participant NPM as registry.npmjs.org
participant OIDC as GitHub OIDC
alt push to main
GH->>RP: trigger (push)
RP->>RP: create-github-app-token
RP->>RP: release-please-action (may create release)
RP-->>NP: "output: release_created=true"
NP->>NP: checkout (github.ref)
NP->>NP: setup-bun + setup-node (registry auth)
NP->>NP: bun install + bun run build
NP->>NP: bun pm pack to tgz
NP->>OIDC: request OIDC token (id-token: write)
OIDC-->>NP: OIDC token
NP->>NPM: npm publish tgz --provenance
NPM-->>NP: published with provenance attestation
else "workflow_dispatch (ref=v0.3.0)"
GH->>RP: trigger (dispatch)
RP->>RP: release-please-action (no-op)
RP-->>NP: "release_created='' (falsy)"
Note over NP: github.event_name==workflow_dispatch is true
NP->>NP: "checkout (github.event.inputs.ref = v0.3.0)"
NP->>NP: setup-bun + setup-node
NP->>NP: bun install + bun run build
NP->>NP: bun pm pack to tgz
NP->>OIDC: request OIDC token
OIDC-->>NP: OIDC token
NP->>NPM: npm publish tgz --provenance
NPM-->>NP: published
end
Prompt To Fix All With AIFix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
.github/workflows/release-please.yml:37-40
**Manual re-publish silently skipped when `release-please` fails**
`npm-publish` uses `needs: release-please` without `if: always()`. If the `release-please` job exits with a failure (transient API error, GitHub App token scope issue, etc.), GitHub Actions skips — not fails — any downstream job, bypassing the `if` condition entirely. On a `workflow_dispatch` run this means the whole manual re-publish path silently does nothing, which is exactly the scenario this PR is designed to unblock.
Adding `always()` to the condition guards against this:
```yaml
if: ${{ always() && (needs.release-please.outputs.release_created || github.event_name == 'workflow_dispatch') }}
```
This still correctly prevents a publish when a `push` run completes without creating a release (`release_created` is falsy and `event_name` is `push`).
### Issue 2 of 2
.github/workflows/release-please.yml:46-48
**Inconsistent action pinning**
`actions/setup-node` is pinned to a full commit SHA (good practice), but `actions/checkout@v4`, `oven-sh/setup-bun@v2`, and `actions/cache@v4` still use floating mutable tags. A compromised or accidentally force-pushed tag in any of those actions would run untrusted code in the same job that holds the `NPM_TOKEN` secret and the OIDC token. Pinning all actions to SHAs would make the supply-chain posture consistent.
Reviews (1): Last reviewed commit: "fix(ci): publish to npm with provenance ..." | Re-trigger Greptile |
| npm-publish: | ||
| needs: release-please | ||
| if: ${{ needs.release-please.outputs.release_created }} | ||
| # Publish on a fresh release, or manually for a given tag via workflow_dispatch. | ||
| if: ${{ needs.release-please.outputs.release_created || github.event_name == 'workflow_dispatch' }} |
There was a problem hiding this comment.
Manual re-publish silently skipped when
release-please fails
npm-publish uses needs: release-please without if: always(). If the release-please job exits with a failure (transient API error, GitHub App token scope issue, etc.), GitHub Actions skips — not fails — any downstream job, bypassing the if condition entirely. On a workflow_dispatch run this means the whole manual re-publish path silently does nothing, which is exactly the scenario this PR is designed to unblock.
Adding always() to the condition guards against this:
if: ${{ always() && (needs.release-please.outputs.release_created || github.event_name == 'workflow_dispatch') }}This still correctly prevents a publish when a push run completes without creating a release (release_created is falsy and event_name is push).
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/release-please.yml
Line: 37-40
Comment:
**Manual re-publish silently skipped when `release-please` fails**
`npm-publish` uses `needs: release-please` without `if: always()`. If the `release-please` job exits with a failure (transient API error, GitHub App token scope issue, etc.), GitHub Actions skips — not fails — any downstream job, bypassing the `if` condition entirely. On a `workflow_dispatch` run this means the whole manual re-publish path silently does nothing, which is exactly the scenario this PR is designed to unblock.
Adding `always()` to the condition guards against this:
```yaml
if: ${{ always() && (needs.release-please.outputs.release_created || github.event_name == 'workflow_dispatch') }}
```
This still correctly prevents a publish when a `push` run completes without creating a release (`release_created` is falsy and `event_name` is `push`).
How can I resolve this? If you propose a fix, please make it concise.| - uses: actions/checkout@v4 | ||
| with: | ||
| ref: ${{ github.event.inputs.ref || github.ref }} |
There was a problem hiding this comment.
actions/setup-node is pinned to a full commit SHA (good practice), but actions/checkout@v4, oven-sh/setup-bun@v2, and actions/cache@v4 still use floating mutable tags. A compromised or accidentally force-pushed tag in any of those actions would run untrusted code in the same job that holds the NPM_TOKEN secret and the OIDC token. Pinning all actions to SHAs would make the supply-chain posture consistent.
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/release-please.yml
Line: 46-48
Comment:
**Inconsistent action pinning**
`actions/setup-node` is pinned to a full commit SHA (good practice), but `actions/checkout@v4`, `oven-sh/setup-bun@v2`, and `actions/cache@v4` still use floating mutable tags. A compromised or accidentally force-pushed tag in any of those actions would run untrusted code in the same job that holds the `NPM_TOKEN` secret and the OIDC token. Pinning all actions to SHAs would make the supply-chain posture consistent.
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
The v0.3.0 release tag + GitHub release were created, but the npm-publish job failed:
bun publishfails auth here and can't emit npm provenance, which the repo standard (CLAUDE.md) explicitly requires.Changes
npm publish --provenancefrom abun pm packtarball (bun still does install/build)actions/setup-node(pinned to SHA) for registry auth + Node, and job-levelid-token: writefor OIDC provenancerepositoryfield topackage.json(required by--provenance)workflow_dispatchtrigger (with arefinput) so the already-tagged v0.3.0 can be re-published manually after this mergesFollow-up after merge
Run the workflow manually for the existing tag:
Summary by cubic
Fixes the failed npm publish by switching from
bun publishtonpm publish --provenancewith OIDC, so releases publish correctly and include required provenance.Bug Fixes
npm publish --provenanceusing a tarball frombun pm pack.actions/setup-nodefor registry auth and setid-token: writefor provenance.repositoryfield topackage.json(required by provenance).workflow_dispatchwith arefinput to manually publish existing tags (e.g.,v0.3.0).Migration
gh workflow run release-please.yml --ref main -f ref=v0.3.0Written for commit 9fd0060. Summary will update on new commits.
Summary by CodeRabbit