mirror of
https://github.com/pnpm/pnpm.git
synced 2026-05-12 01:54:53 -04:00
fix(config): allow user-level preferences in global config.yaml (#11477)
Moves 20 user-level preference settings from the workspace-only exclusion list into the global config allowlist (`config/reader/src/configFileKey.ts`): - Shell / scripts: `scriptShell`, `shellEmulator` - Notifications & UI: `updateNotifier`, `useStderr` - Trust policy (already DLX-inherited as user-level posture): `trustPolicy`, `trustPolicyExclude`, `trustPolicyIgnoreAfter` - Store / virtual store: `globalVirtualStoreDir`, `virtualStoreDir`, `virtualStoreDirMaxLength`, `verifyStoreIntegrity`, `sideEffectsCache`, `sideEffectsCacheReadonly` - Build / dep verification: `strictDepBuilds`, `verifyDepsBeforeRun` - Misc personal/system prefs: `stateDir`, `registrySupportsTimeField`, `initPackageManager`, `initType`, `agent` These are personal/system preferences rather than workspace structure. In v10 they could be set in `~/.npmrc`. v11 silently dropped them from both `~/.npmrc` and the new global `config.yaml`, leaving `pnpm-workspace.yaml` as the only working location — which the issue author rightly points out is impractical for system-level defaults like `scriptShell`. After this change: - Settings in `~/.config/pnpm/config.yaml` are applied instead of being filtered out by `isConfigFileKey` (`config/reader/src/index.ts:296`). - `pnpm config set --location global scriptShell <path>` succeeds instead of throwing `ConfigSetUnsupportedYamlConfigKeyError` (same predicate used in `config/commands/src/configSet.ts:237`). `pmOnFail` and `runtimeOnFail` are intentionally left workspace-only because they would cause lockfile divergence between contributors when set globally. `~/.npmrc` support for non-auth/non-network keys is also intentionally not restored — the team has moved those settings to YAML config. Closes #11474.
This commit is contained in:
6
.changeset/global-yaml-user-prefs.md
Normal file
6
.changeset/global-yaml-user-prefs.md
Normal file
@@ -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).
|
||||
@@ -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',
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user