Config: add source option for command to show where a config value is loaded from (#10129)
parent
012556daee
commit
44c5b6cde6
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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[]>
|
||||
*/
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue