Skip to content

chore: upgrade pnpm 9 → 11 with supply-chain protection#235

Merged
cameronapak merged 18 commits into
mainfrom
chore/pnpm-11-upgrade-supply-chain-protection
Jul 15, 2026
Merged

chore: upgrade pnpm 9 → 11 with supply-chain protection#235
cameronapak merged 18 commits into
mainfrom
chore/pnpm-11-upgrade-supply-chain-protection

Conversation

@cameronapak

@cameronapak cameronapak commented May 12, 2026

Copy link
Copy Markdown
Collaborator

RELATES TO https://lifechurch.atlassian.net/browse/YPE-2688

The recent news of seeing more and more supply-chain attacks via npm has caused me to want to make sure we're as secure as can be. That's what this PR does.

Summary

  • Upgrade pnpm 9.0.0 → 11.10.0 with minimumReleaseAge: 4320 (3-day cooldown) to mitigate supply-chain attacks on new package versions
  • Move overrides from package.jsonpnpm-workspace.yaml (pnpm 11 breaking change — overrides in package.json no longer enforce for auto-installed peers)
  • Pin every override to an exact version — vite was the last loose range (>=5.4.217.3.3), which could otherwise resolve a compromised newer release silently
  • Remove version pins from all CI workflows — pnpm/action-setup@v4 now reads from packageManager field (single source of truth)

Changes

File Change
package.json packageManager: "pnpm@11.10.0", engines.pnpm: ">=11.0.0", removed pnpm.overrides, added @internal/eslint-config + eslint-plugin-storybook as root devDeps
pnpm-workspace.yaml Added minimumReleaseAge, exact-pinned overrides (incl. vite: "7.3.3"), allowBuilds (@swc/core: false — tsup uses esbuild, swc native build unneeded)
CONTRIBUTING.md Prerequisites bumped to Node >=22, pnpm >=11 to match engines
.github/workflows/ci.yml Removed 4x version: 9.0.0 pins
.github/workflows/release.yml Removed version: 9.0.0 pin
.github/workflows/storybook.yml Removed version: 9.0.0 pin
AGENTS.md Updated pnpm version refs, added supply-chain protection docs

pnpm 11 breaking changes handled

  • Overrides must live in pnpm-workspace.yaml (not package.json) to enforce for auto-installed peers
  • Build scripts require allowBuilds approval (esbuild, @parcel/watcher, msw)
  • Workspace packages not hoisted to root — @internal/eslint-config and eslint-plugin-storybook must be root devDependencies
  • minimumReleaseAge blocks packages published < 3 days ago; override with --force if needed urgently

Verification

  • pnpm lint — all 7 packages pass
  • pnpm typecheck — all 6 packages pass
  • pnpm test — 751 tests pass (core: 298, hooks: 275, ui: 178)

Greptile Summary

This PR upgrades the monorepo from pnpm 9 to pnpm 11.10.0 and adds supply-chain protections, handling all pnpm 11 breaking changes along the way.

  • pnpm 11 migration: Overrides moved from package.jsonpnpm-workspace.yaml (required for peer enforcement), .npmrc stripped to auth/registry only, allowBuilds map replaces the legacy onlyBuiltDependencies/neverBuiltDependencies settings, and @internal/eslint-config + eslint-plugin-storybook added as explicit root devDependencies to compensate for the removed workspace hoisting.
  • Supply-chain hardening: minimumReleaseAge: 4320 (3-day cooldown, in minutes) blocks packages published less than 72 hours ago from being resolved; vite override tightened from a loose >=5.4.21 range to the exact 7.3.3; all CI workflows now derive the pnpm version from the packageManager field rather than hardcoded pins.
  • Node / CI bump: engines.node raised to >=22.13.0, Node in all CI jobs bumped to 24, consistent with the documented pnpm 11 minimum and what AGENTS.md/CONTRIBUTING.md now state.

Confidence Score: 5/5

Safe to merge — all pnpm 11 breaking changes are handled correctly, the supply-chain settings are well-configured, and the previous concerns about engines.node floor and minimumReleaseAgeExclude have both been resolved in the final state of the diff.

This is a carefully executed tooling upgrade. The overrides migration, allowBuilds map, .npmrc cleanup, and packageManager hash are all structurally correct per the pnpm 11 docs. The engines.node floor matches AGENTS.md/CONTRIBUTING.md/review-guidelines.md (>=22.13.0). The minimumReleaseAge value of 4320 is in minutes (confirmed), giving the documented 3-day cooldown. All CI workflows consistently derive the pnpm version from the packageManager field. The 751-test suite passing gives further confidence that the esbuild-only tsup path (with @swc/core build suppressed) works correctly.

No files require special attention.

Important Files Changed

