1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-25 08:12:40 +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:
dschleimer 2012-06-13 16:02:29 -07:00
parent a6a4b1ebbc
commit 5089cd7de1
5 changed files with 144 additions and 30 deletions

View file

@ -189,13 +189,17 @@ try {
$need_auth = $workflow->requiresAuthentication();
$need_repository_api = $workflow->requiresRepositoryAPI();
$want_repository_api = $workflow->desiresRepositoryAPI();
$want_working_copy = $workflow->desiresWorkingCopy() ||
$want_repository_api;
$need_conduit = $need_conduit ||
$need_auth;
$need_working_copy = $need_working_copy ||
$need_repository_api;
if ($need_working_copy) {
if (!$working_copy->getProjectRoot()) {
if ($need_working_copy || $want_working_copy) {
if ($need_working_copy && !$working_copy->getProjectRoot()) {
throw new ArcanistUsageException(
"This command must be run in a Git, Mercurial or Subversion working ".
"copy.");
@ -267,7 +271,7 @@ try {
$workflow->authenticateConduit();
}
if ($need_repository_api) {
if ($need_repository_api || ($want_repository_api && $working_copy)) {
$repository_api = ArcanistRepositoryAPI::newAPIFromWorkingCopyIdentity(
$working_copy);
$workflow->setRepositoryAPI($repository_api);

View file

@ -465,11 +465,18 @@ abstract class ArcanistBaseWorkflow {
return false;
}
public function desiresWorkingCopy() {
return false;
}
public function requiresRepositoryAPI() {
return false;
}
public function desiresRepositoryAPI() {
return false;
}
public function setCommand($command) {
$this->command = $command;
return $this;
@ -1020,6 +1027,25 @@ abstract class ArcanistBaseWorkflow {
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

View file

@ -25,7 +25,7 @@ final class ArcanistGetConfigWorkflow extends ArcanistBaseWorkflow {
public function getCommandSynopses() {
return phutil_console_format(<<<EOTEXT
**get-config** [__name__ ...]
**get-config** -- [__name__ ...]
EOTEXT
);
}
@ -45,20 +45,32 @@ EOTEXT
);
}
public function desiresRepositoryAPI() {
return true;
}
public function run() {
$argv = $this->getArgument('argv');
$config = self::readGlobalArcConfig();
$configs = array(
'global' => self::readGlobalArcConfig(),
'local' => $this->readLocalArcConfig(),
);
if ($argv) {
$keys = $argv;
} else {
$keys = array_keys($config);
$keys = array_mergev(array_map('array_keys', $configs));
$keys = array_unique($keys);
sort($keys);
}
foreach ($keys as $key) {
$val = self::formatConfigValueForDisplay(idx($config, $key));
echo "{$key} = {$val}\n";
foreach ($configs as $name => $config) {
if ($name == 'global' || isset($config[$key])) {
$val = self::formatConfigValueForDisplay(idx($config, $key));
echo "({$name}) {$key} = {$val}\n";
}
}
}
return 0;

View file

@ -25,7 +25,7 @@ final class ArcanistSetConfigWorkflow extends ArcanistBaseWorkflow {
public function getCommandSynopses() {
return phutil_console_format(<<<EOTEXT
**set-config** __name__ __value__
**set-config** [__options__] -- __name__ __value__
EOTEXT
);
}
@ -35,8 +35,14 @@ EOTEXT
Supports: cli
Sets an arc configuration option.
Values are written to '~/.arcrc' on Linux and Mac OS X, and an
undisclosed location on Windows.
Options are either global (apply to all arc commands you invoke
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
is shown.
@ -49,10 +55,17 @@ EOTEXT
'show' => array(
'help' => 'Show available configuration values.',
),
'local' => array(
'help' => 'Set a local config value instead of a global one',
),
'*' => 'argv',
);
}
public function requiresRepositoryAPI() {
return $this->getArgument('local');
}
public function run() {
if ($this->getArgument('show')) {
return $this->show();
@ -64,7 +77,15 @@ EOTEXT
"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];
$val = $argv[1];
@ -76,26 +97,34 @@ EOTEXT
if (!strlen($val)) {
unset($config[$key]);
self::writeGlobalArcConfig($config);
if ($is_local) {
$this->writeLocalArcConfig($config);
} else {
self::writeGlobalArcConfig($config);
}
if ($old === null) {
echo "Deleted key '{$key}'.\n";
echo "Deleted key '{$key}' from {$which} config.\n";
} else {
echo "Deleted key '{$key}' (was '{$old}').\n";
echo "Deleted key '{$key}' from {$which} config (was '{$old}').\n";
}
} else {
$val = $this->parse($key, $val);
$config[$key] = $val;
self::writeGlobalArcConfig($config);
if ($is_local) {
$this->writeLocalArcConfig($config);
} else {
self::writeGlobalArcConfig($config);
}
$val = self::formatConfigValueForDisplay($val);
$old = self::formatConfigValueForDisplay($old);
if ($old === null) {
echo "Set key '{$key}' = '{$val}'.\n";
echo "Set key '{$key}' = '{$val}' in {$which} config.\n";
} else {
echo "Set key '{$key}' = '{$val}' (was '{$old}').\n";
echo "Set key '{$key}' = '{$val}' in {$which} config (was '{$old}').\n";
}
}

View file

@ -26,6 +26,7 @@
*/
final class ArcanistWorkingCopyIdentity {
protected $localConfig;
protected $projectConfig;
protected $projectRoot;
@ -97,6 +98,26 @@ final class ArcanistWorkingCopyIdentity {
protected function __construct($root, array $config) {
$this->projectRoot = $root;
$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() {
@ -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.
* 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 wild Default value if key is not found.
@ -144,26 +180,33 @@ final class ArcanistWorkingCopyIdentity {
* @task config
*/
public function getConfigFromAnySource($key, $default = null) {
$pval = $this->getConfig($key);
if ($pval !== null) {
return $pval;
// try local config first
$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(
'lint.engine' => 'lint_engine',
'unit.engine' => 'unit_engine',
);
if (isset($deprecated_names[$key])) {
if ($pval === null && isset($deprecated_names[$key])) {
$pval = $this->getConfig($deprecated_names[$key]);
if ($pval !== null) {
return $pval;
}
}
$global_config = ArcanistBaseWorkflow::readGlobalArcConfig();
return idx($global_config, $key, $default);
// lastly, try global (i.e. user-level) config
if ($pval === null) {
$global_config = ArcanistBaseWorkflow::readGlobalArcConfig();
$pval = idx($global_config, $key, $default);
}
return $pval;
}
}