1
0
Fork 0

Config: add source option for command to show where a config value is loaded from (#10129)

pull/9261/head
Stephan 2021-11-11 14:17:58 +00:00 committed by GitHub
parent 012556daee
commit 44c5b6cde6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 113 additions and 17 deletions

View File

@ -736,6 +736,8 @@ See the [Config](06-config.md) chapter for valid configuration options.
instead of relative.
* **--json:** JSON decode the setting value, to be used with `extra.*` keys.
* **--merge:** Merge the setting value with the current value, to be used with `extra.*` keys in combination with `--json`.
* **--append:** When adding a repository, append it (lowest priority) to the existing ones instead of prepending it (highest priority).
* **--source:** Display where the config value is loaded from.
### Modifying Repositories

View File

@ -77,6 +77,7 @@ class ConfigCommand extends BaseCommand
new InputOption('json', 'j', InputOption::VALUE_NONE, 'JSON decode the setting value, to be used with extra.* keys'),
new InputOption('merge', 'm', InputOption::VALUE_NONE, 'Merge the setting value with the current value, to be used with extra.* keys in combination with --json'),
new InputOption('append', null, InputOption::VALUE_NONE, 'When adding a repository, append it (lowest priority) to the existing ones instead of prepending it (highest priority)'),
new InputOption('source', null, InputOption::VALUE_NONE, 'Display where the config value is loaded from'),
new InputArgument('setting-key', null, 'Setting key'),
new InputArgument('setting-value', InputArgument::IS_ARRAY, 'Setting value'),
))
@ -234,13 +235,13 @@ EOT
}
if (!$input->getOption('global')) {
$this->config->merge($this->configFile->read());
$this->config->merge(array('config' => $this->authConfigFile->exists() ? $this->authConfigFile->read() : array()));
$this->config->merge($this->configFile->read(), $this->configFile->getPath());
$this->config->merge(array('config' => $this->authConfigFile->exists() ? $this->authConfigFile->read() : array()), $this->authConfigFile->getPath());
}
// List the configuration of the file settings
if ($input->getOption('list')) {
$this->listConfiguration($this->config->all(), $this->config->raw(), $output);
$this->listConfiguration($this->config->all(), $this->config->raw(), $output, null, (bool) $input->getOption('source'));
return 0;
}
@ -305,7 +306,12 @@ EOT
$value = json_encode($value);
}
$this->getIO()->write($value, true, IOInterface::QUIET);
$sourceOfConfigValue = '';
if ($input->getOption('source')) {
$sourceOfConfigValue = ' (' . $this->config->getSourceOfValue($settingKey) . ')';
}
$this->getIO()->write($value . $sourceOfConfigValue, true, IOInterface::QUIET);
return 0;
}
@ -823,10 +829,11 @@ EOT
* @param array<array|bool|string> $contents
* @param array<array|string> $rawContents
* @param string|null $k
* @param bool $showSource
*
* @return void
*/
protected function listConfiguration(array $contents, array $rawContents, OutputInterface $output, $k = null)
protected function listConfiguration(array $contents, array $rawContents, OutputInterface $output, $k = null, $showSource = false)
{
$origK = $k;
$io = $this->getIO();
@ -839,7 +846,7 @@ EOT
if (is_array($value) && (!is_numeric(key($value)) || ($key === 'repositories' && null === $k))) {
$k .= preg_replace('{^config\.}', '', $key . '.');
$this->listConfiguration($value, $rawVal, $output, $k);
$this->listConfiguration($value, $rawVal, $output, $k, $showSource);
$k = $origK;
continue;
@ -857,10 +864,14 @@ EOT
$value = var_export($value, true);
}
$source = '';
if ($showSource) {
$source = ' (' . $this->config->getSourceOfValue($k . $key) . ')';
}
if (is_string($rawVal) && $rawVal != $value) {
$io->write('[<comment>' . $k . $key . '</comment>] <info>' . $rawVal . ' (' . $value . ')</info>', true, IOInterface::QUIET);
$io->write('[<comment>' . $k . $key . '</comment>] <info>' . $rawVal . ' (' . $value . ')</info>' . $source, true, IOInterface::QUIET);
} else {
$io->write('[<comment>' . $k . $key . '</comment>] <info>' . $value . '</info>', true, IOInterface::QUIET);
$io->write('[<comment>' . $k . $key . '</comment>] <info>' . $value . '</info>' . $source, true, IOInterface::QUIET);
}
}
}

View File

