Skip to content

chore: migrate from npm to pnpm 10#659

Open
B4nan wants to merge 4 commits into
masterfrom
chore/migrate-to-pnpm
Open

chore: migrate from npm to pnpm 10#659
B4nan wants to merge 4 commits into
masterfrom
chore/migrate-to-pnpm

Conversation

@B4nan
Copy link
Copy Markdown
Member

@B4nan B4nan commented May 19, 2026

Closes #658.

The driver is pnpm-workspace.yaml's minimumReleaseAge: 1440 — a 24-hour quarantine on newly published versions blunting supply-chain attacks. npm has no equivalent; this is why the issue requested the migration.

Two commits, intentionally separated

  1. Migrate to pnpm 11 (b789ce6).
  2. Downgrade to pnpm 10 (3396fdc) — keeps Node 20 in the supported matrix. pnpm 11 requires Node ≥22, which would force-drop Node 20. Kept separate so it can be reverted cleanly the moment we drop Node 20 support.

Lockfile / package manager

  • packageManager: pnpm@10.33.4 + matching devEngines.packageManager with onFail: error.
  • engines.node stays at >=20.11 (matching the previous floor).
  • Commit pnpm-lock.yaml. The previous setup had package-lock.json in .gitignore, so CI was effectively unpinned — every npm install re-resolved deps from the registry. That's exactly the surface this PR closes.
  • prepublishOnly: npm run buildpnpm run build.
  • before-beta-release.cjs: npm showpnpm view. npm CLI is blocked under devEngines onFail: error; pnpm view is functionally equivalent.

Caveat (pnpm 10 + devEngines): pnpm 10 shells out to npm for pnpm config / pnpm version / pnpm pkg, so those subcommands break under onFail: error. We don't invoke any of them in scripts or CI (pnpm publish, pnpm view, pnpm install, pnpm run, pnpm deploy are all native and safe), so the guard stays useful. This caveat goes away on pnpm 11.

