Skip to content

Adopt pnpm supply-chain policy (recipe A) - #13

Merged
pyramation merged 1 commit into
mainfrom
feat/pnpm-policy
Aug 6, 2026
Merged

Adopt pnpm supply-chain policy (recipe A)#13
pyramation merged 1 commit into
mainfrom
feat/pnpm-policy

Conversation

@pyramation

Copy link
Copy Markdown
Contributor

What changed

Adopts the org's pnpm supply-chain policy (recipe A) in this workspace, via pnpm-policy.

  • Added pnpm-policy.yaml at the workspace root: 2-day minimumReleaseAge for third-party packages (a compromised release is normally reported and yanked within hours, so a short wait catches it without stalling upgrades), blockExoticSubdeps: true (this repo's lockfile resolves nothing from git/URL — verified), maintainers: [pyramation], and our owned scopes (@constructive-io, @constructive-db, @launchql, @pgpm, @pgpmjs, @pgsql).
  • Added @constructive-io/pnpm-policy (pinned exact at 0.2.1, the currently published version — not ^0.2.1) and pnpm-policy as root devDependencies.
  • Added root scripts policy (regenerate) and policy:check (drift check), both using --builds-key onlyBuiltDependencies.
  • Regenerated pnpm-workspace.yaml's managed block (minimumReleaseAge, minimumReleaseAgeExclude, blockExoticSubdeps, onlyBuiltDependencies).
  • Wired pnpm run policy:check into CI (.github/workflows/ci.yml, build job) immediately after the existing pnpm install --frozen-lockfile step.

allowBuilds

CI pins pnpm 10.12.2 (below the 10.16 cutoff where the allowBuilds key was introduced), so this uses onlyBuiltDependencies instead, per the tool's --builds-key flag. Packages allowed to run install scripts, with reasons:

  • nx — postinstall (bin/post-install) downloads/links the native platform binary that powers Nx's task graph runner; without it the install is broken.
  • unrs-resolver — postinstall runs napi-postinstall to fetch the native (napi-rs) resolver binary used by eslint's TypeScript import resolver during lint.

Both were flagged by pnpm's ERR_PNPM_IGNORED_BUILDS after pnpm add; verified by inspecting each package's package.json scripts.postinstall.

Version pin

@constructive-io/pnpm-policy pinned exact to 0.2.1 (currently published version), not caret.

CI

Wired into the existing build job in .github/workflows/ci.yml (no separate lint workflow exists) — added a Check supply-chain policy step running pnpm run policy:check right after Install dependencies.

Verification (all run locally against pnpm 10.12.2, matching what CI pins — the repo has no packageManager field, so CI's pin is the canonical version; my locally installed pnpm is 11.11.0, which behaves differently for build approvals, see deviation below)

  • pnpm run policy:checkpnpm-workspace.yaml matches the policy
  • pnpm install --frozen-lockfile → succeeds
  • pnpm run policy run twice → pnpm-workspace.yaml shasum identical before/after (deterministic), second run reported Unchanged

Deviations / notes

  • pnpm-lock.yaml shows a large diff beyond the two new devDependencies. This is YAML formatting churn (double- vs single-quoted keys) from being regenerated under pnpm 10.12.2 rather than whatever pnpm version last touched the lockfile — not a content/resolution change. Ran all verification against 10.12.2 since that's what CI pins.
  • Locally-installed pnpm (11.11.0, newer than CI's pin) has a different, more aggressive auto-approval flow for ignored builds — it auto-writes a placeholder allowBuilds: {pkg: "set this to true or false"} block into pnpm-workspace.yaml outside pnpm-policy's managed section whenever it detects unapproved build scripts, even when onlyBuiltDependencies already lists them. This is pnpm 11's own behavior, not pnpm-policy's; I removed the stray block each time and verified everything with pnpm 10.12.2 (CI's pinned version) instead, where it does not occur.
  • No packageManager field exists in package.json; CI's pnpm/action-setup pin (10.12.2) is the closest thing to a canonical version, so onlyBuiltDependencies was the correct choice per the policy's own guidance.

Part of constructive-io/constructive-planning#1464. Not merging — for review only.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CEswUi4ANuB58rva48aHge

Add pnpm-policy to gate installs: third-party releases must be 2 days
old before pnpm will install them, since a compromised release is
normally reported and yanked well within that window. Our own
packages (the @constructive-io/@constructive-db/@launchql/@pgpm/
@pgpmjs/@pgsql scopes, published under pyramation) skip the wait —
waiting on our own release protects nothing.

The exempt package/scope list in pnpm-workspace.yaml is generated
from pnpm-policy's inventory of what pyramation actually publishes,
not hand-maintained, so it can't silently drift out of date. Run
`pnpm run policy` after editing pnpm-policy.yaml, `pnpm run
policy:check` to verify no drift (now wired into CI).

allowBuilds is expressed via `onlyBuiltDependencies` (not the newer
`allowBuilds` map) because CI pins pnpm 10.12.2, older than the 10.16
cutoff where `allowBuilds` was introduced. nx and unrs-resolver are
the only packages allowed to run install scripts, both for native
binary postinstall steps.

Part of constructive-io/constructive-planning#1464.
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​constructive-io/​pnpm-policy@​0.2.17610010091100
Addedpnpm-policy@​0.2.27610010090100

View full report

@pyramation
pyramation merged commit 7bbb0a1 into main Aug 6, 2026
7 checks passed
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.

1 participant