Filename Overview
pnpm-workspace.yaml New central config hub for pnpm 11: minimumReleaseAge (4320 min = 3 days), migrated .npmrc hoisting settings, exact-pinned overrides, and allowBuilds map — all correct per pnpm 11 docs
package.json packageManager bumped to pnpm@11.10.0 with SHA-512 integrity hash; engines.node raised to >=22.13.0 matching docs; pnpm.overrides block removed (moved to pnpm-workspace.yaml)
.npmrc Correctly stripped to a comment-only file; all non-auth/registry settings migrated to pnpm-workspace.yaml per pnpm 11 requirements
.github/workflows/ci.yml Dropped hardcoded version: 9.0.0 from pnpm/action-setup@v4 (now reads packageManager field); Node bumped 20→24 across all 4 jobs
.github/workflows/release.yml Removed pnpm version pin; now relies on packageManager field for version resolution
.github/workflows/storybook.yml Removed pnpm version pin and bumped Node 20→24; consistent with other workflow changes
AGENTS.md Updated Node/pnpm prerequisites to 22.13.0/11.0.0; added supply-chain protection docs and pnpm 11 breaking-change notes for agent guidance
pnpm-lock.yaml Lock file regenerated for pnpm 11; vite overridden to exact 7.3.3, @swc/core no longer resolved as a tsup peer (expected — allowBuilds disables its build script)

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[pnpm install] --> B{Lockfile frozen?}
    B -- Yes / CI --> C[Install from lockfile\nno resolution step]
    B -- No / local add --> D{minimumReleaseAge\n4320 min = 3 days}
    D -- Package age < 3 days --> E[❌ Blocked\nuse --force to override]
    D -- Package age ≥ 3 days --> F{In allowBuilds?}
    F -- Not listed --> G[Install, no build script run]
    F -- Listed: true --> H[Install + run build script\ne.g. esbuild, msw, @parcel/watcher]
    F -- Listed: false --> I[Install only\nbuild script suppressed\ne.g. @swc/core]
    C --> J[✅ Packages installed]
    G --> J
    H --> J
    I --> J
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[pnpm install] --> B{Lockfile frozen?}
    B -- Yes / CI --> C[Install from lockfile\nno resolution step]
    B -- No / local add --> D{minimumReleaseAge\n4320 min = 3 days}
    D -- Package age < 3 days --> E[❌ Blocked\nuse --force to override]
    D -- Package age ≥ 3 days --> F{In allowBuilds?}
    F -- Not listed --> G[Install, no build script run]
    F -- Listed: true --> H[Install + run build script\ne.g. esbuild, msw, @parcel/watcher]
    F -- Listed: false --> I[Install only\nbuild script suppressed\ne.g. @swc/core]
    C --> J[✅ Packages installed]
    G --> J
    H --> J
    I --> J
Loading

Comments Outside Diff (1)

  1. pnpm-lock.yaml, line 194-195 (link)

    P2 @swc/core no longer resolved as a tsup peer

    Across every package (root, packages/core, packages/hooks, packages/ui), tsup@8.5.0 previously resolved with @swc/core@1.13.5 as a satisfied peer; after this upgrade it resolves without it. This means tsup will now use esbuild as its transformer instead of SWC. If any package's tsup.config.ts explicitly sets esbuildOptions or SWC-specific options, behaviour changes silently. The test suite passing is reassuring, but it's worth confirming no tsup.config references experimentalDts or similar options that behaved differently under SWC.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: pnpm-lock.yaml
    Line: 194-195
    
    Comment:
    **`@swc/core` no longer resolved as a `tsup` peer**
    
    Across every package (`root`, `packages/core`, `packages/hooks`, `packages/ui`), `tsup@8.5.0` previously resolved with `@swc/core@1.13.5` as a satisfied peer; after this upgrade it resolves without it. This means tsup will now use esbuild as its transformer instead of SWC. If any package's `tsup.config.ts` explicitly sets esbuildOptions or SWC-specific options, behaviour changes silently. The test suite passing is reassuring, but it's worth confirming no `tsup.config` references `experimentalDts` or similar options that behaved differently under SWC.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code Fix in Cursor

Reviews (17): Last reviewed commit: "docs: state Node 22.13 minimum with Node..." | Re-trigger Greptile

- Upgrade pnpm 9.0.0 → 11.1.1 (packageManager, engines, corepack)
- Add minimumReleaseAge: 4320 (3-day cooldown) to pnpm-workspace.yaml
- Move overrides from package.json → pnpm-workspace.yaml (pnpm 11 requirement)
- Add @internal/eslint-config and eslint-plugin-storybook as root devDeps
- Add allowBuilds for esbuild, @parcel/watcher, msw
- Remove version pins from CI workflows (reads from packageManager field)
- Update AGENTS.md with pnpm 11 refs and supply-chain docs
@changeset-bot