pnpm-workspace.yaml

  • minimumReleaseAge: 1440 (24 h) with @apify/* excluded.
  • allowBuilds: puppeteer (Chromium download, required by e2e), esbuild (platform binary, required by tsx); core-js denied (postinstall is just a donation banner).
  • strictDepBuilds: false so a new build-script transitive doesn't fail --frozen-lockfile installs.

CI (check.yaml, release.yaml)

  • Every install step collapses to:
    - uses: apify/actions/pnpm-install@v1.1.2
  • Test matrix stays [20, 22, 24]. Node 26 is intentionally NOT added: a pre-existing tsx + yargs (16.x CJS bin) + Node 26 ESM-loader incompatibility breaks mocha under the repo's node-option: ["import=tsx"] config (reproducible on master with plain npm install, so not a migration regression — fix belongs in a separate change).
  • release.yaml: npm publishpnpm publish --tag <…> --no-git-checks. npm publish would be rejected under devEngines onFail: error; pnpm publish honours the dist-tag and inherits OIDC auth from setup-node's registry-url.

Notes for review

  • The github-registry-token input on apify/actions/pnpm-install is not passed — this repo has no @apify-packages/* deps (only public-registry packages).
  • The previous npm-based setup never committed a lockfile, so --frozen-lockfile-style strictness is new here. Renovate / dependency updates will start writing to pnpm-lock.yaml going forward.
  • Verified locally: pnpm install clean on both Node 20 and Node 24; pnpm run build, pnpm run lint, pnpm run test:unit (4 passing) all green.

🤖 Generated with Claude Code

Closes #658. The driver is `pnpm-workspace.yaml`'s `minimumReleaseAge: 1440`
— a 24-hour quarantine on newly published versions blunting supply-chain
attacks. npm has no equivalent; this is why the issue requested the migration.

## Lockfile / package manager
- `packageManager: pnpm@11.1.3` + matching `devEngines.packageManager`
  (`onFail: error` — pnpm v11 reimplemented `config / version / pkg` as native
  subcommands, so `error` is safe).
- Bump `engines.node` `>=20.11` → `>=22` (pnpm 11 requires Node 22+).
- Commit `pnpm-lock.yaml` (the previous setup had `package-lock.json`
  gitignored, so CI was effectively unpinned — every run re-resolved deps
  from the registry, exactly the surface this PR closes).
- `prepublishOnly`: `npm run build` → `pnpm run build`.
- `before-beta-release.cjs`: `npm show` → `pnpm view` (npm CLI is blocked
  under devEngines `onFail: error`; `pnpm view` is functionally equivalent).

## `pnpm-workspace.yaml`
- `minimumReleaseAge: 1440` (24 h) with `@apify/*` excluded.
- `allowBuilds`: `puppeteer` (Chromium download, required by e2e), `esbuild`
  (platform binary, required by tsx); `core-js` denied (postinstall is just
  a donation banner).
- `strictDepBuilds: false` so a new build-script transitive doesn't fail
  `--frozen-lockfile` installs.

## CI (`check.yaml`, `release.yaml`)
- Every install step collapses to:
  ```yaml
  - uses: apify/actions/pnpm-install@v1.1.2
  ```
- Test matrix `[20, 22, 24]` → `[22, 24]`. Node 20 drops because pnpm 11
  needs ≥22. Node 26 is intentionally NOT added: a pre-existing tsx + yargs
  (16.x CJS bin) + Node 26 ESM-loader incompatibility breaks `mocha` under
  the repo's `node-option: ["import=tsx"]` config (reproducible on `master`
  with plain `npm install`, so not a migration regression — but the fix
  belongs in a separate change).
- `release.yaml`: `npm publish` → `pnpm publish --tag <…> --no-git-checks`.
  npm publish would be rejected under devEngines `onFail: error`; pnpm publish
  honours the dist-tag and inherits OIDC auth from `setup-node`'s
  `registry-url`.

Verified locally on Node 24: `pnpm install` clean, `pnpm run build`,
`pnpm run lint`, `pnpm run test:unit` (4 passing).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions github-actions Bot added this to the 141st sprint - Tooling team milestone May 19, 2026
@github-actions github-actions Bot added the t-tooling Issues with this label are in the ownership of the tooling team. label May 19, 2026
@B4nan B4nan added the adhoc Ad-hoc unplanned task added during the sprint. label May 19, 2026
Comment thread package.json Outdated
},
"engines": {
"node": ">=20.11"
"node": ">=22"
Copy link
Copy Markdown
Member Author

@B4nan B4nan May 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see we kept compact with node 20 in the just-released major version, so I guess we'll need to go back to pnpm v10 (v11 requires node 22)...

(or we just remove the CI checks and keep pnpm v11)

B4nan and others added 3 commits May 19, 2026 17:55
pnpm 11 requires Node ≥22, which would force-drop Node 20 from the supported
matrix. Staying on Node 20 means staying on pnpm 10.

- `packageManager`: `pnpm@11.1.3` → `pnpm@10.33.4`
- `devEngines.packageManager.version`: same
- `engines.node`: `>=22` → `>=20.11` (restoring the previous floor)
- Test matrix: `[22, 24]` → `[20, 22, 24]`
- `pnpm-lock.yaml` regenerated under pnpm 10

Note: pnpm 10 shells out to npm for `pnpm config` / `pnpm version` / `pnpm pkg`,
so those break under `devEngines onFail: error`. We don't invoke any of them
in scripts or CI (`pnpm publish`, `pnpm view`, `pnpm install`, `pnpm run`,
`pnpm deploy` are all native and safe), so the guard stays useful.

Kept as a separate commit so it can be reverted cleanly the moment we drop
Node 20 support and move to pnpm 11.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
A pass through the comments added in this PR caught three claims that
sounded plausible but don't survive testing on pnpm 10.33.4:

- `release.yaml`: the rationale for `pnpm publish` said "`npm publish` would
  be rejected" under `devEngines.onFail: error`. It isn't — only `pnpm
  config`/`version`/`pkg` shell-outs break. Reframed the comment around the
  real reason: toolchain consistency, plus `--no-git-checks` for the
  detached-HEAD release-tag checkout.
- `before-beta-release.cjs`: same pattern. `npm view` is not blocked by
  devEngines either. `pnpm view` substitution stays (toolchain consistency)
  but the justification is corrected.
- `pnpm-workspace.yaml`: the `strictDepBuilds` comment claimed the default
  is `true` (per pnpm docs). On 10.33.4 it's empirically warn-only. Rewrote
  the comment around the actual failure mode that motivated the explicit
  `false` setting.

Also dropped the "see issue #658" task-reference line from the workspace
header — that context belongs in the PR description, not in code.

No functional changes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
pnpm's content-addressable store runs `postinstall` once when a package is
first fetched, then symlinks on subsequent installs. GH Actions caches the
pnpm store but not puppeteer's separate browser cache (`~/.cache/puppeteer/`).
On warm-cache CI runs — i.e. every run after the first against a given
lockfile hash — pnpm just symlinks from the store and skips `postinstall`,
so the Chromium download never happens. Result: 52 e2e failures with
`Error: Could not find Chromium (rev. 1108766)`.

Triggered immediately on this PR: the third commit (a docs-only change with
the same `pnpm-lock.yaml` hash as the second) hit the cache for the first
time and surfaced the bug.

Fix: run `pnpm rebuild puppeteer` after install in the two jobs that need
the browser. Idempotent — no-op once the binary is in place — and scoped
to the e2e jobs (lint, unit, type-check don't drive puppeteer).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@B4nan B4nan changed the title chore: migrate from npm to pnpm 11 chore: migrate from npm to pnpm 10 May 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

adhoc Ad-hoc unplanned task added during the sprint. t-tooling Issues with this label are in the ownership of the tooling team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate to pnpm

2 participants