Publish to npm on tag push via trusted publishing (OIDC)#1048
Publish to npm on tag push via trusted publishing (OIDC)#1048dev-milos wants to merge 3 commits into
Conversation
Adds .github/workflows/release-publish.yml: on a v* tag push it builds and publishes the root bobbit package to npm using trusted publishing (OIDC) with automatic provenance -- no NPM_TOKEN, no OTP. Mirrors the validated gr-oss-developers/trusted-publishing-demo workflow (top-level id-token: write, Node 24 for npm >= 11.5.1, npm publish --provenance), plus the two things bobbit needs that the demo doesn't: npm ci (bobbit has deps; the build runs via the existing prepublishOnly hook) and a tag-vs-package.json-version guard. package.json gains repository/homepage/bugs. repository is required for provenance -- its URL must resolve to the building repo -- and was absent, which is why the runbook's manual `npm publish --provenance` never actually produced an attestation. Runbook (SKILL.md + docs/releasing.md) now delegates the root publish to CI: the human bumps the version, writes notes, signs the tag, and pushes it; the tag push triggers the publish. npm login/OTP is scoped to the binary sub-package republish case (still manual). Also fixes a stale SuuBro/bobbit reference to G-Research/bobbit. The root publish is not exercisable from a fork -- the npm trusted-publisher is bound to the G-Research/bobbit repo identity. Validated here: actionlint clean, npm publish --dry-run packs on Node 24, guard logic, and the JSON/YAML parse. Manual prerequisites (npm admin, not code): configure the trusted publisher on npmjs.com for bobbit -> G-Research/bobbit -> release-publish.yml. bobbit already exists on npm (0.14.2), so no manual first publish is needed.
A v* tag with a semver pre-release suffix (v1.0.0-rc.1, v0.15.0-beta) now publishes to the `next` dist-tag instead of `latest`, so a pre-release cannot become the version `npm install bobbit` resolves. Stable tags still go to `latest`.
Publish under the @gresearch npm org: sets name to @gresearch/bobbit and adds publishConfig.access public (scoped packages default to restricted). The bin command stays `bobbit`. This is a new scoped package on npm, so its first publish is a manual bootstrap before trusted publishing takes over.
Greptile SummaryAutomates npm publication when a version tag is pushed.
Confidence Score: 2/5This PR should not merge until the npm package identity is corrected and the publishing workflow enforces the approved release boundary. The workflow publishes the package identity declared in package.json, which now differs from the existing package and trusted-publisher target, and any qualifying upstream tag can trigger OIDC publication without validating its signature or approved commit ancestry. package.json, .github/workflows/release-publish.yml
|
| Filename | Overview |
|---|---|
| .github/workflows/release-publish.yml | Adds tag-triggered OIDC publication but does not verify that the tag or tagged commit passed the documented release approval process. |
| package.json | Adds provenance metadata and public access configuration but changes the package identity away from the existing bobbit package. |
| docs/releasing.md | Documents CI publication of the root package and manual binary publication ordering. |
| .claude/skills/release/SKILL.md | Updates the release automation instructions to push the tag before monitoring the CI-backed root publish. |
Sequence Diagram
sequenceDiagram
participant M as Maintainer
participant GH as GitHub Actions
participant N as npm Registry
M->>GH: "Push v* tag"
GH->>GH: Verify tag equals package.json version
GH->>GH: npm ci and prepublishOnly build
GH->>N: OIDC npm publish with provenance
N-->>GH: Publish latest or next dist-tag
Reviews (1): Last reviewed commit: "Scope the package as @gresearch/bobbit" | Re-trigger Greptile
| @@ -1,7 +1,18 @@ | |||
| { | |||
| "name": "bobbit", | |||
| "name": "@gresearch/bobbit", | |||
There was a problem hiding this comment.
There was a problem hiding this comment.
The package identity change is intentional. This is the first phase of a migration from bobbit to @gresearch/bobbit; the unscoped package will remain available during a transition period. This PR is not intended to dual-publish both identities.
The agreed activation sequence is:
- Merge this PR.
- Manually bootstrap
@gresearch/bobbit. - Configure its npm trusted-publisher binding.
- Only then push the next release tag.
Before merging, we should make the PR consistent with those assumptions:
- Update the PR description to identify
@gresearch/bobbitas the target, document the post-merge bootstrap as the one-time exception to normal CI-only publishing, and state that the next release tag follows bootstrap and OIDC configuration. - Regenerate
package-lock.jsonwith the scoped package name. - Update release verification and reporting to install/import/link to
@gresearch/bobbit.
User-facing installation instructions can continue referencing bobbit until the later migration phase. With these changes, the scoped package identity is the intended release target rather than an accidental one.
| push: | ||
| tags: | ||
| - 'v*' |
There was a problem hiding this comment.
Tag push bypasses release approval
When a collaborator able to push upstream tags creates a matching v* tag on arbitrary code, this workflow checks out that commit and obtains the npm OIDC identity without verifying tag signatures, master ancestry, or the approved merge commit, causing unreviewed package contents to be published.
There was a problem hiding this comment.
@SuuBro @dev-milos We can add a ruleset that protects the tags a bit later, once automated fashion of creating tags is implemented. Or, again add a bypass to who can create a tag in the very same ruleset
Publishes the root
bobbitpackage to npm on av*tag push, using trusted publishing (OIDC) — noNPM_TOKEN, no OTP, provenance generated automatically. Mirrors the approach validated ingr-oss-developers/trusted-publishing-demo.Changes
.github/workflows/release-publish.yml— onpush: tags: 'v*': guard that the tag matchespackage.jsonversion →npm ci→npm publish --provenance. Node 24 (npm ≥ 11.5.1 for OIDC); the build runs via the existingprepublishOnlyhook. Pre-release tags (v1.0.0-rc.1) publish to thenextdist-tag; stable tags tolatest.package.json— addsrepository/homepage/bugs.repositoryis required for provenance and was missing, which is why the old manual--provenancenever produced an attestation.SKILL.md,docs/releasing.md) — the human bumps the version, writes notes, signs and pushes the tag; the tag push triggers the publish.npm login/OTP now applies only to the manual binary sub-package publishes.Scope: root package only; sub-packages, version bump, notes, signed tag, and the GitHub Release stay as they are.
To activate (npm admin, not in this PR)
Configure the Trusted Publisher on npmjs.com →
bobbit→ orgG-Research, repobobbit, workflowrelease-publish.yml.bobbitalready exists on npm (0.14.2), so no manual first publish is needed. The workflow filename must match the npm config exactly.Validation
actionlintclean,npm publish --dry-runpacks on Node 24,npm ciin sync. The real OIDC publish can't be tested from a fork (the trusted-publisher binding is scoped toG-Research/bobbit) — the first real tag is the live test, recoverable by re-running the workflow for the same version.