diff --git a/.changeset/global-yaml-user-prefs.md b/.changeset/global-yaml-user-prefs.md new file mode 100644 index 0000000000..76df43a3d4 --- /dev/null +++ b/.changeset/global-yaml-user-prefs.md @@ -0,0 +1,6 @@ +--- +"@pnpm/config.reader": patch +"pnpm": patch +--- + +Allow user-level preferences in the global `config.yaml`. The following settings can now be set in `~/.config/pnpm/config.yaml` (or via `pnpm config set --location global`) instead of being restricted to `pnpm-workspace.yaml`: `agent`, `globalVirtualStoreDir`, `initPackageManager`, `initType`, `registrySupportsTimeField`, `scriptShell`, `shellEmulator`, `sideEffectsCache`, `sideEffectsCacheReadonly`, `stateDir`, `strictDepBuilds`, `trustPolicy`, `trustPolicyExclude`, `trustPolicyIgnoreAfter`, `updateNotifier`, `useStderr`, `verifyDepsBeforeRun`, `verifyStoreIntegrity`, `virtualStoreDir`, `virtualStoreDirMaxLength` [#11474](https://github.com/pnpm/pnpm/issues/11474). diff --git a/config/reader/src/configFileKey.ts b/config/reader/src/configFileKey.ts index 7500aa4372..a1891e87a9 100644 --- a/config/reader/src/configFileKey.ts +++ b/config/reader/src/configFileKey.ts @@ -8,6 +8,7 @@ type PnpmKey = keyof typeof pnpmTypes * Keys from {@link pnpmTypes} that are valid fields in a global config file. */ export const pnpmConfigFileKeys = [ + 'agent', 'bail', 'ci', 'color', @@ -28,7 +29,10 @@ export const pnpmConfigFileKeys = [ 'global-dir', 'global-path', 'global-pnpmfile', + 'global-virtual-store-dir', 'http-proxy', + 'init-package-manager', + 'init-type', 'optimistic-repeat-install', 'loglevel', 'maxsockets', @@ -47,10 +51,26 @@ export const pnpmConfigFileKeys = [ 'prefer-offline', 'prefer-symlinked-executables', 'block-exotic-subdeps', + 'registry-supports-time-field', 'reporter', 'resolution-mode', + 'script-shell', + 'shell-emulator', + 'side-effects-cache', + 'side-effects-cache-readonly', + 'state-dir', 'store-dir', + 'strict-dep-builds', + 'trust-policy', + 'trust-policy-exclude', + 'trust-policy-ignore-after', + 'update-notifier', 'use-beta-cli', + 'use-stderr', + 'verify-deps-before-run', + 'verify-store-integrity', + 'virtual-store-dir', + 'virtual-store-dir-max-length', ] as const satisfies readonly PnpmKey[] export type PnpmConfigFileKey = typeof pnpmConfigFileKeys[number] @@ -87,8 +107,6 @@ export const excludedPnpmKeys = [ 'ignore-workspace-cycles', 'ignore-workspace-root-check', 'include-workspace-root', - 'init-package-manager', - 'init-type', 'inject-workspace-packages', 'legacy-dir-filtering', 'link-workspace-packages', @@ -104,7 +122,6 @@ export const excludedPnpmKeys = [ 'patches-dir', 'pnpmfile', 'pm-on-fail', - 'agent', 'prefer-workspace-packages', 'preserve-absolute-paths', 'production', @@ -118,28 +135,13 @@ export const excludedPnpmKeys = [ 'save-catalog-name', 'save-peer', 'save-workspace-protocol', - 'script-shell', 'shamefully-hoist', 'shared-workspace-lockfile', - 'shell-emulator', - 'side-effects-cache', - 'side-effects-cache-readonly', 'symlink', 'sort', - 'state-dir', 'stream', - 'strict-dep-builds', 'strict-store-pkg-content-check', 'strict-peer-dependencies', - 'trust-policy', - 'trust-policy-exclude', - 'trust-policy-ignore-after', - 'use-stderr', - 'verify-deps-before-run', - 'verify-store-integrity', - 'global-virtual-store-dir', - 'virtual-store-dir', - 'virtual-store-dir-max-length', 'virtual-store-only', 'peers-suffix-max-length', 'workspace-concurrency', @@ -148,8 +150,6 @@ export const excludedPnpmKeys = [ 'test-pattern', 'changed-files-ignore-pattern', 'embed-readme', - 'update-notifier', - 'registry-supports-time-field', 'fail-if-no-match', 'sync-injected-deps-after-scripts', 'cpu', diff --git a/config/reader/test/index.ts b/config/reader/test/index.ts index b405f13ff1..a4f3ebd6d5 100644 --- a/config/reader/test/index.ts +++ b/config/reader/test/index.ts @@ -1687,6 +1687,55 @@ describe('global config.yaml', () => { expect(config.dangerouslyAllowAllBuilds).toBeDefined() }) + test('reads user-level preference settings from global config.yaml', async () => { + prepareEmpty() + + fs.mkdirSync('.config/pnpm', { recursive: true }) + writeYamlFileSync('.config/pnpm/config.yaml', { + scriptShell: '/usr/local/bin/bash', + shellEmulator: true, + updateNotifier: false, + stateDir: '/custom/state', + trustPolicy: 'no-downgrade', + trustPolicyExclude: ['legacy-pkg'], + registrySupportsTimeField: true, + sideEffectsCache: false, + strictDepBuilds: true, + useStderr: true, + verifyDepsBeforeRun: 'error', + verifyStoreIntegrity: false, + virtualStoreDir: '/custom/.pnpm', + virtualStoreDirMaxLength: 80, + }) + + process.env.XDG_CONFIG_HOME = path.resolve('.config') + + const { config, warnings } = await getConfig({ + cliOptions: {}, + packageManager: { + name: 'pnpm', + version: '1.0.0', + }, + workspaceDir: process.cwd(), + }) + + expect(config.scriptShell).toBe('/usr/local/bin/bash') + expect(config.shellEmulator).toBe(true) + expect(config.updateNotifier).toBe(false) + expect(config.stateDir).toBe('/custom/state') + expect(config.trustPolicy).toBe('no-downgrade') + expect(config.trustPolicyExclude).toEqual(['legacy-pkg']) + expect(config.registrySupportsTimeField).toBe(true) + expect(config.sideEffectsCache).toBe(false) + expect(config.strictDepBuilds).toBe(true) + expect(config.useStderr).toBe(true) + expect(config.verifyDepsBeforeRun).toBe('error') + expect(config.verifyStoreIntegrity).toBe(false) + expect(config.virtualStoreDir).toBe('/custom/.pnpm') + expect(config.virtualStoreDirMaxLength).toBe(80) + expect(warnings.find((w) => w.includes('global config file'))).toBeUndefined() + }) + test('warns when global config.yaml contains settings that are not allowed in the global config', async () => { prepareEmpty()