@ -354,7 +354,7 @@ EOT
protected function installRootPackage(IOInterface $io, Config $config, $packageName, PlatformRequirementFilterInterface $platformRequirementFilter, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, array $repositories = null, $disablePlugins = false, $noScripts = false, $noProgress = false, $secureHttp = true)
{
if (!$secureHttp) {
$config->merge(array('config' => array('secure-http' => false)));
$config->merge(array('config' => array('secure-http' => false)), Config::SOURCE_COMMAND);
}
$parser = new VersionParser();

View File

@ -90,7 +90,7 @@ EOT
$config = Factory::createConfig();
}
$config->merge(array('config' => array('secure-http' => false)));
$config->merge(array('config' => array('secure-http' => false)), Config::SOURCE_COMMAND);
$config->prohibitUrlByConfig('http://repo.packagist.org', new NullIO);
$this->httpDownloader = Factory::createHttpDownloader($io, $config);

View File

@ -23,6 +23,10 @@ use Composer\Util\ProcessExecutor;
*/
class Config
{
const SOURCE_DEFAULT = 'default';
const SOURCE_COMMAND = 'command';
const SOURCE_UNKNOWN = 'unknown';
const RELATIVE_PATHS = 1;
/** @var array<string, mixed> */
@ -100,6 +104,8 @@ class Config
private $useEnvironment;
/** @var array<string, true> */
private $warnedHosts = array();
/** @var array<string, string> */
private $sourceOfConfigValue = array();
/**
* @param bool $useEnvironment Use COMPOSER_ environment variables to replace config settings
@ -112,6 +118,14 @@ class Config
$this->repositories = static::$defaultRepositories;
$this->useEnvironment = (bool) $useEnvironment;
$this->baseDir = $baseDir;
foreach ($this->config as $configKey => $configValue) {
$this->setSourceOfConfigValue($configValue, $configKey, self::SOURCE_DEFAULT);
}
foreach ($this->repositories as $configKey => $configValue) {
$this->setSourceOfConfigValue($configValue, 'repositories.' . $configKey, self::SOURCE_DEFAULT);
}
}
/**
@ -150,18 +164,21 @@ class Config
* Merges new config values with the existing ones (overriding)
*
* @param array<string, mixed> $config
* @param string $source
*
* @return void
*/
public function merge($config)
public function merge($config, $source = self::SOURCE_UNKNOWN)
{
// override defaults with given config
if (!empty($config['config']) && is_array($config['config'])) {
foreach ($config['config'] as $key => $val) {
if (in_array($key, array('bitbucket-oauth', 'github-oauth', 'gitlab-oauth', 'gitlab-token', 'http-basic', 'bearer')) && isset($this->config[$key])) {
$this->config[$key] = array_merge($this->config[$key], $val);
$this->setSourceOfConfigValue($val, $key, $source);
} elseif (in_array($key, array('gitlab-domains', 'github-domains')) && isset($this->config[$key])) {
$this->config[$key] = array_unique(array_merge($this->config[$key], $val));
$this->setSourceOfConfigValue($val, $key, $source);
} elseif ('preferred-install' === $key && isset($this->config[$key])) {
if (is_array($val) || is_array($this->config[$key])) {
if (is_string($val)) {
@ -169,8 +186,10 @@ class Config
}
if (is_string($this->config[$key])) {
$this->config[$key] = array('*' => $this->config[$key]);
$this->sourceOfConfigValue[$key . '*'] = $source;
}
$this->config[$key] = array_merge($this->config[$key], $val);
$this->setSourceOfConfigValue($val, $key, $source);
// the full match pattern needs to be last
if (isset($this->config[$key]['*'])) {
$wildcard = $this->config[$key]['*'];
@ -179,9 +198,11 @@ class Config
}
} else {
$this->config[$key] = $val;
$this->setSourceOfConfigValue($val, $key, $source);
}
} else {
$this->config[$key] = $val;
$this->setSourceOfConfigValue($val, $key, $source);
}
}
}
@ -210,11 +231,14 @@ class Config
// store repo
if (is_int($name)) {
$this->repositories[] = $repository;
$this->setSourceOfConfigValue($repository, 'repositories.' . array_search($repository, $this->repositories, true), $source);
} else {
if ($name === 'packagist') { // BC support for default "packagist" named repo
$this->repositories[$name . '.org'] = $repository;
$this->setSourceOfConfigValue($repository, 'repositories.' . $name . '.org', $source);
} else {
$this->repositories[$name] = $repository;
$this->setSourceOfConfigValue($repository, 'repositories.' . $name, $source);
}
}
}
@ -257,6 +281,10 @@ class Config
$env = 'COMPOSER_' . strtoupper(strtr($key, '-', '_'));
$val = $this->getComposerEnv($env);
if ($val !== false) {
$this->setSourceOfConfigValue($val, $key, $env);
}
$val = rtrim((string) $this->process(false !== $val ? $val : $this->config[$key], $flags), '/\\');
$val = Platform::expandPath($val);
@ -275,6 +303,8 @@ class Config
$val = $this->getComposerEnv($env);
if (false === $val) {
$val = $this->config[$key];
} else {
$this->setSourceOfConfigValue($val, $key, $env);
}
return $val !== 'false' && (bool) $val;
@ -405,6 +435,35 @@ class Config
return $all;
}
/**
* @param string $key
* @return string
*/
public function getSourceOfValue($key)
{
$this->get($key);
return isset($this->sourceOfConfigValue[$key]) ? $this->sourceOfConfigValue[$key] : self::SOURCE_UNKNOWN;
}
/**
* @param mixed $configValue
* @param string $path
* @param string $source
*
* @return void
*/
private function setSourceOfConfigValue($configValue, $path, $source)
{
$this->sourceOfConfigValue[$path] = $source;
if (is_array($configValue)) {
foreach ($configValue as $key => $value) {
$this->setSourceOfConfigValue($value, $path . '.' . $key, $source);
}
}
}
/**
* @return array<string, mixed[]>
*/

