From 4633a5f49d6b209d952d99da3608cd6a89b4fa7c Mon Sep 17 00:00:00 2001 From: Ismet Togay Date: Wed, 10 Jun 2026 14:22:37 +0300 Subject: [PATCH 1/3] Add pnpm-workspace.yaml to allow esbuild build scripts pnpm 10+ blocks all dependency build scripts by default unless explicitly approved via allowBuilds or onlyBuiltDependencies in pnpm-workspace.yaml. esbuild (transitive dependency of vitest -> vite) has a postinstall script that downloads a platform-specific native binary. Without this config, pnpm install exits non-zero with [ERR_PNPM_IGNORED_BUILDS], breaking any downstream packaging (AUR, Nix, Docker) or local setup using pnpm >=10. Refs: #1195 --- pnpm-workspace.yaml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 pnpm-workspace.yaml diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000000..5ed0b5af0d --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +allowBuilds: + esbuild: true From a6f9013218ce402fda9e11e896653fba0e8fc074 Mon Sep 17 00:00:00 2001 From: Clay Good Date: Tue, 4 Aug 2026 12:24:49 -0500 Subject: [PATCH 2/3] fix(build): declare pnpm workspace root --- pnpm-workspace.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 5ed0b5af0d..7f5dfddca3 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,2 +1,5 @@ +packages: + - '.' + allowBuilds: esbuild: true From 4a32fb440ea0d02c7e9577881b807f0f217c4bf4 Mon Sep 17 00:00:00 2001 From: Clay Good Date: Tue, 4 Aug 2026 18:33:58 -0500 Subject: [PATCH 3/3] fix(build): harden pnpm workspace policies --- .github/workflows/ci.yml | 1 + .github/workflows/security.yml | 1 + flake.nix | 1 + package.json | 3 ++ pnpm-workspace.yaml | 6 ++- test/pnpm-workspace-config.test.ts | 65 ++++++++++++++++++++++++++++++ website/package.json | 3 ++ website/pnpm-workspace.yaml | 11 +++++ 8 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 test/pnpm-workspace-config.test.ts create mode 100644 website/pnpm-workspace.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b780082383..87c167391b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,7 @@ jobs: - 'flake.lock' - 'package.json' - 'pnpm-lock.yaml' + - 'pnpm-workspace.yaml' - 'scripts/update-flake.sh' - '.github/workflows/ci.yml' diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 551ff53189..c59c8c8b93 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -6,6 +6,7 @@ on: paths: - '**/package.json' - '**/pnpm-lock.yaml' + - '**/pnpm-workspace.yaml' - '.github/workflows/security.yml' pull_request: branches: [main] diff --git a/flake.nix b/flake.nix index 1e184ce9f0..007c810832 100644 --- a/flake.nix +++ b/flake.nix @@ -39,6 +39,7 @@ ./test ./package.json ./pnpm-lock.yaml + ./pnpm-workspace.yaml ./tsconfig.json ./build.js ./vitest.config.ts diff --git a/package.json b/package.json index 9bbb05765b..4ea40c6002 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,9 @@ "zod": "^4.4.3" }, "pnpm": { + "onlyBuiltDependencies": [ + "esbuild" + ], "overrides": { "brace-expansion@<=5.0.8": ">=5.0.9 <6", "postcss@<8.5.23": ">=8.5.23 <9" diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 7f5dfddca3..3de39a74fd 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,4 +2,8 @@ packages: - '.' allowBuilds: - esbuild: true + esbuild@0.28.1: true + +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 new file mode 100644 index 0000000000..d8313478e0 --- /dev/null +++ b/test/pnpm-workspace-config.test.ts @@ -0,0 +1,65 @@ +import fs from 'fs'; +import path from 'path'; +import { describe, expect, it } from 'vitest'; +import { parse } from 'yaml'; + +const projectRoot = process.cwd(); + +function readJson(relativePath: string): Record { + return JSON.parse(fs.readFileSync(path.join(projectRoot, relativePath), 'utf8')); +} + +function readYaml(relativePath: string): Record { + return parse(fs.readFileSync(path.join(projectRoot, relativePath), 'utf8')); +} + +describe('pnpm workspace configuration', () => { + it('keeps root build approval and security overrides compatible across pnpm versions', () => { + const packageJson = readJson('package.json'); + const lockfile = readYaml('pnpm-lock.yaml'); + const workspace = readYaml('pnpm-workspace.yaml'); + const esbuildVersions = Object.keys(lockfile.packages) + .filter((key) => key.startsWith('esbuild@')) + .map((key) => key.slice('esbuild@'.length)); + + expect(workspace.packages).toEqual(['.']); + expect(packageJson.pnpm.onlyBuiltDependencies).toEqual(['esbuild']); + expect(esbuildVersions).toHaveLength(1); + expect(workspace.allowBuilds).toEqual({ + [`esbuild@${esbuildVersions[0]}`]: true, + }); + expect(workspace.overrides).toEqual(packageJson.pnpm.overrides); + expect(workspace.overrides).toEqual(lockfile.overrides); + }); + + it('keeps the website as an independently locked project', () => { + const packageJson = readJson('website/package.json'); + const lockfile = readYaml('website/pnpm-lock.yaml'); + const workspace = readYaml('website/pnpm-workspace.yaml'); + const esbuildVersions = Object.keys(lockfile.packages) + .filter((key) => key.startsWith('esbuild@')) + .map((key) => key.slice('esbuild@'.length)); + + expect(workspace.packages).toEqual(['.']); + expect(packageJson.pnpm.onlyBuiltDependencies).toEqual(['esbuild']); + expect(esbuildVersions).toHaveLength(1); + expect(workspace.allowBuilds).toEqual({ + [`esbuild@${esbuildVersions[0]}`]: true, + }); + expect(workspace.overrides).toEqual(packageJson.pnpm.overrides); + expect(workspace.overrides).toEqual(lockfile.overrides); + }); + + it('includes install policy changes in Nix and security validation', () => { + const flake = fs.readFileSync(path.join(projectRoot, 'flake.nix'), 'utf8'); + const ci = fs.readFileSync(path.join(projectRoot, '.github/workflows/ci.yml'), 'utf8'); + const security = fs.readFileSync( + path.join(projectRoot, '.github/workflows/security.yml'), + 'utf8' + ); + + expect(flake).toContain('./pnpm-workspace.yaml'); + expect(ci).toContain("- 'pnpm-workspace.yaml'"); + expect(security).toContain("- '**/pnpm-workspace.yaml'"); + }); +}); diff --git a/website/package.json b/website/package.json index 0f74c95ebc..4b05a82a8d 100644 --- a/website/package.json +++ b/website/package.json @@ -34,6 +34,9 @@ "typescript": "^6.0.3" }, "pnpm": { + "onlyBuiltDependencies": [ + "esbuild" + ], "overrides": { "postcss": "^8.5.22", "sharp": "^0.35.3", diff --git a/website/pnpm-workspace.yaml b/website/pnpm-workspace.yaml new file mode 100644 index 0000000000..b82385fd53 --- /dev/null +++ b/website/pnpm-workspace.yaml @@ -0,0 +1,11 @@ +packages: + - '.' + +allowBuilds: + esbuild@0.28.1: true + +overrides: + postcss: ^8.5.22 + sharp: ^0.35.3 + brace-expansion@<=5.0.8: '>=5.0.9 <6' + fast-uri@<3.1.5: ^3.1.5