From 1c198e6acf2f8abe42fa563e0b95b4a2fe73eb21 Mon Sep 17 00:00:00 2001 From: Nick Taylor Date: Thu, 20 Aug 2026 16:04:45 -0400 Subject: [PATCH 1/5] ci: standardize on npm 11 with min-release-age gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes a pre-commit CI failure on PR #2350 where npm ci errored with "Missing: unist-util-visit@4.1.2 from lock file" — Node's bundled npm 10.9.4 resolves an optional nested dependency subtree differently than npm 11 (hast-util-to-estree's optional dependency on unist-util-visit/ unist-util-visit-parents), and hard-fails on the gap where npm 11 tolerates it. Also brings this repo in line with the npm 11 + min-release-age supply-chain hardening rolled out to other Pomerium repos (npm 10 silently ignores min-release-age, so npm 11 is required for it to take effect at all): - .npmrc: min-release-age=3 - package.json: engines.npm and packageManager pinned to npm 11.12.1 - pre-commit.yml: installs npm 11.12.1 before npm ci - npm-audit-signatures.yml: new workflow, verifies registry signatures and provenance attestations on package.json/package-lock.json changes Regenerated package-lock.json with npm 11.12.1. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/npm-audit-signatures.yml | 38 ++++++++++++++++++++++ .github/workflows/pre-commit.yml | 9 +++++ .npmrc | 2 ++ package-lock.json | 34 ++----------------- package.json | 4 ++- 5 files changed, 54 insertions(+), 33 deletions(-) create mode 100644 .github/workflows/npm-audit-signatures.yml create mode 100644 .npmrc diff --git a/.github/workflows/npm-audit-signatures.yml b/.github/workflows/npm-audit-signatures.yml new file mode 100644 index 000000000..b0e07e058 --- /dev/null +++ b/.github/workflows/npm-audit-signatures.yml @@ -0,0 +1,38 @@ +name: NPM Audit Signatures +on: + push: + branches: [main] + paths: + - 'package.json' + - 'package-lock.json' + pull_request: + paths: + - 'package.json' + - 'package-lock.json' + +permissions: + contents: read + +jobs: + audit-signatures: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version-file: .tool-versions + + - name: Install npm 11.12.1 (npm 10 ignores min-release-age) + run: | + npm_dir="$(npm root -g)/npm" + rm -rf "$npm_dir" + mkdir -p "$npm_dir" + curl -fsSL "https://registry.npmjs.org/npm/-/npm-11.12.1.tgz" | tar xz --strip-components=1 -C "$npm_dir" + - name: Verify npm version + run: npm --version + + - run: npm ci --ignore-scripts + - run: npm audit signatures --min-release-age=0 diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index eea2f59a2..2486b31a4 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -18,6 +18,15 @@ jobs: with: node-version-file: .tool-versions + - name: Install npm 11.12.1 (npm 10 ignores min-release-age) + run: | + npm_dir="$(npm root -g)/npm" + rm -rf "$npm_dir" + mkdir -p "$npm_dir" + curl -fsSL "https://registry.npmjs.org/npm/-/npm-11.12.1.tgz" | tar xz --strip-components=1 -C "$npm_dir" + - name: Verify npm version + run: npm --version + # CI validates the repo scripts directly so formatting and spelling # always use the repo-root toolchain and config. - run: npm ci diff --git a/.npmrc b/.npmrc new file mode 100644 index 000000000..5b685d797 --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +; npm 11+ required: npm 10 silently ignores min-release-age +min-release-age=3 diff --git a/package-lock.json b/package-lock.json index eea44a42d..2e838f029 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,7 +56,8 @@ "typescript": "^6.0.3" }, "engines": { - "node": ">=22.18.0" + "node": ">=22.18.0", + "npm": ">=11 <12" } }, "node_modules/@11ty/gray-matter": { @@ -14894,37 +14895,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-to-estree/node_modules/unist-util-visit": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", - "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", - "license": "MIT", - "optional": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0", - "unist-util-visit-parents": "^5.1.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-estree/node_modules/unist-util-visit-parents": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", - "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", - "license": "MIT", - "optional": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/hast-util-to-jsx-runtime": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", diff --git a/package.json b/package.json index 72c6cd3f6..bef5dce6b 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,10 @@ "license": "Creative Commons Attribution-NonCommercial 4.0 International", "private": true, "engines": { - "node": ">=22.18.0" + "node": ">=22.18.0", + "npm": ">=11 <12" }, + "packageManager": "npm@11.12.1", "scripts": { "docusaurus": "docusaurus", "lint": "oxlint .", From 665e60b1f69e4a968a46ce98a9645bca4c296c58 Mon Sep 17 00:00:00 2001 From: Nick Taylor Date: Thu, 20 Aug 2026 16:15:38 -0400 Subject: [PATCH 2/5] chore: trigger build From 4ca97460e18962aae7cfd71e2351e5d991dd9a45 Mon Sep 17 00:00:00 2001 From: Nick Taylor Date: Thu, 20 Aug 2026 16:23:16 -0400 Subject: [PATCH 3/5] ci: enable corepack so nested yarn postinstall scripts work npm ci was failing because postman-code-generators (a transitive dep of docusaurus-theme-openapi-docs) shells out to `yarn install` from its postinstall script. Node's bundled corepack shim for yarn refuses to run once package.json declares a "packageManager" field (added in 1c198e6a for npm 11), even though that field only pins npm -- it just needs `corepack enable` to be run first, as its own error message says. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/pre-commit.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 2486b31a4..4de7ff93e 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -18,6 +18,14 @@ jobs: with: node-version-file: .tool-versions + # Without this, Node's bundled (disabled) corepack shim for `yarn` + # refuses to run at all once package.json declares a "packageManager" + # field, even though that field only pins npm. postman-code-generators + # (a transitive dep of docusaurus-theme-openapi-docs) shells out to + # `yarn install` from its postinstall script, so npm ci fails without + # this enabled. + - run: corepack enable + - name: Install npm 11.12.1 (npm 10 ignores min-release-age) run: | npm_dir="$(npm root -g)/npm" From ac90bfbc6f2189833c5cdce3a1fa4b0985439034 Mon Sep 17 00:00:00 2001 From: Nick Taylor Date: Fri, 21 Aug 2026 08:15:39 -0400 Subject: [PATCH 4/5] ci: bump node to 24, drop manual npm 11 install step MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Node 24 bundles npm 11 by default (24.19.0 ships npm 11.17.0), matching the convention already used in pomerium/pomerium. This makes the curl-based "install npm 11.12.1" workaround in pre-commit.yml and npm-audit-signatures.yml unnecessary — it was only needed because Node 22 bundles npm 10, and `corepack enable` does not shim npm on its own (only yarn/pnpm). - .tool-versions: nodejs 22.22.0 -> 24.19.0 - pre-commit.yml, npm-audit-signatures.yml: drop the manual npm install/verify steps, rely on Node's bundled npm - package.json: bump engines.node floor to >=24.0.0 (the version where npm 11 becomes bundled), so it reflects what's actually required to avoid the npm 10 lockfile-resolution gap from #2350 Verified locally under node 24.19.0 (bundled npm 11.17.0): npm ci, npm ci --ignore-scripts, and npm audit signatures --min-release-age=0 all pass. Follow-up to #2351, in response to review feedback questioning why Node wasn't just bumped to get npm 11 natively. AI usage: Claude Code (Sonnet 5) investigated the fix, confirmed against pomerium/pomerium's convention and Node's release metadata, and verified the change locally; changes reviewed by Nick Taylor. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/npm-audit-signatures.yml | 9 --------- .github/workflows/pre-commit.yml | 9 --------- .tool-versions | 2 +- package.json | 2 +- 4 files changed, 2 insertions(+), 20 deletions(-) diff --git a/.github/workflows/npm-audit-signatures.yml b/.github/workflows/npm-audit-signatures.yml index b0e07e058..8d75dc332 100644 --- a/.github/workflows/npm-audit-signatures.yml +++ b/.github/workflows/npm-audit-signatures.yml @@ -25,14 +25,5 @@ jobs: with: node-version-file: .tool-versions - - name: Install npm 11.12.1 (npm 10 ignores min-release-age) - run: | - npm_dir="$(npm root -g)/npm" - rm -rf "$npm_dir" - mkdir -p "$npm_dir" - curl -fsSL "https://registry.npmjs.org/npm/-/npm-11.12.1.tgz" | tar xz --strip-components=1 -C "$npm_dir" - - name: Verify npm version - run: npm --version - - run: npm ci --ignore-scripts - run: npm audit signatures --min-release-age=0 diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 4de7ff93e..c6eeb65a3 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -26,15 +26,6 @@ jobs: # this enabled. - run: corepack enable - - name: Install npm 11.12.1 (npm 10 ignores min-release-age) - run: | - npm_dir="$(npm root -g)/npm" - rm -rf "$npm_dir" - mkdir -p "$npm_dir" - curl -fsSL "https://registry.npmjs.org/npm/-/npm-11.12.1.tgz" | tar xz --strip-components=1 -C "$npm_dir" - - name: Verify npm version - run: npm --version - # CI validates the repo scripts directly so formatting and spelling # always use the repo-root toolchain and config. - run: npm ci diff --git a/.tool-versions b/.tool-versions index a3128f26b..12376e4f0 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -nodejs 22.22.0 +nodejs 24.19.0 diff --git a/package.json b/package.json index bef5dce6b..11c8d6bd3 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "license": "Creative Commons Attribution-NonCommercial 4.0 International", "private": true, "engines": { - "node": ">=22.18.0", + "node": ">=24.0.0", "npm": ">=11 <12" }, "packageManager": "npm@11.12.1", From d99e3103aa4dcbfca748346464d7235a450b317a Mon Sep 17 00:00:00 2001 From: Nick Taylor Date: Fri, 21 Aug 2026 08:26:11 -0400 Subject: [PATCH 5/5] ci: drop packageManager pin and corepack enable step The `packageManager: "npm@11.12.1"` field wasn't doing anything: Corepack only manages npm from it if you run `corepack enable npm` (nothing here does that, only bare `corepack enable`), npm itself doesn't validate its own version against the field, and Netlify's docs confirm it uses this field via Corepack for Yarn/pnpm but not for npm. It was also already stale (11.12.1 vs the 11.17.0 actually bundled with Node 24.19.0) and would keep drifting with every Node point release. Removing it also removes the reason the `corepack enable` step in pre-commit.yml existed: that step was only needed because our own `packageManager` field is what triggered the disabled yarn shim's check when postman-code-generators's postinstall shells out to `yarn install`. With no `packageManager` field declared anywhere in the ancestor chain, the shim doesn't trigger the check. Verified with a clean copy of package.json/package-lock.json under Node 24.19.0, Corepack explicitly disabled: `npm ci` (with scripts) completes successfully, including postman-code-generators's yarn-based postinstall populating its codegens/ folders. Reran against the actual repo tree with the same result. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/pre-commit.yml | 8 -------- package.json | 1 - 2 files changed, 9 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index c6eeb65a3..eea2f59a2 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -18,14 +18,6 @@ jobs: with: node-version-file: .tool-versions - # Without this, Node's bundled (disabled) corepack shim for `yarn` - # refuses to run at all once package.json declares a "packageManager" - # field, even though that field only pins npm. postman-code-generators - # (a transitive dep of docusaurus-theme-openapi-docs) shells out to - # `yarn install` from its postinstall script, so npm ci fails without - # this enabled. - - run: corepack enable - # CI validates the repo scripts directly so formatting and spelling # always use the repo-root toolchain and config. - run: npm ci diff --git a/package.json b/package.json index 11c8d6bd3..51dfc6ded 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,6 @@ "node": ">=24.0.0", "npm": ">=11 <12" }, - "packageManager": "npm@11.12.1", "scripts": { "docusaurus": "docusaurus", "lint": "oxlint .",