View File

@ -186,7 +186,7 @@ class Factory
'home' => $home,
'cache-dir' => self::getCacheDir($home),
'data-dir' => self::getDataDir($home),
)));
)), Config::SOURCE_DEFAULT);
// load global config
$file = new JsonFile($config->get('home').'/config.json');
@ -194,7 +194,7 @@ class Factory
if ($io && $io->isDebug()) {
$io->writeError('Loading config file ' . $file->getPath());
}
$config->merge($file->read());
$config->merge($file->read(), $file->getPath());
}
$config->setConfigSource(new JsonConfigSource($file));
@ -220,7 +220,7 @@ class Factory
if ($io && $io->isDebug()) {
$io->writeError('Loading config file ' . $file->getPath());
}
$config->merge(array('config' => $file->read()));
$config->merge(array('config' => $file->read()), $file->getPath());
}
$config->setAuthConfigSource(new JsonConfigSource($file, true));
@ -236,7 +236,7 @@ class Factory
if ($io && $io->isDebug()) {
$io->writeError('Loading auth config from COMPOSER_AUTH');
}
$config->merge(array('config' => $authData));
$config->merge(array('config' => $authData), 'COMPOSER_AUTH');
}
}
@ -309,6 +309,7 @@ class Factory
$localConfig = static::getComposerFile();
}
$localConfigSource = Config::SOURCE_UNKNOWN;
if (is_string($localConfig)) {
$composerFile = $localConfig;
@ -340,11 +341,12 @@ class Factory
}
$localConfig = $file->read();
$localConfigSource = $file->getPath();
}
// Load config and override with local config/auth config
$config = static::createConfig($io, $cwd);
$config->merge($localConfig);
$config->merge($localConfig, $localConfigSource);
if (isset($composerFile)) {
$io->writeError('Loading config file ' . $composerFile .' ('.realpath($composerFile).')', true, IOInterface::DEBUG);
$config->setConfigSource(new JsonConfigSource(new JsonFile(realpath($composerFile), null, $io)));
@ -352,7 +354,7 @@ class Factory
$localAuthFile = new JsonFile(dirname(realpath($composerFile)) . '/auth.json', null, $io);
if ($localAuthFile->exists()) {
$io->writeError('Loading config file ' . $localAuthFile->getPath(), true, IOInterface::DEBUG);
$config->merge(array('config' => $localAuthFile->read()));
$config->merge(array('config' => $localAuthFile->read()), $localAuthFile->getPath());
$config->setAuthConfigSource(new JsonConfigSource($localAuthFile, true));
}
}

View File

@ -340,4 +340,26 @@ class ConfigTest extends TestCase
$this->assertEquals(0, $config->get('htaccess-protect'));
putenv('COMPOSER_HTACCESS_PROTECT');
}
public function testGetSourceOfValue()
{
$config = new Config;
$this->assertSame(Config::SOURCE_DEFAULT, $config->getSourceOfValue('process-timeout'));
$config->merge(
array('config' => array('process-timeout' => 1)),
'phpunit-test'
);
$this->assertSame('phpunit-test', $config->getSourceOfValue('process-timeout'));
}
public function testGetSourceOfValueEnvVariables()
{
putenv('COMPOSER_HTACCESS_PROTECT=0');
$config = new Config;
$this->assertEquals('COMPOSER_HTACCESS_PROTECT', $config->getSourceOfValue('htaccess-protect'));
putenv('COMPOSER_HTACCESS_PROTECT');
}
}