From 6545793845b3a780d40dccdf416cf005ceaf78bb Mon Sep 17 00:00:00 2001 From: Nightt <87569709+nightt5879@users.noreply.github.com> Date: Sat, 20 Jun 2026 18:39:15 +0800 Subject: [PATCH] fix: avoid updating allowBuilds when workspace is ignored (#12488) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pnpm install --ignore-workspace auto-populated ignored builds into the allowBuilds map of pnpm-workspace.yaml, overwriting committed true/false values with the "set this to true or false" placeholder — even under --frozen-lockfile, which must stay read-only. The ignore-workspace CLI flag is now a first-class Config field (ignoreWorkspace, mirroring ignoreWorkspaceCycles) instead of being read from the untyped cliOptions bag. handleIgnoredBuilds reads it directly and skips writing to allowBuilds when the workspace is ignored. The strict ignored-build failure is unchanged. Closes pnpm/pnpm#12469 --------- Co-authored-by: Zoltan Kochan --- .changeset/ignore-workspace-allow-builds.md | 6 ++++ config/reader/src/Config.ts | 1 + .../commands/src/handleIgnoredBuilds.ts | 5 ++- installing/commands/src/installDeps.ts | 1 + .../commands/test/handleIgnoredBuilds.ts | 31 +++++++++++++++++++ pnpm/test/install/lifecycleScripts.ts | 23 ++++++++++++++ 6 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 .changeset/ignore-workspace-allow-builds.md create mode 100644 installing/commands/test/handleIgnoredBuilds.ts diff --git a/.changeset/ignore-workspace-allow-builds.md b/.changeset/ignore-workspace-allow-builds.md new file mode 100644 index 0000000000..0be6f9a06e --- /dev/null +++ b/.changeset/ignore-workspace-allow-builds.md @@ -0,0 +1,6 @@ +--- +"@pnpm/installing.commands": patch +"pnpm": patch +--- + +Fixed `pnpm install --ignore-workspace` overwriting the `allowBuilds` map in `pnpm-workspace.yaml`. The ignored builds of a package with a build script were auto-populated into `allowBuilds` even though `--ignore-workspace` was passed, clobbering committed `true`/`false` values with the `set this to true or false` placeholder [#12469](https://github.com/pnpm/pnpm/issues/12469). diff --git a/config/reader/src/Config.ts b/config/reader/src/Config.ts index 1b2f818b1e..990fe8d217 100644 --- a/config/reader/src/Config.ts +++ b/config/reader/src/Config.ts @@ -232,6 +232,7 @@ export interface Config extends OptionsFromRootManifest { dedupePeerDependents?: boolean dedupePeers?: boolean patchesDir?: string + ignoreWorkspace?: boolean ignoreWorkspaceCycles?: boolean disallowWorkspaceCycles?: boolean packGzipLevel?: number diff --git a/installing/commands/src/handleIgnoredBuilds.ts b/installing/commands/src/handleIgnoredBuilds.ts index 3ca11963fc..207d4442b6 100644 --- a/installing/commands/src/handleIgnoredBuilds.ts +++ b/installing/commands/src/handleIgnoredBuilds.ts @@ -8,6 +8,7 @@ import { lexCompare } from '@pnpm/util.lex-comparator' export interface HandleIgnoredBuildsOpts { allowBuilds?: Record + ignoreWorkspace?: boolean rootProjectManifestDir?: string workspaceDir?: string strictDepBuilds?: boolean @@ -18,7 +19,9 @@ export async function handleIgnoredBuilds ( ignoredBuilds: IgnoredBuilds | undefined ): Promise { if (!ignoredBuilds?.size) return - await writeIgnoredBuildsToAllowBuilds(opts, ignoredBuilds) + if (!opts.ignoreWorkspace) { + await writeIgnoredBuildsToAllowBuilds(opts, ignoredBuilds) + } if (opts.strictDepBuilds) { throw new IgnoredBuildsError(ignoredBuilds) } diff --git a/installing/commands/src/installDeps.ts b/installing/commands/src/installDeps.ts index 0dea29ce91..0dfa07f519 100644 --- a/installing/commands/src/installDeps.ts +++ b/installing/commands/src/installDeps.ts @@ -117,6 +117,7 @@ export type InstallDepsOptions = Pick { + prepareEmpty() + + const workspaceManifestFile = path.resolve('pnpm-workspace.yaml') + const workspaceManifest = { + allowBuilds: { + esbuild: false, + }, + } + writeYamlFileSync(workspaceManifestFile, workspaceManifest) + const workspaceManifestBefore = fs.readFileSync(workspaceManifestFile, 'utf8') + + await handleIgnoredBuilds({ + ignoreWorkspace: true, + rootProjectManifestDir: process.cwd(), + }, new Set(['esbuild@0.25.0' as DepPath])) + + expect(fs.readFileSync(workspaceManifestFile, 'utf8')).toBe(workspaceManifestBefore) + expect(readYamlFileSync(workspaceManifestFile)).toStrictEqual(workspaceManifest) +}) diff --git a/pnpm/test/install/lifecycleScripts.ts b/pnpm/test/install/lifecycleScripts.ts index 928c57e841..af225406ac 100644 --- a/pnpm/test/install/lifecycleScripts.ts +++ b/pnpm/test/install/lifecycleScripts.ts @@ -333,6 +333,29 @@ test('auto-populated placeholders are merged with existing allowBuilds', async ( expect(manifest?.allowBuilds?.['@pnpm.e2e/pre-and-postinstall-scripts-example']).toBe('set this to true or false') }) +test('install --ignore-workspace does not overwrite allowBuilds in pnpm-workspace.yaml', () => { + prepare({ + dependencies: { + '@pnpm.e2e/pre-and-postinstall-scripts-example': '1.0.0', + }, + }) + writeYamlFileSync('pnpm-workspace.yaml', { + allowBuilds: { + '@pnpm.e2e/pre-and-postinstall-scripts-example': false, + }, + }) + const manifestBefore = fs.readFileSync('pnpm-workspace.yaml', 'utf8') + + const { status, stdout, stderr } = execPnpmSync(['install', '--ignore-workspace']) + + // The build is ignored (--ignore-workspace skips the allowBuilds entry), so the + // install ends in ERR_PNPM_IGNORED_BUILDS — the same code path that would have + // written the placeholder. The manifest must stay untouched regardless. + expect(status).toBe(1) + expect(`${stdout}${stderr}`).toContain('ERR_PNPM_IGNORED_BUILDS') + expect(fs.readFileSync('pnpm-workspace.yaml', 'utf8')).toBe(manifestBefore) +}) + test('selective rebuild preserves ignoredBuilds for packages not being rebuilt', async () => { const project = prepare({}) writeYamlFileSync('pnpm-workspace.yaml', {