feat(pnpm): publish pnpm v12 (the Rust port) as pnpm and @pnpm/exe (#12670)

Make the pacquet Rust port installable from npm as pnpm v12, published with
equal content under two names — `pnpm` and `@pnpm/exe` — on the `next-12`
dist-tag (never `latest`, which keeps serving the production TypeScript pnpm
v11). First release is 12.0.0-alpha.0.

CLI presents as pnpm: clap name/bin_name are `pnpm`; PACQUET_VERSION holds the
pnpm 12.x version; the default User-Agent is the plain `pnpm/<version>` form;
the reporter footer reads `Done in ... using pnpm v<version>`; the `pnpm link`
/ `pnpm patch` / `pnpm patch-commit` hints now name pnpm (parity fixes); and
launched as `pnpx`/`pnx`, the binary injects the `dlx` subcommand itself by
detecting its current_exe name (porting pnpm's buildArgv).

npm packaging mirrors how `@pnpm/exe` ships pnpm: both the `pnpm` and `@pnpm/exe`
wrappers ship root-level placeholder bins (pnpm/pn/pnpx/pnx) plus an install.js
preinstall that hardlinks the host's native binary over the placeholder (no Node
launcher). Native binaries publish as `@pnpm/exe.<platform>-<arch>[-musl]` (file
`pnpm` inside). `pacquet`/`@pnpm/pacquet` are no longer published.

installPnpm initializes v12 exactly like `@pnpm/exe`: linkExePlatformBinary takes
the wrapper name and resolves the native binary via require, and PNPM_ALLOW_BUILDS
includes both names so the GVS hash is per-platform. resolvePackageManagerIntegrities
already resolves both `pnpm` and `@pnpm/exe`, so publishing `@pnpm/exe@12` makes
self-update / version-switch work with no v12-specific logic. From v12 the
install always uses the `pnpm` package (even from a `@pnpm/exe` build), via
pnpmPackageNameToInstall().

The configDependencies install-engine path still installs the published
`pacquet`/`@pnpm/pacquet` releases (which ship `@pacquet/<target>` binaries) and
is left untouched.
This commit is contained in:
Zoltan Kochan
2026-06-27 01:24:33 +02:00
committed by GitHub
parent 103b99bdb4
commit a33eeec9cd
31 changed files with 748 additions and 331 deletions

View File

@@ -1,16 +1,27 @@
name: Release Pacquet
name: Release pnpm (Rust)
# Manual-trigger only. Type the version to publish — the workflow patches
# pacquet/npm/pacquet/package.json with it before generating per-platform
# packages and publishing. No git tag is created and no GitHub release
# asset is uploaded; npm is the authoritative artifact store.
# Manual-trigger only: patches the version into pacquet/npm/pnpm/package.json,
# generates the per-platform packages, and publishes (no git tag / release asset
# — npm is the artifact store). Ships the Rust port under two names, `pnpm` and
# `@pnpm/exe`, both depending on the `@pnpm/exe.<target>` natives. The `tag`
# defaults to `next-12` so a release never touches the production `latest`.
on:
workflow_dispatch:
inputs:
version:
description: 'Version to publish (e.g. 0.2.3 or 0.2.3-rc.1)'
description: 'Version to publish (e.g. 12.0.0-alpha.0)'
required: true
type: string
tag:
# A `choice` (not free-form) so a mistaken `latest` can't move the
# production `pnpm` dist-tag. `latest` is intentionally omitted.
description: 'npm dist-tag to publish under'
required: true
default: 'next-12'
type: choice
options:
- next-12
- next
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -160,18 +171,15 @@ jobs:
exit 1
fi
- name: Inject version into pacquet/npm/pacquet/package.json
# The committed package.json has no `version` field; the version comes
# from the workflow_dispatch input. Patching it here means
# generate-packages.mjs (which copies the version into each
# per-platform manifest) and `pnpm publish` (which reads from
# package.json) both see the right value.
- name: Inject version into pacquet/npm/pnpm/package.json
# The committed package.json has no `version`; patch it from the input so
# generate-packages.mjs and `pnpm publish` both pick it up.
env:
VERSION: ${{ inputs.version }}
run: |
jq --arg v "$VERSION" '.version = $v' pacquet/npm/pacquet/package.json > pacquet/npm/pacquet/package.json.tmp
mv pacquet/npm/pacquet/package.json.tmp pacquet/npm/pacquet/package.json
cat pacquet/npm/pacquet/package.json
jq --arg v "$VERSION" '.version = $v' pacquet/npm/pnpm/package.json > pacquet/npm/pnpm/package.json.tmp
mv pacquet/npm/pnpm/package.json.tmp pacquet/npm/pnpm/package.json
cat pacquet/npm/pnpm/package.json
- name: Install pnpm and Node
uses: pnpm/setup@77cf06832101b3ac8c65caaf76a21643936d07a4
@@ -195,22 +203,19 @@ jobs:
- name: Generate npm packages
run: |
node pacquet/npm/pacquet/scripts/generate-packages.mjs
cat pacquet/npm/pacquet/package.json
for package in pacquet/npm/pacquet* pacquet/npm/pnpm-pacquet; do cat $package/package.json ; echo ; done
node pacquet/npm/pnpm/scripts/generate-packages.mjs
cat pacquet/npm/pnpm/package.json
for package in pacquet/npm/pacquet-* pacquet/npm/pnpm*; do cat $package/package.json ; echo ; done
- name: Publish npm packages as latest
# Auth is via npm's trusted publishing: `id-token: write` above grants
# this job an OIDC token that pnpm/npm exchange with the registry,
# so no NPM_TOKEN is needed. `--provenance` attaches the same OIDC
# token to a provenance attestation on each tarball.
# The trailing slash on $package/ changes it to publishing the directory.
# `pnpm-pacquet` is the `@pnpm/pacquet` scoped alias mirror —
# same shim and same `@pacquet/<target>` optional deps as the
# unscoped `pacquet`. Published alongside so users can adopt
# either name; the per-target binary sub-packages stay under
# the `@pacquet/` scope (no need to mirror those).
- name: Publish npm packages
# Auth via npm trusted publishing (the `id-token: write` OIDC token), so
# no NPM_TOKEN; `--provenance` attests each tarball. The trailing slash
# publishes the directory. The glob covers the native sub-packages (build
# dirs `pacquet-*`, published as `@pnpm/exe.<target>`) plus the `pnpm` and
# `@pnpm/exe` (`pnpm-exe`) wrappers.
env:
TAG: ${{ inputs.tag }}
run: |
for package in pacquet/npm/pacquet* pacquet/npm/pnpm-pacquet; do
pnpm publish "$package/" --tag latest --access public --provenance --no-git-checks
for package in pacquet/npm/pacquet-* pacquet/npm/pnpm*; do
pnpm publish "$package/" --tag "$TAG" --access public --provenance --no-git-checks
done