mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-11-25 16:22:42 +01:00
[Arcanist] add a local (per working copy) config file
Summary: This adds the concept of a local (i.e. working copy local) config file to arc. This config file has the highest precedence for config settings that may come from any config file. The config is stored in .(git|hg|sv)/arc/config, and is read ahead of the project config by getConfigFromAnySource(). Test Plan: #Testing arc set-config and arc get-config [16:57:04 Tue Jun 12 2012] dschleimer@dev4022.snc6 ~/devtools/arcanist arcanist local_config 19410 $ ./bin/arc get-config (global) default = https://phabricator.fb.com/conduit [16:57:12 Tue Jun 12 2012] dschleimer@dev4022.snc6 ~/devtools/arcanist arcanist local_config 19410 $ ./bin/arc set-config foo bar Set key 'foo' = 'bar' in global config. [16:57:23 Tue Jun 12 2012] dschleimer@dev4022.snc6 ~/devtools/arcanist arcanist local_config 19411 $ ./bin/arc get-config (global) default = https://phabricator.fb.com/conduit (global) foo = bar [16:57:26 Tue Jun 12 2012] dschleimer@dev4022.snc6 ~/devtools/arcanist arcanist local_config 19411 $ ./bin/arc set-config --local foo baz Set key 'foo' = 'baz' in local config. [16:57:35 Tue Jun 12 2012] dschleimer@dev4022.snc6 ~/devtools/arcanist arcanist local_config 19412 $ ./bin/arc get-config (global) default = https://phabricator.fb.com/conduit (global) foo = bar (local) foo = baz [16:57:39 Tue Jun 12 2012] dschleimer@dev4022.snc6 ~/devtools/arcanist arcanist local_config 19412 $ ./bin/arc set-config foo '' Deleted key 'foo' from global config (was 'bar'). [16:57:49 Tue Jun 12 2012] dschleimer@dev4022.snc6 ~/devtools/arcanist arcanist local_config 19413 $ ./bin/arc get-config (global) default = https://phabricator.fb.com/conduit (global) foo = (local) foo = baz [16:57:51 Tue Jun 12 2012] dschleimer@dev4022.snc6 ~/devtools/arcanist arcanist local_config 19413 $ ./bin/arc set-config --local foo '' Deleted key 'foo' from local config (was 'baz'). [16:58:05 Tue Jun 12 2012] dschleimer@dev4022.snc6 ~/devtools/arcanist arcanist local_config 19414 $ ./bin/arc get-config (global) default = https://phabricator.fb.com/conduit #testing getConfigFromAnySource by means of lint [11:26:57 Wed Jun 13 2012] dschleimer@dev4022.snc6 ~/devtools/arcanist arcanist local_config 19612 $ ./bin/arc set-config lint.engine BogusLintEngine Set key 'lint.engine' = 'BogusLintEngine' in global config. [11:30:04 Wed Jun 13 2012] dschleimer@dev4022.snc6 ~/devtools/arcanist arcanist local_config 19613 $ ./bin/arc set-config --local lint.engine DummyLintEngine Set key 'lint.engine' = 'DummyLintEngine' in local config. [11:30:19 Wed Jun 13 2012] dschleimer@dev4022.snc6 ~/devtools/arcanist arcanist local_config 19587 $ ./bin/arc lint Exception Failed to load symbol 'DummyLintEngine'. If this symbol was recently added or moved, your library map may be out of date. You can rebuild the map by running 'arc liberate'. For more information, see: http://www.phabricator.com/docs/phabricator/article/libphutil_Libraries_User_Guide.html (Run with --trace for a full exception trace.) [11:30:25 Wed Jun 13 2012] dschleimer@dev4022.snc6 ~/devtools/arcanist arcanist local_config 19586 $ ./bin/arc set-config --local lint.engine '' Deleted key 'lint.engine' from local config (was 'DummyLintEngine'). [11:30:34 Wed Jun 13 2012] dschleimer@dev4022.snc6 ~/devtools/arcanist arcanist local_config 19587 $ ./bin/arc lint OKAY No lint warnings. Reviewers: epriestley Reviewed By: epriestley CC: aran, Korvin Maniphest Tasks: T1233 Differential Revision: https://secure.phabricator.com/D2739
This commit is contained in:
parent
a6a4b1ebbc
commit
5089cd7de1
5 changed files with 144 additions and 30 deletions
|
@ -189,13 +189,17 @@ try {
|
||||||
$need_auth = $workflow->requiresAuthentication();
|
$need_auth = $workflow->requiresAuthentication();
|
||||||
$need_repository_api = $workflow->requiresRepositoryAPI();
|
$need_repository_api = $workflow->requiresRepositoryAPI();
|
||||||
|
|
||||||
|
$want_repository_api = $workflow->desiresRepositoryAPI();
|
||||||
|
$want_working_copy = $workflow->desiresWorkingCopy() ||
|
||||||
|
$want_repository_api;
|
||||||
|
|
||||||
$need_conduit = $need_conduit ||
|
$need_conduit = $need_conduit ||
|
||||||
$need_auth;
|
$need_auth;
|
||||||
$need_working_copy = $need_working_copy ||
|
$need_working_copy = $need_working_copy ||
|
||||||
$need_repository_api;
|
$need_repository_api;
|
||||||
|
|
||||||
if ($need_working_copy) {
|
if ($need_working_copy || $want_working_copy) {
|
||||||
if (!$working_copy->getProjectRoot()) {
|
if ($need_working_copy && !$working_copy->getProjectRoot()) {
|
||||||
throw new ArcanistUsageException(
|
throw new ArcanistUsageException(
|
||||||
"This command must be run in a Git, Mercurial or Subversion working ".
|
"This command must be run in a Git, Mercurial or Subversion working ".
|
||||||
"copy.");
|
"copy.");
|
||||||
|
@ -267,7 +271,7 @@ try {
|
||||||
$workflow->authenticateConduit();
|
$workflow->authenticateConduit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($need_repository_api) {
|
if ($need_repository_api || ($want_repository_api && $working_copy)) {
|
||||||
$repository_api = ArcanistRepositoryAPI::newAPIFromWorkingCopyIdentity(
|
$repository_api = ArcanistRepositoryAPI::newAPIFromWorkingCopyIdentity(
|
||||||
$working_copy);
|
$working_copy);
|
||||||
$workflow->setRepositoryAPI($repository_api);
|
$workflow->setRepositoryAPI($repository_api);
|
||||||
|
|
|
@ -465,11 +465,18 @@ abstract class ArcanistBaseWorkflow {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function desiresWorkingCopy() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public function requiresRepositoryAPI() {
|
public function requiresRepositoryAPI() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function desiresRepositoryAPI() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public function setCommand($command) {
|
public function setCommand($command) {
|
||||||
$this->command = $command;
|
$this->command = $command;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -1020,6 +1027,25 @@ abstract class ArcanistBaseWorkflow {
|
||||||
self::writeUserConfigurationFile($config);
|
self::writeUserConfigurationFile($config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function readLocalArcConfig() {
|
||||||
|
$local = array();
|
||||||
|
$file = $this->readScratchFile('config');
|
||||||
|
if ($file) {
|
||||||
|
$local = json_decode($file, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $local;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function writeLocalArcConfig(array $config) {
|
||||||
|
$json_encoder = new PhutilJSON();
|
||||||
|
$json = $json_encoder->encodeFormatted($config);
|
||||||
|
|
||||||
|
$this->writeScratchFile('config', $json);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a message to stderr so that '--json' flags or stdout which is meant
|
* Write a message to stderr so that '--json' flags or stdout which is meant
|
||||||
|
|
|
@ -25,7 +25,7 @@ final class ArcanistGetConfigWorkflow extends ArcanistBaseWorkflow {
|
||||||
|
|
||||||
public function getCommandSynopses() {
|
public function getCommandSynopses() {
|
||||||
return phutil_console_format(<<<EOTEXT
|
return phutil_console_format(<<<EOTEXT
|
||||||
**get-config** [__name__ ...]
|
**get-config** -- [__name__ ...]
|
||||||
EOTEXT
|
EOTEXT
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -45,20 +45,32 @@ EOTEXT
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function desiresRepositoryAPI() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public function run() {
|
public function run() {
|
||||||
$argv = $this->getArgument('argv');
|
$argv = $this->getArgument('argv');
|
||||||
|
|
||||||
$config = self::readGlobalArcConfig();
|
$configs = array(
|
||||||
|
'global' => self::readGlobalArcConfig(),
|
||||||
|
'local' => $this->readLocalArcConfig(),
|
||||||
|
);
|
||||||
if ($argv) {
|
if ($argv) {
|
||||||
$keys = $argv;
|
$keys = $argv;
|
||||||
} else {
|
} else {
|
||||||
$keys = array_keys($config);
|
$keys = array_mergev(array_map('array_keys', $configs));
|
||||||
|
$keys = array_unique($keys);
|
||||||
sort($keys);
|
sort($keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($keys as $key) {
|
foreach ($keys as $key) {
|
||||||
$val = self::formatConfigValueForDisplay(idx($config, $key));
|
foreach ($configs as $name => $config) {
|
||||||
echo "{$key} = {$val}\n";
|
if ($name == 'global' || isset($config[$key])) {
|
||||||
|
$val = self::formatConfigValueForDisplay(idx($config, $key));
|
||||||
|
echo "({$name}) {$key} = {$val}\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -25,7 +25,7 @@ final class ArcanistSetConfigWorkflow extends ArcanistBaseWorkflow {
|
||||||
|
|
||||||
public function getCommandSynopses() {
|
public function getCommandSynopses() {
|
||||||
return phutil_console_format(<<<EOTEXT
|
return phutil_console_format(<<<EOTEXT
|
||||||
**set-config** __name__ __value__
|
**set-config** [__options__] -- __name__ __value__
|
||||||
EOTEXT
|
EOTEXT
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -35,8 +35,14 @@ EOTEXT
|
||||||
Supports: cli
|
Supports: cli
|
||||||
Sets an arc configuration option.
|
Sets an arc configuration option.
|
||||||
|
|
||||||
Values are written to '~/.arcrc' on Linux and Mac OS X, and an
|
Options are either global (apply to all arc commands you invoke
|
||||||
undisclosed location on Windows.
|
from the current user) or local (apply only to the current working
|
||||||
|
copy). By default, global configuration is written. Use __--local__
|
||||||
|
to write local configuration.
|
||||||
|
|
||||||
|
Global values are written to '~/.arcrc' on Linux and Mac OS X, and an
|
||||||
|
undisclosed location on Windows. Local values are written to an arc
|
||||||
|
directory under either .git, .hg, or .svn as appropriate.
|
||||||
|
|
||||||
With __--show__, a description of supported configuration values
|
With __--show__, a description of supported configuration values
|
||||||
is shown.
|
is shown.
|
||||||
|
@ -49,10 +55,17 @@ EOTEXT
|
||||||
'show' => array(
|
'show' => array(
|
||||||
'help' => 'Show available configuration values.',
|
'help' => 'Show available configuration values.',
|
||||||
),
|
),
|
||||||
|
'local' => array(
|
||||||
|
'help' => 'Set a local config value instead of a global one',
|
||||||
|
),
|
||||||
'*' => 'argv',
|
'*' => 'argv',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function requiresRepositoryAPI() {
|
||||||
|
return $this->getArgument('local');
|
||||||
|
}
|
||||||
|
|
||||||
public function run() {
|
public function run() {
|
||||||
if ($this->getArgument('show')) {
|
if ($this->getArgument('show')) {
|
||||||
return $this->show();
|
return $this->show();
|
||||||
|
@ -64,7 +77,15 @@ EOTEXT
|
||||||
"Specify a key and a value, or --show.");
|
"Specify a key and a value, or --show.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$config = self::readGlobalArcConfig();
|
$is_local = $this->getArgument('local');
|
||||||
|
|
||||||
|
if ($is_local) {
|
||||||
|
$config = $this->readLocalArcConfig();
|
||||||
|
$which = 'local';
|
||||||
|
} else {
|
||||||
|
$config = self::readGlobalArcConfig();
|
||||||
|
$which = 'global';
|
||||||
|
}
|
||||||
|
|
||||||
$key = $argv[0];
|
$key = $argv[0];
|
||||||
$val = $argv[1];
|
$val = $argv[1];
|
||||||
|
@ -76,26 +97,34 @@ EOTEXT
|
||||||
|
|
||||||
if (!strlen($val)) {
|
if (!strlen($val)) {
|
||||||
unset($config[$key]);
|
unset($config[$key]);
|
||||||
self::writeGlobalArcConfig($config);
|
if ($is_local) {
|
||||||
|
$this->writeLocalArcConfig($config);
|
||||||
|
} else {
|
||||||
|
self::writeGlobalArcConfig($config);
|
||||||
|
}
|
||||||
|
|
||||||
if ($old === null) {
|
if ($old === null) {
|
||||||
echo "Deleted key '{$key}'.\n";
|
echo "Deleted key '{$key}' from {$which} config.\n";
|
||||||
} else {
|
} else {
|
||||||
echo "Deleted key '{$key}' (was '{$old}').\n";
|
echo "Deleted key '{$key}' from {$which} config (was '{$old}').\n";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$val = $this->parse($key, $val);
|
$val = $this->parse($key, $val);
|
||||||
|
|
||||||
$config[$key] = $val;
|
$config[$key] = $val;
|
||||||
self::writeGlobalArcConfig($config);
|
if ($is_local) {
|
||||||
|
$this->writeLocalArcConfig($config);
|
||||||
|
} else {
|
||||||
|
self::writeGlobalArcConfig($config);
|
||||||
|
}
|
||||||
|
|
||||||
$val = self::formatConfigValueForDisplay($val);
|
$val = self::formatConfigValueForDisplay($val);
|
||||||
$old = self::formatConfigValueForDisplay($old);
|
$old = self::formatConfigValueForDisplay($old);
|
||||||
|
|
||||||
if ($old === null) {
|
if ($old === null) {
|
||||||
echo "Set key '{$key}' = '{$val}'.\n";
|
echo "Set key '{$key}' = '{$val}' in {$which} config.\n";
|
||||||
} else {
|
} else {
|
||||||
echo "Set key '{$key}' = '{$val}' (was '{$old}').\n";
|
echo "Set key '{$key}' = '{$val}' in {$which} config (was '{$old}').\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
*/
|
*/
|
||||||
final class ArcanistWorkingCopyIdentity {
|
final class ArcanistWorkingCopyIdentity {
|
||||||
|
|
||||||
|
protected $localConfig;
|
||||||
protected $projectConfig;
|
protected $projectConfig;
|
||||||
protected $projectRoot;
|
protected $projectRoot;
|
||||||
|
|
||||||
|
@ -97,6 +98,26 @@ final class ArcanistWorkingCopyIdentity {
|
||||||
protected function __construct($root, array $config) {
|
protected function __construct($root, array $config) {
|
||||||
$this->projectRoot = $root;
|
$this->projectRoot = $root;
|
||||||
$this->projectConfig = $config;
|
$this->projectConfig = $config;
|
||||||
|
$this->localConfig = array();
|
||||||
|
|
||||||
|
$vc_dirs = array(
|
||||||
|
'.git',
|
||||||
|
'.hg',
|
||||||
|
'.svn',
|
||||||
|
);
|
||||||
|
foreach ($vc_dirs as $dir) {
|
||||||
|
$local_path = Filesystem::resolvePath(
|
||||||
|
$dir.'/arc/config',
|
||||||
|
$this->projectRoot);
|
||||||
|
if (Filesystem::pathExists($local_path)) {
|
||||||
|
$file = Filesystem::readFile($local_path);
|
||||||
|
if ($file) {
|
||||||
|
$this->localConfig = json_decode($file, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getProjectID() {
|
public function getProjectID() {
|
||||||
|
@ -132,10 +153,25 @@ final class ArcanistWorkingCopyIdentity {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a configuration directive from local configuration. This
|
||||||
|
* reads ONLY the per-working copy configuration,
|
||||||
|
* i.e. .(git|hg|svn)/arc/config, and not other configuration
|
||||||
|
* sources. See @{method:getConfigFromAnySource} to read from any
|
||||||
|
* config source or @{method:getConfig} to read permanent
|
||||||
|
* project-level config.
|
||||||
|
*
|
||||||
|
* @task config
|
||||||
|
*/
|
||||||
|
public function getLocalConfig($key, $default=null) {
|
||||||
|
return idx($this->localConfig, $key, $default);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a configuration directive from any available configuration source.
|
* Read a configuration directive from any available configuration source.
|
||||||
* In contrast to @{method:getConfig}, this will look for the directive in
|
* In contrast to @{method:getConfig}, this will look for the directive in
|
||||||
* user configuration in addition to project configuration.
|
* local and user configuration in addition to project configuration.
|
||||||
|
* The precedence is local > project > user
|
||||||
*
|
*
|
||||||
* @param key Key to read.
|
* @param key Key to read.
|
||||||
* @param wild Default value if key is not found.
|
* @param wild Default value if key is not found.
|
||||||
|
@ -144,26 +180,33 @@ final class ArcanistWorkingCopyIdentity {
|
||||||
* @task config
|
* @task config
|
||||||
*/
|
*/
|
||||||
public function getConfigFromAnySource($key, $default = null) {
|
public function getConfigFromAnySource($key, $default = null) {
|
||||||
$pval = $this->getConfig($key);
|
|
||||||
if ($pval !== null) {
|
// try local config first
|
||||||
return $pval;
|
$pval = $this->getLocalConfig($key);
|
||||||
|
|
||||||
|
// then per-project config
|
||||||
|
if ($pval === null) {
|
||||||
|
$pval = $this->getConfig($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test for older names.
|
// Test for older names in the per-project config only, since
|
||||||
|
// they've only been used there
|
||||||
static $deprecated_names = array(
|
static $deprecated_names = array(
|
||||||
'lint.engine' => 'lint_engine',
|
'lint.engine' => 'lint_engine',
|
||||||
'unit.engine' => 'unit_engine',
|
'unit.engine' => 'unit_engine',
|
||||||
);
|
);
|
||||||
if (isset($deprecated_names[$key])) {
|
if ($pval === null && isset($deprecated_names[$key])) {
|
||||||
$pval = $this->getConfig($deprecated_names[$key]);
|
$pval = $this->getConfig($deprecated_names[$key]);
|
||||||
if ($pval !== null) {
|
|
||||||
return $pval;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$global_config = ArcanistBaseWorkflow::readGlobalArcConfig();
|
// lastly, try global (i.e. user-level) config
|
||||||
return idx($global_config, $key, $default);
|
if ($pval === null) {
|
||||||
|
$global_config = ArcanistBaseWorkflow::readGlobalArcConfig();
|
||||||
|
$pval = idx($global_config, $key, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $pval;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue