Files
pnpm/.github/workflows/release.yml
Zoltan Kochan a4f3c6d3b7 ci: enable manual releases for pacquet and pnpm (#11652)
Two related workflow changes:

### `pacquet-release-to-npm.yml`: switch to `workflow_dispatch`

The trigger was "push to main touching `pacquet/npm/pacquet/package.json`" — the version came from a committed bump and the workflow auto-fired on every such commit. Switch to `workflow_dispatch` only, with a `version` input (validated as semver). The workflow patches `pacquet/npm/pacquet/package.json` before `generate-packages.mjs` runs, so the version is single-sourced from the manual trigger rather than needing a separate commit to bump the manifest first.

The committed manifest now omits the `version` field entirely — it only exists at release time inside the runner.

Dropped along the way:

- The `check` job (EndBug/version-check against unpkg) — no longer needed when the operator types the version.
- The `Create GitHub Release` step — no draft release, no `v*.*.*` git tag. The pacquet `v0.x.x` tag scheme collided with pnpm's `v11.x.x`; npm is the authoritative artifact store and provenance attestations stay attached via `--provenance` on `pnpm publish`.
- `contents: write` on the publish job (no longer needs to create a tag).

### `release.yml`: add `workflow_dispatch` as a lib-only republish path

Add a `workflow_dispatch:` trigger alongside the existing tag-push trigger. Tag-push behaves exactly as before. Manual dispatch becomes a fast **lib-only republish** path — useful after a version bump to one or more lib packages that doesn't warrant a full CLI release.

On `workflow_dispatch` from any ref, the following are skipped (guarded with `if: startsWith(github.ref, 'refs/tags/')`):

- `Publish @pnpm/exe` step — also contains the multi-minute `build-artifacts` call.
- `Publish pnpm CLI` step.
- `Copy Artifacts`, `Attest build provenance` (the `dist/*` attestation), `Generate release description`, `Release` (`softprops/action-gh-release`) — these are the GitHub-Release-side ceremony. Without an explicit `tag_name`, `softprops/action-gh-release@v2.5.0` defaults to `github.ref_name`, which on a manual dispatch from main would create a junk release tagged literally `main`.

What still runs on `workflow_dispatch`:

- `actions/checkout`, garnet scan, `pnpm/setup`
- `Publish internal workspace packages (static token)` — i.e. `pn publish --filter=!pnpm --filter=!@pnpm/exe --access=public --provenance`

Compilation is handled by each lib package's own `prepublishOnly: tsgo --build` hook (which `pnpm publish` runs automatically), same as the existing tag-push flow.

The npm registry rejects any version already on it, so re-running on an already-released tree is a no-op — that's the safety net for accidental clicks.

## How to use

**pacquet release**: Actions → Release Pacquet → Run workflow → fill in `version` (e.g. `0.2.3` or `0.2.3-rc.1`) → Run. No tag, no GitHub release.

**pnpm full release**: still triggered by a `v*.*.*` tag push. Publishes @pnpm/exe + libs + CLI, attests, copies artifacts, creates a draft GitHub release.

**pnpm lib-only republish**: Actions → Release → Run workflow → choose `main` → Run. Publishes just the internal workspace packages from whatever versions are currently in each `package.json`. Skips CLI, @pnpm/exe, build-artifacts, GitHub release.
2026-05-14 22:50:42 +02:00

105 lines
5.5 KiB
YAML

name: Release
on:
push:
tags:
- "v*.*.*"
# Manual trigger for publishing whatever versions are currently in each
# package.json on the selected ref (typically `main`). Useful after a
# version-bump commit that hasn't been tagged yet. Versions already on
# npm are rejected by the registry, so re-running on an already-released
# tree is a no-op rather than a corruption.
workflow_dispatch:
jobs:
release:
permissions:
id-token: write # Required for OIDC
contents: write # for softprops/action-gh-release to create GitHub release
attestations: write # for actions/attest-build-provenance
# Runs on macOS so the darwin artifacts can be ad-hoc signed with native
# `codesign` (no need to build/install `ldid` on the runner) and so
# `verify-binary.mjs` can smoke-test the darwin-arm64 SEA in place — a
# macos-latest runner is Apple Silicon and can execute the arm64 binary.
# Note: this does NOT fix the darwin-x64 crash (nodejs/node#62893) — that's
# an upstream Node.js SEA bug independent of signing; see pack-app docs.
runs-on: macos-latest
environment: release
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: garnet-org/action@9e819143e63d6dda04bca2e90ac85e3cf0e5289d # v2
with:
api_token: ${{ secrets.GARNET_API_TOKEN }}
- name: Install pnpm and Node
uses: pnpm/setup@b1cac37306e39c21283b9dd6cb0ac288fb35ba6b
with:
runtime: node@26.0.0
# The publish phase is split into three sequential steps to control which packages
# use trusted publishing (OIDC) vs. a static token. `pnpm publish` currently bails
# out of OIDC as soon as a static `_authToken` is configured, so the only way to
# force trusted publishing for a given package today is to run its publish in a
# step that doesn't have NPM_TOKEN set. See https://github.com/pnpm/pnpm/pull/11495
# for the longer-term fix that lets OIDC override a configured token.
- name: Publish @pnpm/exe (trusted publishing)
# No NPM_TOKEN: pnpm has no static token to short-circuit on, so it will perform
# the OIDC token exchange against npm's trusted-publishing config for `@pnpm/exe`.
# The exe artifacts must be built before the publish, so they're built here too.
#
# On workflow_dispatch (tagless manual run), skip this step: building the exe
# artifacts takes ~minutes, and the CLI + exe are normally released together
# via a `v*.*.*` tag. Manual dispatch is for lib-only republishes.
if: startsWith(github.ref, 'refs/tags/')
run: |
pn --filter=@pnpm/exe run build-artifacts
pn --filter=@pnpm/exe publish --tag=next-11 --access=public --provenance
- name: Publish internal workspace packages (static token)
# The other workspace packages don't have trusted publishing configured on npm,
# so we still need a static token here. The token is removed from pnpm's config
# at the end of the step so it can't leak into the trusted-publishing step that
# follows (where its presence would silently downgrade `pnpm` to token publishing).
env:
# Setting the "npm_config_//registry.npmjs.org/:_authToken" env variable directly
# doesn't work — pnpm doesn't appear to pass auth tokens to child processes.
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
pn config set "//registry.npmjs.org/:_authToken" "${NPM_TOKEN}"
pn publish --filter=!pnpm --filter=!@pnpm/exe --access=public --provenance
pn config delete "//registry.npmjs.org/:_authToken"
- name: Publish pnpm CLI (trusted publishing)
# No NPM_TOKEN — same rationale as the @pnpm/exe step above. This must come after
# the previous step has cleared its NPM_TOKEN from pnpm's config.
#
# On workflow_dispatch (tagless manual run), skip — see the @pnpm/exe step.
if: startsWith(github.ref, 'refs/tags/')
run: pn publish --filter=pnpm --tag=next-11 --access=public --provenance
# The remaining steps build the GitHub Release (asset upload, body,
# attestation of the dist/* exe files). Skip them on workflow_dispatch:
# there's no version tag to attach the release to, so
# softprops/action-gh-release would default `tag_name` to `github.ref_name`
# (which is `main` for a manual dispatch from main) and produce a junk
# release. The npm publishes above are unaffected — they're the
# authoritative artifact path on manual runs.
- name: Copy Artifacts
if: startsWith(github.ref, 'refs/tags/')
run: pn copy-artifacts
- name: Attest build provenance
if: startsWith(github.ref, 'refs/tags/')
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-path: 'dist/*'
- name: Generate release description
if: startsWith(github.ref, 'refs/tags/')
run: pn make-release-description
- name: Release
if: startsWith(github.ref, 'refs/tags/')
# `gh release create` could replace this, but the release pipeline is
# sensitive and softprops/action-gh-release is widely battle-tested.
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 # zizmor: ignore[superfluous-actions]
with:
draft: true
files: dist/*
body_path: RELEASE.md