diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 860d4401..3ffee2ab 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,10 +1,14 @@ version: 2 # Dependabot does not manage two dependency surfaces in this repo: -# 1. pnpm `overrides` (pnpm-workspace.yaml + package.json, root and website) — -# transitive version pins that remediate advisories Dependabot can't otherwise -# reach. It never bumps or removes these; each carries an inline advisory -# comment noting the removal condition (see pnpm-workspace.yaml). +# 1. pnpm `overrides` (pnpm-workspace.yaml, root and website) — transitive +# version pins that remediate advisories Dependabot can't otherwise reach. +# It never bumps or removes these; each carries an inline advisory comment +# noting the removal condition (see pnpm-workspace.yaml). They live in +# pnpm-workspace.yaml only, because Dependabot *does* rewrite a plain-name +# override (`postcss: ^8.5.26`) mirrored under package.json's `pnpm.overrides` +# — and that block replaces the workspace list rather than merging with it, +# so the mirror displaces the real pins. See #1812. # 2. The Nix flake (flake.nix / flake.lock). Update nixpkgs manually with # `nix flake update`; there is no Dependabot ecosystem for Nix. Note that the # pnpmDeps FOD hash in flake.nix must be regenerated on any root lockfile change. diff --git a/package.json b/package.json index ec0e3af2..8ce0f5cd 100644 --- a/package.json +++ b/package.json @@ -85,13 +85,6 @@ "pnpm": { "onlyBuiltDependencies": [ "esbuild" - ], - "overrides": { - "brace-expansion@<=5.0.8": ">=5.0.9 <6", - "postcss@<8.5.23": ">=8.5.23 <9", - "js-yaml@>=3.0.0 <3.15.1": ">=3.15.1 <4", - "js-yaml@>=4.0.0 <4.3.1": ">=4.3.1 <5", - "nanoid@<3.3.17": ">=3.3.17 <4" - } + ] } } diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 2ad1b4b8..dcd1ab46 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,6 +4,11 @@ packages: allowBuilds: esbuild@0.28.1: true +# The only declaration of these. A `pnpm.overrides` block in package.json does not +# merge with this list — pnpm 10 uses it *instead of* this file (verified: a lone +# entry there produced a lockfile with only that override). Dependabot rewrites +# plain-name entries in package.json when it bumps the same package, so a mirrored +# copy there both drifts and silently takes precedence over these advisory pins. overrides: brace-expansion@<=5.0.8: '>=5.0.9 <6' postcss@<8.5.23: '>=8.5.23 <9' diff --git a/test/pnpm-workspace-config.test.ts b/test/pnpm-workspace-config.test.ts index d8313478..f6507d8c 100644 --- a/test/pnpm-workspace-config.test.ts +++ b/test/pnpm-workspace-config.test.ts @@ -14,7 +14,7 @@ function readYaml(relativePath: string): Record { } describe('pnpm workspace configuration', () => { - it('keeps root build approval and security overrides compatible across pnpm versions', () => { + it('keeps root build approval aligned and security overrides single-sourced', () => { const packageJson = readJson('package.json'); const lockfile = readYaml('pnpm-lock.yaml'); const workspace = readYaml('pnpm-workspace.yaml'); @@ -28,7 +28,11 @@ describe('pnpm workspace configuration', () => { expect(workspace.allowBuilds).toEqual({ [`esbuild@${esbuildVersions[0]}`]: true, }); - expect(workspace.overrides).toEqual(packageJson.pnpm.overrides); + // Overrides are declared once, in pnpm-workspace.yaml. A `pnpm.overrides` block + // in package.json replaces that list rather than merging with it, and Dependabot + // rewrites plain-name entries there when it bumps the same package — so a mirrored + // copy silently displaces the pins that patch advisories. + expect(packageJson.pnpm.overrides).toBeUndefined(); expect(workspace.overrides).toEqual(lockfile.overrides); }); @@ -46,7 +50,8 @@ describe('pnpm workspace configuration', () => { expect(workspace.allowBuilds).toEqual({ [`esbuild@${esbuildVersions[0]}`]: true, }); - expect(workspace.overrides).toEqual(packageJson.pnpm.overrides); + // Single-sourced in website/pnpm-workspace.yaml, for the reason above. + expect(packageJson.pnpm.overrides).toBeUndefined(); expect(workspace.overrides).toEqual(lockfile.overrides); }); diff --git a/website/package.json b/website/package.json index 0f52091e..52d94f46 100644 --- a/website/package.json +++ b/website/package.json @@ -36,13 +36,6 @@ "pnpm": { "onlyBuiltDependencies": [ "esbuild" - ], - "overrides": { - "postcss": "^8.5.28", - "sharp": "^0.35.3", - "brace-expansion@<=5.0.8": ">=5.0.9 <6", - "fast-uri@<3.1.6": "^3.1.6", - "nanoid@<3.3.17": ">=3.3.17 <4" - } + ] } } diff --git a/website/pnpm-workspace.yaml b/website/pnpm-workspace.yaml index 9d26e0ad..e3012e32 100644 --- a/website/pnpm-workspace.yaml +++ b/website/pnpm-workspace.yaml @@ -4,6 +4,11 @@ packages: allowBuilds: esbuild@0.28.2: true +# The only declaration of these. A `pnpm.overrides` block in package.json does not +# merge with this list — pnpm 10 uses it *instead of* this file (verified: a lone +# entry there produced a lockfile with only that override). Dependabot rewrites +# plain-name entries in package.json when it bumps the same package, so a mirrored +# copy there both drifts and silently takes precedence over these advisory pins. overrides: postcss: ^8.5.28 sharp: ^0.35.3