fix(config): restore globalconfig lookup (#12252)

* fix(config): restore globalconfig lookup

* refactor(config): derive globalconfig path from a single shared helper

Extract getGlobalConfigPath into @pnpm/config.reader and use it for both
the reader's warning message and 'config get globalconfig' so the reported
path cannot drift from the file pnpm actually reads. Add an e2e test that
writes a setting to the reported path and reads it back.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
marko1olo
2026-06-07 15:25:52 +04:00
committed by GitHub
parent e42e09dc20
commit 6b5d91a4cc
6 changed files with 50 additions and 4 deletions

View File

@@ -0,0 +1,7 @@
---
"@pnpm/config.commands": patch
"@pnpm/config.reader": patch
"pnpm": patch
---
Fixed `pnpm config get globalconfig` to return the global `config.yaml` path again [pnpm/pnpm#11962](https://github.com/pnpm/pnpm/issues/11962).

View File

@@ -1,4 +1,4 @@
import { isIniConfigKey, types } from '@pnpm/config.reader'
import { getGlobalConfigPath, isIniConfigKey, types } from '@pnpm/config.reader'
import { getObjectValueByPropertyPath } from '@pnpm/object.property-path'
import { isCamelCase } from '@pnpm/text.naming-cases'
import camelcase from 'camelcase'
@@ -35,6 +35,9 @@ function lookupConfig (opts: ConfigCommandOptions, key: string, isScopedKey: boo
}
return { value: opts.authConfig[key] }
}
if (key === 'globalconfig') {
return { value: getGlobalConfigPath(opts.configDir) }
}
const kebabKey = isCamelCase(key) ? kebabCase(key) : key
// Resolve typed keys from Config — check explicitly set values first,
// then fall back to authConfig (for keys like registry set in .npmrc)

View File

@@ -1,3 +1,5 @@
import path from 'node:path'
import { describe, expect, test } from '@jest/globals'
import { config } from '@pnpm/config.commands'
@@ -267,7 +269,18 @@ test('config get with scoped registry key that does not exist', async () => {
expect(getOutputString(getResult)).toBe('undefined')
})
// globalconfig and npm-globalconfig tests removed — pnpm no longer exposes these npm-compat properties
test('config get globalconfig returns the global config.yaml path', async () => {
const configDir = path.join(process.cwd(), 'global-config')
const getResult = await config.handler(createConfigCommandOpts({
dir: process.cwd(),
cliOptions: {},
configDir,
global: true,
authConfig: {},
}), ['get', 'globalconfig'])
expect(getOutputString(getResult)).toBe(path.join(configDir, 'config.yaml'))
})
describe('does not traverse the prototype chain (#10296)', () => {
test.each([

View File

@@ -1,6 +1,12 @@
import os from 'node:os'
import path from 'node:path'
import { GLOBAL_CONFIG_YAML_FILENAME } from '@pnpm/constants'
export function getGlobalConfigPath (configDir: string): string {
return path.join(configDir, GLOBAL_CONFIG_YAML_FILENAME)
}
export function getCacheDir (
opts: {
env: NodeJS.ProcessEnv

View File

@@ -36,7 +36,7 @@ import type {
} from './Config.js'
import { isConfigFileKey } from './configFileKey.js'
import { extractAndRemoveDependencyBuildOptions, hasDependencyBuildOptions } from './dependencyBuildOptions.js'
import { getCacheDir, getConfigDir, getDataDir, getStateDir } from './dirs.js'
import { getCacheDir, getConfigDir, getDataDir, getGlobalConfigPath, getStateDir } from './dirs.js'
import { parseEnvVars } from './env.js'
import { getNetworkConfigs } from './getNetworkConfigs.js'
import { getOptionsFromPnpmSettings } from './getOptionsFromRootManifest.js'
@@ -52,6 +52,7 @@ import { types } from './types.js'
export { types }
export { getDefaultWorkspaceConcurrency, getWorkspaceConcurrency } from './concurrency.js'
export { getGlobalConfigPath } from './dirs.js'
export { getDefaultCreds, getNetworkConfigs, type NetworkConfigs } from './getNetworkConfigs.js'
export { getOptionsFromPnpmSettings, type OptionsFromRootManifest } from './getOptionsFromRootManifest.js'
export type { Creds } from './parseCreds.js'
@@ -305,7 +306,7 @@ export async function getConfig (opts: {
}
}
if (ignoredKeys.length > 0) {
const globalYamlConfigPath = path.join(configDir, GLOBAL_CONFIG_YAML_FILENAME)
const globalYamlConfigPath = getGlobalConfigPath(configDir)
warnings.push(`The following settings cannot be set in the global config file ("${globalYamlConfigPath}") and were ignored: ${ignoredKeys.map(k => `"${k}"`).join(', ')}. Move them to a project-level pnpm-workspace.yaml. To share these settings across projects, use config dependencies: https://pnpm.io/11.x/config-dependencies`)
}
addSettingsFromWorkspaceManifestToConfig(pnpmConfig, {

View File

@@ -276,6 +276,7 @@ test('pnpm config get shows settings from global config.yaml', () => {
expect(configGet('dangerously-allow-all-builds')).toBe('true')
expect(configGet('dlxCacheMaxAge')).toBe('1234')
expect(configGet('dlx-cache-max-age')).toBe('1234')
expect(configGet('globalconfig')).toBe(path.join(configDir, 'config.yaml'))
// doesn't list CLI options
expect(configGet('dev')).toBe('undefined')
@@ -289,3 +290,18 @@ test('pnpm config get shows settings from global config.yaml', () => {
expect(configGet('packageExtensions')).toBe('undefined')
expect(configGet('package-extensions')).toBe('undefined')
})
test('the path from "config get globalconfig" is the file that pnpm actually reads global settings from', () => {
prepare()
const XDG_CONFIG_HOME = path.resolve('.config')
const env = { XDG_CONFIG_HOME }
const configGet = (key: string) =>
execPnpmSync(['config', 'get', key], { expectSuccess: true, env }).stdout.toString().trim()
const globalConfigPath = configGet('globalconfig')
fs.mkdirSync(path.dirname(globalConfigPath), { recursive: true })
fs.writeFileSync(globalConfigPath, 'dlxCacheMaxAge: 4321\n')
expect(configGet('dlx-cache-max-age')).toBe('4321')
})