changeset-bot Bot commented May 12, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: e96ac7e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Comment thread pnpm-workspace.yaml Outdated
@cameronapak
cameronapak marked this pull request as draft May 12, 2026 15:02
- Bump node-version from 20 → 22 in ci.yml and storybook.yml (pnpm 11 requires Node >= 22.13)
- Bump engines.node from >=20 → >=22 in package.json
- Remove minimumReleaseAgeExclude — workspace packages bypass the gate inherently
- Update AGENTS.md Node requirement references
@cameronapak
cameronapak marked this pull request as ready for review May 12, 2026 15:26
@cameronapak

Copy link
Copy Markdown
Collaborator Author

Hey @jhampton, Can I get your feedback on this PR? Mainly around the idea of it. I just want to make sure that with the rise of supply chain attacks, that we are protected on our repos. So I just want to share this with you and let me know so that I can hear your feedback (any feedback)

cameronapak and others added 4 commits June 3, 2026 12:40
Resolve pnpm-lock.yaml conflict by regenerating against main (YPE-642
verse action popover) with pnpm 11.10.0.

Supply-chain hardening follow-ups:
- pnpm 11.1.1 -> 11.10.0 (latest stable 11.x; corepack-written hash)
- vite override ">=5.4.21" -> exact "7.3.3" (closes the last loose
  version range; a >= let a bad newer release resolve silently)
- allowBuilds: '@swc/core': false (tsup uses esbuild by default; no
  tsup.config requests swc, so its native postinstall is unneeded)
- CONTRIBUTING.md prerequisites: Node >=22, pnpm >=11 (match engines)

Verified: pnpm lint (7/7), typecheck (6/6), test (751 pass: core 298,
hooks 275, ui 178), build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cameronapak

cameronapak commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator Author

CLAUDITHAN SAYS

Re: the Greptile note that @swc/core is "no longer resolved as a tsup peer" (pnpm-lock.yaml 194-195) — Cam asked me to verify it. It doesn't hold, on two counts:

1. Stale premise. After the lockfile regen on the current head, @swc/core@1.13.5 is resolved as a tsup peer across all four packages:

pnpm-lock.yaml:59,175,221,366
  tsup@8.5.0(...)(@swc/core@1.13.5(@swc/helpers@0.5.17))(...)

Greptile reviewed an earlier commit where swc had transiently dropped out; the regen restored it, so "resolves without it" no longer describes this code.

2. Wrong mechanism regardless. tsup always transforms/bundles with esbuild. @swc/core is an optional peer used for exactly one thing — emitting legacy decorator metadata (emitDecoratorMetadata). This repo uses no decorators (grep emitDecoratorMetadata / experimentalDecorators → 0 hits), so swc is never on the code path whether installed or not. The experimentalDts concern also doesn't apply: the tsup config sets dts: false (types come from tsc + API Extractor).

Net: nothing to fix. The green build is expected, not just reassuring — esbuild was always the transformer. (Also FYI: allowBuilds: @swc/core: false only skips the native binary's postinstall; harmless here since nothing invokes swc.)

Comment thread .npmrc Outdated
Dustin-Kelley
Dustin-Kelley previously approved these changes Jul 7, 2026
pnpm 11 reads only auth/registry settings from .npmrc; the
publicHoistPattern / autoInstallPeers / strictPeerDependencies lines
were silently ignored. Verified publicHoistPattern default is [] under
pnpm 11, so eslint/prettier/@types/* were no longer hoisted to root.

Migrate them to pnpm-workspace.yaml to preserve the repo's intended
hoisting. Confirmed via `pnpm install --force`: full @types/* set is
hoisted to root node_modules again (was just @types/node before).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread package.json Outdated
cameronapak and others added 2 commits July 13, 2026 15:33
pnpm 11.x declares engines.node >=22.13 in its own package.json
(verified against the npm registry). Aligns engines with the docs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@bmanquen bmanquen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just a couple of questions for clarity, both non blocking.

Comment thread .github/workflows/ci.yml
Comment thread pnpm-workspace.yaml
Comment thread pnpm-workspace.yaml
cameronapak and others added 2 commits July 15, 2026 15:01
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cameronapak

Copy link
Copy Markdown
Collaborator Author

cameronapak and others added 3 commits July 15, 2026 15:25
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pers

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…est/jsdom test envs

The SDK itself is hardened for Node 26 (web-storage guards, in-memory
installationId fallback), but test files and the vitest/jsdom toolchain
still assume a working bare localStorage global. Revisit 26 when the
ecosystem catches up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reverts ddbaed3, 39e3f9a, 44d5a04 to keep this PR scoped to the pnpm 11
upgrade. The hardening work is preserved on fix/ui-localstorage-node26-guard
for a future PR (consumers on Node 26 will still hit the strict
localStorage behavior).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dustin-Kelley
Dustin-Kelley previously approved these changes Jul 15, 2026

@Dustin-Kelley Dustin-Kelley left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM!

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cameronapak
cameronapak merged commit 2381a16 into main Jul 15, 2026
6 checks passed
@cameronapak
cameronapak deleted the chore/pnpm-11-upgrade-supply-chain-protection branch July 15, 2026 21:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants