ci(pnpr): add manual release workflow that publishes @pnpm/pnpr to npm#11963
Conversation
Mirror pacquet's release pipeline for the pnpm-registry crate: - New `Release pnpm-registry` GitHub workflow (manual `workflow_dispatch` with a version input) builds the `pnpm-registry` binary for six `<os>-<cpu>` matrix legs via `cross`, attests provenance, and uploads artifacts. - The publish job patches `registry/npm/pnpm-registry/package.json`'s `version` field with the input, downloads the artifacts, runs `generate-packages.mjs` to produce per-platform packages, then publishes everything via `pnpm publish --provenance` using OIDC. - The wrapper package is `pnpm-registry`; the per-platform binary packages are scoped `@pnpm/registry.<os>-<cpu>` and resolved by a Node shim under `bin/pnpm-registry`. The CLI's `--version` output comes from `CARGO_PKG_VERSION` via clap's derive `version` attribute, so the build leg patches the crate's `Cargo.toml` `version` field rather than a hardcoded clap string. --- Written by an agent (Claude Code, claude-opus-4-7).
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📜 Recent review details⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
🔇 Additional comments (1)
📝 WalkthroughWalkthroughAdds an Changespnpm-registry npm Release Pipeline
Sequence DiagramsBuild Job Multi-Platform Compilation & Attestation: sequenceDiagram
participant GHA as GitHub Actions
participant Matrix as Build Matrix
participant Cross as cross
participant Archive as Archiver
participant Attest as actions/attest-build-provenance
GHA->>Matrix: workflow_dispatch(version)
Matrix->>Cross: cargo build --release for target
Cross-->>Matrix: compiled binary
Matrix->>Archive: create .zip/.tar.gz per target
Archive-->>Matrix: artifact
Matrix->>Attest: attest artifact provenance
Attest-->>GHA: attested artifact uploaded
Artifact Unpacking & npm Publish Flow: sequenceDiagram
participant GHA as GitHub Actions
participant Artifacts as Build Artifacts
participant GeneratePkg as generate-packages.mjs
participant OIDC as OIDC Provider
participant NPM as npm Registry
GHA->>Artifacts: download binaries-* artifacts
Artifacts-->>GHA: unzip/untar binaries
GHA->>GeneratePkg: run to create `@pnpm/pnpr-`* packages
GeneratePkg-->>GHA: package dirs ready
GHA->>OIDC: request id-token (id-token: write)
OIDC-->>GHA: id-token
GHA->>NPM: pnpm publish --provenance for each package
NPM-->>GHA: packages published (latest)
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly Related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
registry/npm/pnpm-registry/scripts/generate-packages.mjs (1)
19-20: ⚡ Quick winFail fast when wrapper version is missing.
Add a guard for
rootManifest.versionbefore generating package manifests; otherwise this can produce invalid versions and fail late during publish.Suggested patch
const rootManifest = JSON.parse(fs.readFileSync(MANIFEST_PATH, "utf-8")); +if (!rootManifest.version || typeof rootManifest.version !== "string") { + throw new Error(`Missing or invalid version in ${MANIFEST_PATH}`); +}Also applies to: 36-46, 78-79
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@registry/npm/pnpm-registry/scripts/generate-packages.mjs` around lines 19 - 20, Add a guard that validates rootManifest.version (read from MANIFEST_PATH via JSON.parse) immediately after parsing rootManifest and before any package manifest generation; if rootManifest.version is missing or falsy, throw an Error or exit with a clear message so the script fails fast. Apply the same check/guard before the code blocks that build package versions (the sections around the previously noted ranges 36-46 and 78-79) so no invalid/undefined versions get written into generated manifests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/pnpm-registry-release-to-npm.yml:
- Around line 10-11: The workflow currently always passes "--tag latest" when
publishing, which will publish prerelease versions
(workflow_dispatch.inputs.version) to the latest dist-tag; change the publish
step to compute the tag instead of hard-coding "--tag latest": detect if the
provided version string contains a prerelease identifier (a hyphen / semver
prerelease like "-rc", "-alpha", "-beta") and if so use a non-latest tag (e.g.,
"next" or a provided inputs.tag), otherwise use "latest"; update the publish
command to use the computed tag variable instead of the literal "--tag latest"
so prereleases do not overwrite latest.
- Around line 194-203: Add a preflight guard before the publish loop that
verifies every target package can be published (e.g., using pnpm publish
--dry-run or an existence check like npm view <pkg>@<version>) and aborts the
job if any check fails; modify the script that currently iterates with for
package in registry/npm/pnpm-registry* and runs pnpm publish "$package/" --tag
latest --access public --provenance --no-git-checks so it first collects all
$package paths, performs the dry-run/exists check for each, and only proceeds to
the real pnpm publish loop if all checks pass.
- Around line 180-183: The "Unzip" step using
montudor/action-zip@0852c26906e00f8a315c704958823928d8018b28 fails to expand
*.zip because with.args is executed without a shell; change the Unzip step’s
with.args to invoke a shell that iterates over each ZIP and runs unzip on each
archive (or refactor to run unzip once per archive via a loop/matrix), ensuring
each archive is extracted separately; also while editing this workflow, avoid
publishing prereleases with the latest tag (do not use --tag latest for
prereleases) and make the publish loop idempotent so reruns don’t fail for
already-published package@version pairs.
In `@registry/npm/pnpm-registry/bin/pnpm-registry`:
- Around line 31-35: The child process result from spawnSync can have
result.status === null when terminated by a signal; instead of assigning
process.exitCode directly, detect the signaled exit (result.status === null &&
result.signal) and propagate it explicitly (e.g., re-emit the same signal for
the current process using process.kill(process.pid, result.signal) or otherwise
exit with a non-zero code), otherwise set process.exitCode = result.status as
before; keep the existing error throw for result.error. Use the existing result
variable and the properties result.status and result.signal to locate and
implement this logic where process.exitCode is currently assigned.
---
Nitpick comments:
In `@registry/npm/pnpm-registry/scripts/generate-packages.mjs`:
- Around line 19-20: Add a guard that validates rootManifest.version (read from
MANIFEST_PATH via JSON.parse) immediately after parsing rootManifest and before
any package manifest generation; if rootManifest.version is missing or falsy,
throw an Error or exit with a clear message so the script fails fast. Apply the
same check/guard before the code blocks that build package versions (the
sections around the previously noted ranges 36-46 and 78-79) so no
invalid/undefined versions get written into generated manifests.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 67bbd0fc-7cbc-4f3f-bb49-28a6cb391f1c
📒 Files selected for processing (5)
.github/workflows/pnpm-registry-release-to-npm.ymlregistry/npm/pnpm-registry/README.mdregistry/npm/pnpm-registry/bin/pnpm-registryregistry/npm/pnpm-registry/package.jsonregistry/npm/pnpm-registry/scripts/generate-packages.mjs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Lint and Test (windows-latest)
- GitHub Check: Lint and Test (macos-latest)
- GitHub Check: Lint and Test (ubuntu-latest)
- GitHub Check: Compile & Lint
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-05-05T23:03:04.286Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11479
File: __utils__/scripts/package.json:6-9
Timestamp: 2026-05-05T23:03:04.286Z
Learning: The pattern cross-env NODE_OPTIONS="$NODE_OPTIONS ..." in package.json scripts is an established convention in the pnpm/pnpm repository and is used across many packages (e.g., fs/hard-link-dir, worker, __utils__/scripts). Do not flag this as a cross-platform issue in individual files; if a change is needed, apply it as a repo-wide change in a separate PR. Scope this guidance to all package.json files in the repo; use the minimatch pattern '**/package.json' to identify relevant files and review changes at the repository level rather than per-file.
Applied to files:
registry/npm/pnpm-registry/package.json
🔇 Additional comments (4)
registry/npm/pnpm-registry/package.json (1)
1-24: LGTM!registry/npm/pnpm-registry/README.md (1)
1-20: LGTM!registry/npm/pnpm-registry/bin/pnpm-registry (1)
1-30: LGTM!Also applies to: 37-42
registry/npm/pnpm-registry/scripts/generate-packages.mjs (1)
1-18: LGTM!Also applies to: 21-35, 47-77, 80-95
…aries Shorter, scoped name that reads naturally on npm: `@pnpm/pnpr` for the wrapper, `@pnpm/pnpr.<os>-<cpu>` for the six per-platform binary packages (mirroring `@pacquet/<os>-<cpu>` under a different scope). - Move `registry/npm/pnpm-registry/` to `registry/npm/pnpr/` and rename the JS shim and `bin` entry to `pnpr`. - Update `generate-packages.mjs` so `BIN_NAME = pnpr` (used for both the binary file inside each platform package and the autogenerated package dir) and the scope/prefix produce `@pnpm/pnpr.<os>-<cpu>`. - Rename the workflow file to `pnpr-release-to-npm.yml` and rename the built `pnpm-registry` binary to `pnpr-<code-target>` at archive time, so `generate-packages.mjs` finds it at `REPO_ROOT/pnpr-<plat>-<arch>`. - The Rust crate name stays `pnpm-registry` (still what `cross build -p` targets); only the npm-publishing layer is renamed. --- Written by an agent (Claude Code, claude-opus-4-7).
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
registry/npm/pnpr/scripts/generate-packages.mjs (1)
82-83:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFormat the manifest JSON for readability.
The manifest is written without formatting, producing a minified single-line file. This is inconsistent with the native package manifests (line 55 uses
JSON.stringify(manifest, null, 2)) and makes debugging harder.📝 Proposed fix
- const content = JSON.stringify(manifestData); + const content = JSON.stringify(manifestData, null, 2); fs.writeFileSync(manifestPath, content);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@registry/npm/pnpr/scripts/generate-packages.mjs` around lines 82 - 83, The manifest is being written minified; change the write to produce pretty-printed JSON by serializing manifestData with indentation (use JSON.stringify(manifestData, null, 2)) and write that plus a trailing newline to manifestPath when calling fs.writeFileSync in generate-packages.mjs so the output matches the other manifest formatting (consistent with the earlier JSON.stringify(manifest, null, 2) usage)..github/workflows/pnpr-release-to-npm.yml (1)
139-147:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRestore
contents: readon thepublishjob.Job-level
permissionsoverrides workflow-level defaults forGITHUB_TOKEN; with onlyid-token: writeset onpublish,actions/checkoutin that job no longer getscontents: readaccess (which checkout recommends for proper operation when using the default token).🔧 Minimal fix
publish: name: Publish runs-on: ubuntu-latest permissions: + contents: read # Required for npm provenance attestations via OIDC. id-token: write🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/pnpr-release-to-npm.yml around lines 139 - 147, The job-level permissions block currently only sets id-token: write which prevents actions/checkout in the publish job from getting the default contents: read scope; update the publish job's permissions to include contents: read alongside id-token: write (i.e., add the contents: read permission in the same permissions mapping) so that the actions/checkout step (uses: actions/checkout..., with: persist-credentials: false) can operate correctly with the GITHUB_TOKEN.
♻️ Duplicate comments (1)
registry/npm/pnpr/bin/pnpr (1)
31-35:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win[Duplicate] Handle null status when child terminates via signal.
This concern was previously raised and remains unaddressed. When
spawnSyncreturnsresult.status === null(child terminated by signal), assigning it directly toprocess.exitCodemasks the failure.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@registry/npm/pnpr/bin/pnpr` around lines 31 - 35, The code assigns result.status directly to process.exitCode even when result.status === null (child terminated by signal), which masks the failure; update the logic around result (from spawnSync) to detect result.status === null and instead set process.exitCode to a non-zero value (e.g., 1) or derive a conventional code from the terminating signal by checking result.signal (preferably set process.exitCode = 128 + signalNumber if you map signal names to numbers), and ensure process.exitCode is only assigned the numeric status when result.status is a non-null number; reference the variables result.status, result.signal and the assignment to process.exitCode to locate the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In @.github/workflows/pnpr-release-to-npm.yml:
- Around line 139-147: The job-level permissions block currently only sets
id-token: write which prevents actions/checkout in the publish job from getting
the default contents: read scope; update the publish job's permissions to
include contents: read alongside id-token: write (i.e., add the contents: read
permission in the same permissions mapping) so that the actions/checkout step
(uses: actions/checkout..., with: persist-credentials: false) can operate
correctly with the GITHUB_TOKEN.
In `@registry/npm/pnpr/scripts/generate-packages.mjs`:
- Around line 82-83: The manifest is being written minified; change the write to
produce pretty-printed JSON by serializing manifestData with indentation (use
JSON.stringify(manifestData, null, 2)) and write that plus a trailing newline to
manifestPath when calling fs.writeFileSync in generate-packages.mjs so the
output matches the other manifest formatting (consistent with the earlier
JSON.stringify(manifest, null, 2) usage).
---
Duplicate comments:
In `@registry/npm/pnpr/bin/pnpr`:
- Around line 31-35: The code assigns result.status directly to process.exitCode
even when result.status === null (child terminated by signal), which masks the
failure; update the logic around result (from spawnSync) to detect result.status
=== null and instead set process.exitCode to a non-zero value (e.g., 1) or
derive a conventional code from the terminating signal by checking result.signal
(preferably set process.exitCode = 128 + signalNumber if you map signal names to
numbers), and ensure process.exitCode is only assigned the numeric status when
result.status is a non-null number; reference the variables result.status,
result.signal and the assignment to process.exitCode to locate the change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: e2e2bfe5-92d5-4a90-97f6-2a0d65a0acdf
📒 Files selected for processing (5)
.github/workflows/pnpr-release-to-npm.ymlregistry/npm/pnpr/README.mdregistry/npm/pnpr/bin/pnprregistry/npm/pnpr/package.jsonregistry/npm/pnpr/scripts/generate-packages.mjs
✅ Files skipped from review due to trivial changes (2)
- registry/npm/pnpr/package.json
- registry/npm/pnpr/README.md
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: Lint and Test (windows-latest)
- GitHub Check: Dylint
- GitHub Check: Doc
- GitHub Check: Lint and Test (ubuntu-latest)
- GitHub Check: Lint and Test (macos-latest)
- GitHub Check: Analyze (javascript)
- GitHub Check: Compile & Lint
🔇 Additional comments (2)
registry/npm/pnpr/bin/pnpr (1)
4-17: LGTM!Also applies to: 36-42
registry/npm/pnpr/scripts/generate-packages.mjs (1)
8-23: LGTM!Also applies to: 25-67, 69-79, 86-95
`spawnSync` returns `result.status === null` when the child terminates via a signal. Assigning that to `process.exitCode` makes the parent exit 0 and masks the signal — bad for a long-running server where the operator is most likely to kill it with SIGINT/SIGTERM. Re-raise the signal so the parent terminates the same way, falling back to a non-zero exit code if for some reason we can't. --- Written by an agent (Claude Code, claude-opus-4-7).
the changes mirror how pacquet is released today.
Summary
Mirror pacquet's release pipeline (
pacquet-release-to-npm.yml) for thepnpm-registrycrate, so the binary can be installed viapnpm add -g @pnpm/pnpr.Release @pnpm/pnprGitHub workflow (.github/workflows/pnpr-release-to-npm.yml): manualworkflow_dispatchwith aversioninput. Six matrix legs (win32-x64,win32-arm64,linux-x64,linux-arm64,darwin-x64,darwin-arm64) build thepnpm-registrybinary viacross, rename it topnpr-<code-target>for archival, attest build provenance, and upload artifacts.registry/npm/pnpr/package.json'sversionfield with the input, downloads the artifacts, runsgenerate-packages.mjsto produce per-platform packages, then publishes everything viapnpm publish --provenanceusing OIDC trusted publishing.Package layout
@pnpm/pnprbin/pnpr) that resolves to the native binary.@pnpm/pnpr.win32-x64@pnpm/pnpr.win32-arm64@pnpm/pnpr.linux-x64@pnpm/pnpr.linux-arm64@pnpm/pnpr.darwin-x64@pnpm/pnpr.darwin-arm64The wrapper depends on the six per-platform packages via
optionalDependencies, so npm/pnpm only installs the one matching the host'sos×cpu.Naming
The Rust crate stays
pnpm-registry(still whatcross build -ptargets); only the npm-publishing layer is renamed. The built binary is renamed topnprat archive time so the artifact name, the shim's resolved path, and the on-disk binary inside each@pnpm/pnpr.<os>-<cpu>package all match.Version sourcing
pnpm-registry's CLI usesclap's derive#[command(version)]attribute (registry/crates/pnpm-registry/src/main.rs:11), which reads fromCARGO_PKG_VERSION. The build leg patches the crate'sCargo.tomlversionfield rather than a hardcoded clap string (which is what pacquet does). The publishedpackage.json'sversionis patched separately in the publish job. Nothing is committed back — both patches happen in the CI checkout and are discarded after publish.Test plan
0.0.1-test.0); confirm six binaries build cleanly and the publish step would emit seven packagespnpm add -g @pnpm/pnpron each of Linux/macOS/Windows and confirmpnpr --versionreports the published versionWritten by an agent (Claude Code, claude-opus-4-7).
Summary by CodeRabbit
New Releases
New Features
Documentation