1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-12-22 21:40:54 +01:00

Extract configuration read/write methods out of BaseWorlkflow

Summary:
Create a new class for them, pass instance around as need.

This looks like it's mostly working, but I'd like to replace the various `new ArcanistConfigurationManager()`
calls with something more suitable.

And maybe get a better name for ArcanistConfigurationManager ("Configuration" is already taken).

Test Plan: arc unit --everything, and then some.

Reviewers: epriestley, #blessed_reviewers

Reviewed By: epriestley

CC: Korvin, epriestley, aran, chad

Differential Revision: https://secure.phabricator.com/D7271
This commit is contained in:
Aviv Eyal 2013-10-18 16:10:06 -07:00 committed by epriestley
parent b2021586d4
commit a2285b2b5a
25 changed files with 517 additions and 323 deletions

View file

@ -74,14 +74,17 @@ try {
array_unshift($args, 'help');
}
$global_config = ArcanistBaseWorkflow::readGlobalArcConfig();
$system_config = ArcanistBaseWorkflow::readSystemArcConfig();
$configuration_manager = new ArcanistConfigurationManager();
$global_config = $configuration_manager->readUserArcConfig();
$system_config = $configuration_manager->readSystemArcConfig();
if ($skip_arcconfig) {
$working_copy = ArcanistWorkingCopyIdentity::newDummyWorkingCopy();
} else {
$working_copy =
ArcanistWorkingCopyIdentity::newFromPath($working_directory);
}
$configuration_manager->setWorkingCopyIdentity($working_copy);
reenter_if_this_is_arcanist_or_libphutil(
$console,
@ -133,7 +136,7 @@ try {
$working_copy);
}
$user_config = ArcanistBaseWorkflow::readUserConfigurationFile();
$user_config = $configuration_manager->readUserConfigurationFile();
$config_class = $working_copy->getConfig('arcanist_configuration');
if ($config_class) {
@ -144,7 +147,12 @@ try {
$command = strtolower($args[0]);
$args = array_slice($args, 1);
$workflow = $config->selectWorkflow($command, $args, $working_copy, $console);
$workflow = $config->selectWorkflow(
$command,
$args,
$configuration_manager,
$console);
$workflow->setConfigurationManager($configuration_manager);
$workflow->setArcanistConfiguration($config);
$workflow->setCommand($command);
$workflow->setWorkingDirectory($working_directory);
@ -182,14 +190,16 @@ try {
"This command must be run in a Git, Mercurial or Subversion working ".
"copy.");
}
$workflow->setWorkingCopy($working_copy);
$configuration_manager->setWorkingCopyIdentity($working_copy);
}
if ($force_conduit) {
$conduit_uri = $force_conduit;
} else {
if ($working_copy->getConduitURI()) {
$conduit_uri = $working_copy->getConduitURI();
$project_conduit_uri =
$configuration_manager->getProjectConfig('conduit_uri');
if ($project_conduit_uri) {
$conduit_uri = $project_conduit_uri;
} else {
$conduit_uri = idx($global_config, 'default');
}
@ -207,14 +217,15 @@ try {
$workflow->setConduitURI($conduit_uri);
// Apply global CA bundle from configs.
if ($ca_bundle = $working_copy->getConfigFromAnySource('https.cabundle')) {
$ca_bundle = $configuration_manager->getConfigFromAnySource('https.cabundle');
if ($ca_bundle) {
$ca_bundle = Filesystem::resolvePath(
$ca_bundle, $working_copy->getProjectRoot());
HTTPSFuture::setGlobalCABundleFromPath($ca_bundle);
}
$blind_key = 'https.blindly-trust-domains';
$blind_trust = $working_copy->getConfigFromAnySource($blind_key);
$blind_trust = $configuration_manager->getConfigFromAnySource($blind_key);
if ($blind_trust) {
HTTPSFuture::setBlindlyTrustDomains($blind_trust);
}
@ -268,12 +279,13 @@ try {
}
if ($need_repository_api || ($want_repository_api && $working_copy)) {
$repository_api = ArcanistRepositoryAPI::newAPIFromWorkingCopyIdentity(
$working_copy);
$repository_api = ArcanistRepositoryAPI::newAPIFromConfigurationManager(
$configuration_manager);
$workflow->setRepositoryAPI($repository_api);
}
$listeners = $working_copy->getConfigFromAnySource('events.listeners');
$listeners = $configuration_manager->getConfigFromAnySource(
'events.listeners');
if ($listeners) {
foreach ($listeners as $listener) {
$console->writeLog(

View file

@ -43,6 +43,7 @@ phutil_register_library_map(array(
'ArcanistConduitLinter' => 'lint/linter/ArcanistConduitLinter.php',
'ArcanistConfiguration' => 'configuration/ArcanistConfiguration.php',
'ArcanistConfigurationDrivenLintEngine' => 'lint/engine/ArcanistConfigurationDrivenLintEngine.php',
'ArcanistConfigurationManager' => 'configuration/ArcanistConfigurationManager.php',
'ArcanistCoverWorkflow' => 'workflow/ArcanistCoverWorkflow.php',
'ArcanistCppcheckLinter' => 'lint/linter/ArcanistCppcheckLinter.php',
'ArcanistCpplintLinter' => 'lint/linter/ArcanistCpplintLinter.php',

View file

@ -78,7 +78,7 @@ class ArcanistConfiguration {
final public function selectWorkflow(
&$command,
array &$args,
ArcanistWorkingCopyIdentity $working_copy,
ArcanistConfigurationManager $configuration_manager,
PhutilConsole $console) {
// First, try to build a workflow with the exact name provided. We always
@ -92,12 +92,12 @@ class ArcanistConfiguration {
// and substitute it. We do this only after trying to resolve the workflow
// normally to prevent you from doing silly things like aliasing 'alias'
// to something else.
$aliases = ArcanistAliasWorkflow::getAliases($working_copy);
$aliases = ArcanistAliasWorkflow::getAliases($configuration_manager);
list($new_command, $args) = ArcanistAliasWorkflow::resolveAliases(
$command,
$this,
$args,
$working_copy);
$configuration_manager);
$full_alias = idx($aliases, $command, array());
$full_alias = implode(' ', $full_alias);

View file

@ -0,0 +1,248 @@
<?php
/**
* This class holds everything related to configuration and configuration files.
*
* @group config
*/
final class ArcanistConfigurationManager {
private $runtimeConfig = array();
private $workingCopy = null;
public function setWorkingCopyIdentity(
ArcanistWorkingCopyIdentity $working_copy) {
$this->workingCopy = $working_copy;
}
/* -( Get config )--------------------------------------------------------- */
const CONFIG_SOURCE_RUNTIME = 'runtime';
const CONFIG_SOURCE_LOCAL = 'local';
const CONFIG_SOURCE_PROJECT = 'project';
const CONFIG_SOURCE_USER = 'user';
const CONFIG_SOURCE_SYSTEM = 'system';
public function getProjectConfig($key) {
if ($this->workingCopy) {
return $this->workingCopy->getConfig($key);
}
return null;
}
public function getLocalConfig($key) {
if ($this->workingCopy) {
return $this->workingCopy->getLocalConfig($key);
}
return null;
}
public function getWorkingCopyIdentity() {
return $this->workingCopy;
}
/**
* Read a configuration directive from any available configuration source.
* This includes the directive in local, user and system configuration in
* addition to project configuration, and configuration provided as command
* arguments ("runtime").
* The precedence is runtime > local > project > user > system
*
* @param key Key to read.
* @param wild Default value if key is not found.
* @return wild Value, or default value if not found.
*
* @task config
*/
public function getConfigFromAnySource($key, $default = null) {
$all = $this->getConfigFromAllSources($key);
return empty($all) ? $default : head($all);
}
/**
* For the advanced case where you want customized configuratin handling.
*
* Reads the configuration from all available sources, returning a map (array)
* of results, with the source as key. Missing values will not be in the map,
* so an empty array will be returned if no results are found.
*
* The map is ordered by the cannonical sources precedence, which is:
* runtime > local > project > user > system
*
* @param key Key to read
* @return array Mapping of source => value read. Sources with no value are
* not in the array.
*
* @task config
*/
public function getConfigFromAllSources($key) {
$results = array();
$settings = new ArcanistSettings();
$pval = idx($this->runtimeConfig, $key);
if ($pval !== null) {
$results[self::CONFIG_SOURCE_RUNTIME] =
$settings->willReadValue($key, $pval);
}
$pval = $this->getLocalConfig($key);
if ($pval !== null) {
$results[self::CONFIG_SOURCE_LOCAL] =
$settings->willReadValue($key, $pval);
}
$pval = $this->getProjectConfig($key);
if ($pval !== null) {
$results[self::CONFIG_SOURCE_PROJECT] =
$settings->willReadValue($key, $pval);
}
$user_config = $this->readUserArcConfig();
$pval = idx($user_config, $key);
if ($pval !== null) {
$results[self::CONFIG_SOURCE_USER] =
$settings->willReadValue($key, $pval);
}
$system_config = $this->readSystemArcConfig();
$pval = idx($system_config, $key);
if ($pval !== null) {
$results[self::CONFIG_SOURCE_SYSTEM] =
$settings->willReadValue($key, $pval);
}
return $results;
}
/**
* Sets a runtime config value that takes precedence over any static
* config values.
*
* @param key Key to set.
* @param value The value of the key.
*
* @task config
*/
public function setRuntimeConfig($key, $value) {
$this->runtimeConfig[$key] = $value;
return $this;
}
/* -( Read/write config )--------------------------------------------------- */
public function readLocalArcConfig() {
if ($this->workingCopy) {
return $this->workingCopy->readLocalArcConfig();
}
return array();
}
public function writeLocalArcConfig(array $config) {
if ($this->workingCopy) {
return $this->workingCopy->writeLocalArcConfig($config);
}
return false;
}
/**
* This is probably not the method you're looking for; try
* @{method:readUserArcConfig}.
*/
public function readUserConfigurationFile() {
$user_config = array();
$user_config_path = self::getUserConfigurationFileLocation();
if (Filesystem::pathExists($user_config_path)) {
if (!phutil_is_windows()) {
$mode = fileperms($user_config_path);
if (!$mode) {
throw new Exception("Unable to get perms of '{$user_config_path}'!");
}
if ($mode & 0177) {
// Mode should allow only owner access.
$prompt = "File permissions on your ~/.arcrc are too open. ".
"Fix them by chmod'ing to 600?";
if (!phutil_console_confirm($prompt, $default_no = false)) {
throw new ArcanistUsageException("Set ~/.arcrc to file mode 600.");
}
execx('chmod 600 %s', $user_config_path);
// Drop the stat cache so we don't read the old permissions if
// we end up here again. If we don't do this, we may prompt the user
// to fix permissions multiple times.
clearstatcache();
}
}
$user_config_data = Filesystem::readFile($user_config_path);
$user_config = json_decode($user_config_data, true);
if (!is_array($user_config)) {
throw new ArcanistUsageException(
"Your '~/.arcrc' file is not a valid JSON file.");
}
}
return $user_config;
}
/**
* This is probably not the method you're looking for; try
* @{method:writeUserArcConfig}.
*/
public function writeUserConfigurationFile($config) {
$json_encoder = new PhutilJSON();
$json = $json_encoder->encodeFormatted($config);
$path = self::getUserConfigurationFileLocation();
Filesystem::writeFile($path, $json);
if (!phutil_is_windows()) {
execx('chmod 600 %s', $path);
}
}
public function getUserConfigurationFileLocation() {
if (phutil_is_windows()) {
return getenv('APPDATA').'/.arcrc';
} else {
return getenv('HOME').'/.arcrc';
}
}
public function readUserArcConfig() {
return idx(self::readUserConfigurationFile(), 'config', array());
}
public function writeUserArcConfig(array $options) {
$config = self::readUserConfigurationFile();
$config['config'] = $options;
self::writeUserConfigurationFile($config);
}
public function getSystemArcConfigLocation() {
if (phutil_is_windows()) {
return Filesystem::resolvePath(
'Phabricator/Arcanist/config',
getenv('ProgramData'));
} else {
return '/etc/arcconfig';
}
}
public function readSystemArcConfig() {
$system_config = array();
$system_config_path = self::getSystemArcConfigLocation();
if (Filesystem::pathExists($system_config_path)) {
$file = Filesystem::readFile($system_config_path);
if ($file) {
$system_config = json_decode($file, true);
}
}
return $system_config;
}
}

View file

@ -347,9 +347,8 @@ final class ArcanistScriptAndRegexLinter extends ArcanistLinter {
* @task config
*/
private function getConfiguredScript() {
$working_copy = $this->getEngine()->getWorkingCopy();
$key = 'linter.scriptandregex.script';
$config = $working_copy->getConfigFromAnySource($key);
$config = $this->getConfigFromAnySource($key);
if (!$config) {
throw new ArcanistUsageException(
@ -373,9 +372,8 @@ final class ArcanistScriptAndRegexLinter extends ArcanistLinter {
* @task config
*/
private function getConfiguredRegex() {
$working_copy = $this->getEngine()->getWorkingCopy();
$key = 'linter.scriptandregex.regex';
$config = $working_copy->getConfigFromAnySource($key);
$config = $this->getConfigFromAnySource($key);
if (!$config) {
throw new ArcanistUsageException(

View file

@ -1630,7 +1630,7 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
$global_string = $global->getConcreteString();
$globals_map[$global_string] = true;
$names[] = array(
'global',
'user',
$global_string,
$global,

View file

@ -38,10 +38,10 @@ final class ArcanistBaseCommitParser {
public function resolveBaseCommit(array $specs) {
$specs += array(
'args' => '',
'runtime' => '',
'local' => '',
'project' => '',
'global' => '',
'user' => '',
'system' => '',
);
@ -50,10 +50,10 @@ final class ArcanistBaseCommitParser {
}
$this->try = array(
'args',
'runtime',
'local',
'project',
'global',
'user',
'system',
);
@ -116,6 +116,8 @@ final class ArcanistBaseCommitParser {
* Handle resolving "arc:*" rules.
*/
private function resolveArcRule($rule, $name, $source) {
$name = $this->updateLegacyRuleName($name);
switch ($name) {
case 'verbose':
$this->verbose = true;
@ -126,9 +128,9 @@ final class ArcanistBaseCommitParser {
$this->api->setBaseCommitExplanation($reason);
return phutil_console_prompt('Against which commit?');
case 'local':
case 'global':
case 'user':
case 'project':
case 'args':
case 'runtime':
case 'system':
// Push the other source on top of the list.
array_unshift($this->try, $name);
@ -175,4 +177,17 @@ final class ArcanistBaseCommitParser {
}
}
private function updateLegacyRuleName($name) {
$updated = array(
'global' => 'user',
'args' => 'runtime',
);
$new_name = idx($updated, $name);
if ($new_name) {
$this->log("translating legacy name '$name' to '$new_name'");
return $new_name;
}
return $name;
}
}

View file

@ -16,7 +16,7 @@ final class ArcanistBaseCommitParserTestCase extends ArcanistTestCase {
'Literal',
'xyz',
array(
'args' => 'literal:xyz',
'runtime' => 'literal:xyz',
));
}
@ -32,8 +32,8 @@ final class ArcanistBaseCommitParserTestCase extends ArcanistTestCase {
array(
'local' => 'literal:n',
'project' => 'literal:n',
'args' => 'literal:y',
'global' => 'literal:n',
'runtime' => 'literal:y',
'user' => 'literal:n',
));
$this->assertCommit(
@ -42,7 +42,7 @@ final class ArcanistBaseCommitParserTestCase extends ArcanistTestCase {
array(
'project' => 'literal:n',
'local' => 'literal:y',
'global' => 'literal:n',
'user' => 'literal:n',
));
$this->assertCommit(
@ -50,17 +50,42 @@ final class ArcanistBaseCommitParserTestCase extends ArcanistTestCase {
'y',
array(
'project' => 'literal:y',
'global' => 'literal:n',
'user' => 'literal:n',
));
$this->assertCommit(
'Order: Global',
'y',
array(
'global' => 'literal:y',
'user' => 'literal:y',
));
}
public function testLegacyRule() {
// 'global' should translate to 'user'
$this->assertCommit(
'"global" name',
'y',
array(
'runtime' => 'arc:global, arc:halt',
'local' => 'arc:halt',
'project' => 'arc:halt',
'user' => 'literal:y',
));
// 'args' should translate to 'runtime'
$this->assertCommit(
'"args" name',
'y',
array(
'runtime' => 'arc:project, literal:y',
'local' => 'arc:halt',
'project' => 'arc:args',
'user' => 'arc:halt',
));
}
public function testHalt() {
// 'arc:halt' should halt all processing.
@ -69,7 +94,7 @@ final class ArcanistBaseCommitParserTestCase extends ArcanistTestCase {
'Halt',
null,
array(
'args' => 'arc:halt',
'runtime' => 'arc:halt',
'local' => 'literal:xyz',
));
}
@ -82,17 +107,17 @@ final class ArcanistBaseCommitParserTestCase extends ArcanistTestCase {
'Yield',
'xyz',
array(
'args' => 'arc:yield, literal:abc',
'runtime' => 'arc:yield, literal:abc',
'local' => 'literal:xyz',
));
// This one should return to 'args' after exhausting 'local'.
// This one should return to 'runtime' after exhausting 'local'.
$this->assertCommit(
'Yield + Return',
'abc',
array(
'args' => 'arc:yield, literal:abc',
'runtime' => 'arc:yield, literal:abc',
'local' => 'arc:skip',
));
}
@ -105,25 +130,25 @@ final class ArcanistBaseCommitParserTestCase extends ArcanistTestCase {
'Jump',
'abc',
array(
'args' => 'arc:project, arc:halt',
'runtime' => 'arc:project, arc:halt',
'local' => 'literal:abc',
'project' => 'arc:global, arc:halt',
'global' => 'arc:local, arc:halt',
'project' => 'arc:user, arc:halt',
'user' => 'arc:local, arc:halt',
));
}
public function testJumpReturn() {
// After jumping to project, we should return to 'args'.
// After jumping to project, we should return to 'runtime'.
$this->assertCommit(
'Jump Return',
'xyz',
array(
'args' => 'arc:project, literal:xyz',
'runtime' => 'arc:project, literal:xyz',
'local' => 'arc:halt',
'project' => '',
'global' => 'arc:halt',
'user' => 'arc:halt',
));
}
@ -140,8 +165,11 @@ final class ArcanistBaseCommitParserTestCase extends ArcanistTestCase {
// isolation for repository-oriented test cases.
$root = dirname(phutil_get_library_root('arcanist'));
$copy = ArcanistWorkingCopyIdentity::newFromPath($root);
$repo = ArcanistRepositoryAPI::newAPIFromWorkingCopyIdentity($copy);
$working_copy = ArcanistWorkingCopyIdentity::newFromPath($root);
$configuration_manager = new ArcanistConfigurationManager();
$configuration_manager->setWorkingCopyIdentity($working_copy);
$repo = ArcanistRepositoryAPI::newAPIFromConfigurationManager(
$configuration_manager);
return new ArcanistBaseCommitParser($repo);
}

View file

@ -78,8 +78,11 @@ final class ArcanistBundleTestCase extends ArcanistTestCase {
$fixture_path = $fixture->getPath();
$working_copy = ArcanistWorkingCopyIdentity::newFromPath($fixture_path);
$repository_api = ArcanistRepositoryAPI::newAPIFromWorkingCopyIdentity(
$working_copy);
$configuration_manager = new ArcanistConfigurationManager();
$configuration_manager->setWorkingCopyIdentity($working_copy);
$repository_api = ArcanistRepositoryAPI::newAPIFromConfigurationManager(
$configuration_manager);
$repository_api->setBaseCommitArgumentRules('arc:this');
$diff = $repository_api->getFullGitDiff();

View file

@ -197,7 +197,7 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
}
if ($this->getBaseCommitArgumentRules() ||
$this->getWorkingCopyIdentity()->getConfigFromAnySource('base')) {
$this->getConfigurationManager()->getConfigFromAnySource('base')) {
$base = $this->resolveBaseCommit();
if (!$base) {
throw new ArcanistUsageException(

View file

@ -28,7 +28,7 @@ abstract class ArcanistRepositoryAPI {
protected $path;
protected $diffLinesOfContext = 0x7FFF;
private $baseCommitExplanation = '???';
private $workingCopyIdentity;
private $configurationManager;
private $baseCommitArgumentRules;
private $uncommittedStatusCache;
@ -49,11 +49,22 @@ abstract class ArcanistRepositoryAPI {
}
public function getWorkingCopyIdentity() {
return $this->workingCopyIdentity;
return $this->configurationManager->getWorkingCopyIdentity();
}
public static function newAPIFromWorkingCopyIdentity(
ArcanistWorkingCopyIdentity $working_copy) {
public function getConfigurationManager() {
return $this->configurationManager;
}
public static function newAPIFromConfigurationManager(
ArcanistConfigurationManager $configuration_manager) {
$working_copy = $configuration_manager->getWorkingCopyIdentity();
if (!$working_copy) {
throw new Exception(
"Trying to create a RepositoryApi without a working copy");
}
$root = $working_copy->getProjectRoot();
@ -65,7 +76,7 @@ abstract class ArcanistRepositoryAPI {
if (Filesystem::pathExists($root.'/.hg')) {
$api = new ArcanistMercurialAPI($root);
$api->workingCopyIdentity = $working_copy;
$api->configurationManager = $configuration_manager;
return $api;
}
@ -78,7 +89,7 @@ abstract class ArcanistRepositoryAPI {
}
$api = new ArcanistGitAPI($root);
$api->workingCopyIdentity = $working_copy;
$api->configurationManager = $configuration_manager;
return $api;
}
@ -86,7 +97,7 @@ abstract class ArcanistRepositoryAPI {
foreach (Filesystem::walkToRoot($root) as $dir) {
if (Filesystem::pathExists($dir . '/.svn')) {
$api = new ArcanistSubversionAPI($root);
$api->workingCopyIdentity = $working_copy;
$api->configurationManager = $configuration_manager;
return $api;
}
}
@ -643,19 +654,19 @@ abstract class ArcanistRepositoryAPI {
}
public function resolveBaseCommit() {
$working_copy = $this->getWorkingCopyIdentity();
$global_config = ArcanistBaseWorkflow::readGlobalArcConfig();
$system_config = ArcanistBaseWorkflow::readSystemArcConfig();
$base_commit_rules = array(
'runtime' => $this->getBaseCommitArgumentRules(),
'local' => '',
'project' => '',
'user' => '',
'system' => '',
);
$all_sources = $this->configurationManager->getConfigFromAllSources('base');
$base_commit_rules = $all_sources + $base_commit_rules;
$parser = new ArcanistBaseCommitParser($this);
$commit = $parser->resolveBaseCommit(
array(
'args' => $this->getBaseCommitArgumentRules(),
'local' => $working_copy->getLocalConfig('base', ''),
'project' => $working_copy->getConfig('base', ''),
'global' => idx($global_config, 'base', ''),
'system' => idx($system_config, 'base', ''),
));
$commit = $parser->resolveBaseCommit($base_commit_rules);
return $commit;
}

View file

@ -32,9 +32,11 @@ final class ArcanistRepositoryAPIStateTestCase extends ArcanistTestCase {
$fixture_path = $fixture->getPath();
$working_copy = ArcanistWorkingCopyIdentity::newFromPath($fixture_path);
$configuration_manager = new ArcanistConfigurationManager();
$configuration_manager->setWorkingCopyIdentity($working_copy);
$api = ArcanistRepositoryAPI::newAPIFromConfigurationManager(
$configuration_manager);
$api = ArcanistRepositoryAPI::newAPIFromWorkingCopyIdentity(
$working_copy);
$api->setBaseCommitArgumentRules('arc:this');
if ($api instanceof ArcanistSubversionAPI) {

View file

@ -59,27 +59,29 @@ EOTEXT
);
}
public static function getAliases($working_copy) {
$working_copy_config_aliases = $working_copy->getConfig('aliases');
public static function getAliases(
ArcanistConfigurationManager $configuration_manager) {
$working_copy_config_aliases =
$configuration_manager->getProjectConfig('aliases');
if (!$working_copy_config_aliases) {
$working_copy_config_aliases = array();
}
$user_config_aliases =
idx(self::readUserConfigurationFile(), 'aliases', array());
$user_config_aliases = idx(
$configuration_manager->readUserConfigurationFile(),
'aliases',
array());
return $user_config_aliases + $working_copy_config_aliases;
}
private function writeAliases(array $aliases) {
$config = self::readUserConfigurationFile();
$config = $this->getConfigurationManager()->readUserConfigurationFile();
$config['aliases'] = $aliases;
self::writeUserConfigurationFile($config);
$this->getConfigurationManager()->writeUserConfigurationFile($config);
}
public function run() {
// We might not be in a working directory, so we don't want to require a
// working copy identity here.
$working_copy = ArcanistWorkingCopyIdentity::newFromPath(getcwd());
$aliases = self::getAliases($working_copy);
$aliases = self::getAliases($this->getConfigurationManager());
$argv = $this->getArgument('argv');
if (count($argv) == 0) {
@ -140,9 +142,9 @@ EOTEXT
$command,
ArcanistConfiguration $config,
array $argv,
ArcanistWorkingCopyIdentity $working_copy) {
ArcanistConfigurationManager $configuration_manager) {
$aliases = ArcanistAliasWorkflow::getAliases($working_copy);
$aliases = ArcanistAliasWorkflow::getAliases($configuration_manager);
if (!isset($aliases[$command])) {
return array(null, $argv);
}

View file

@ -55,6 +55,7 @@ abstract class ArcanistBaseWorkflow extends Phobject {
private $userPHID;
private $userName;
private $repositoryAPI;
private $configurationManager;
private $workingCopy;
private $arguments;
private $passedArguments;
@ -182,8 +183,8 @@ abstract class ArcanistBaseWorkflow extends Phobject {
$this->conduit->setTimeout($this->conduitTimeout);
}
$user = $this->getConfigFromWhateverSourceAvailiable('http.basicauth.user');
$pass = $this->getConfigFromWhateverSourceAvailiable('http.basicauth.pass');
$user = $this->getConfigFromAnySource('http.basicauth.user');
$pass = $this->getConfigFromAnySource('http.basicauth.pass');
if ($user !== null && $pass !== null) {
$this->conduit->setBasicAuthCredentials($user, $pass);
}
@ -191,21 +192,8 @@ abstract class ArcanistBaseWorkflow extends Phobject {
return $this;
}
final public function getConfigFromWhateverSourceAvailiable($key) {
if ($this->requiresWorkingCopy()) {
$working_copy = $this->getWorkingCopy();
return $working_copy->getConfigFromAnySource($key);
} else {
$global_config = self::readGlobalArcConfig();
$pval = idx($global_config, $key);
if ($pval === null) {
$system_config = self::readSystemArcConfig();
$pval = idx($system_config, $key);
}
return $pval;
}
final public function getConfigFromAnySource($key) {
return $this->configurationManager->getConfigFromAnySource($key);
}
@ -513,6 +501,17 @@ abstract class ArcanistBaseWorkflow extends Phobject {
return $this->arcanistConfiguration;
}
public function setConfigurationManager(
ArcanistConfigurationManager $arcanist_configuration_manager) {
$this->configurationManager = $arcanist_configuration_manager;
return $this;
}
public function getConfigurationManager() {
return $this->configurationManager;
}
public function requiresWorkingCopy() {
return false;
}
@ -565,6 +564,7 @@ abstract class ArcanistBaseWorkflow extends Phobject {
$workflow = $arc_config->buildWorkflow($command);
$workflow->setParentWorkflow($this);
$workflow->setCommand($command);
$workflow->setConfigurationManager($this->getConfigurationManager());
if ($this->repositoryAPI) {
$workflow->setRepositoryAPI($this->repositoryAPI);
@ -732,13 +732,14 @@ abstract class ArcanistBaseWorkflow extends Phobject {
}
public function getWorkingCopy() {
if (!$this->workingCopy) {
$working_copy = $this->getConfigurationManager()->getWorkingCopyIdentity();
if (!$working_copy) {
$workflow = get_class($this);
throw new Exception(
"This workflow ('{$workflow}') requires a working copy, override ".
"requiresWorkingCopy() to return true.");
}
return $this->workingCopy;
return $working_copy;
}
public function setWorkingCopy(
@ -856,7 +857,7 @@ abstract class ArcanistBaseWorkflow extends Phobject {
$api->addToCommit($unstaged);
$must_commit += array_flip($unstaged);
} else {
$permit_autostash = $this->getWorkingCopy()->getConfigFromAnySource(
$permit_autostash = $this->getConfigFromAnySource(
'arc.autostash',
false);
if ($permit_autostash && $api->canStashChanges()) {
@ -1167,114 +1168,6 @@ abstract class ArcanistBaseWorkflow extends Phobject {
return $argv;
}
public static function getSystemArcConfigLocation() {
if (phutil_is_windows()) {
return Filesystem::resolvePath(
'Phabricator/Arcanist/config',
getenv('ProgramData'));
} else {
return '/etc/arcconfig';
}
}
public static function readSystemArcConfig() {
$system_config = array();
$system_config_path = self::getSystemArcConfigLocation();
if (Filesystem::pathExists($system_config_path)) {
$file = Filesystem::readFile($system_config_path);
if ($file) {
$system_config = json_decode($file, true);
}
}
return $system_config;
}
public static function getUserConfigurationFileLocation() {
if (phutil_is_windows()) {
return getenv('APPDATA').'/.arcrc';
} else {
return getenv('HOME').'/.arcrc';
}
}
public static function readUserConfigurationFile() {
$user_config = array();
$user_config_path = self::getUserConfigurationFileLocation();
if (Filesystem::pathExists($user_config_path)) {
if (!phutil_is_windows()) {
$mode = fileperms($user_config_path);
if (!$mode) {
throw new Exception("Unable to get perms of '{$user_config_path}'!");
}
if ($mode & 0177) {
// Mode should allow only owner access.
$prompt = "File permissions on your ~/.arcrc are too open. ".
"Fix them by chmod'ing to 600?";
if (!phutil_console_confirm($prompt, $default_no = false)) {
throw new ArcanistUsageException("Set ~/.arcrc to file mode 600.");
}
execx('chmod 600 %s', $user_config_path);
// Drop the stat cache so we don't read the old permissions if
// we end up here again. If we don't do this, we may prompt the user
// to fix permissions multiple times.
clearstatcache();
}
}
$user_config_data = Filesystem::readFile($user_config_path);
$user_config = json_decode($user_config_data, true);
if (!is_array($user_config)) {
throw new ArcanistUsageException(
"Your '~/.arcrc' file is not a valid JSON file.");
}
}
return $user_config;
}
public static function writeUserConfigurationFile($config) {
$json_encoder = new PhutilJSON();
$json = $json_encoder->encodeFormatted($config);
$path = self::getUserConfigurationFileLocation();
Filesystem::writeFile($path, $json);
if (!phutil_is_windows()) {
execx('chmod 600 %s', $path);
}
}
public static function readGlobalArcConfig() {
return idx(self::readUserConfigurationFile(), 'config', array());
}
public static function writeGlobalArcConfig(array $options) {
$config = self::readUserConfigurationFile();
$config['config'] = $options;
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
* to be piped somewhere aren't disrupted.
@ -1288,9 +1181,8 @@ abstract class ArcanistBaseWorkflow extends Phobject {
protected function isHistoryImmutable() {
$repository_api = $this->getRepositoryAPI();
$working_copy = $this->getWorkingCopy();
$config = $working_copy->getConfigFromAnySource('history.immutable');
$config = $this->getConfigFromAnySource('history.immutable');
if ($config !== null) {
return $config;
}
@ -1530,7 +1422,7 @@ abstract class ArcanistBaseWorkflow extends Phobject {
protected function newInteractiveEditor($text) {
$editor = new PhutilInteractiveEditor($text);
$preferred = $this->getWorkingCopy()->getConfigFromAnySource('editor');
$preferred = $this->getConfigFromAnySource('editor');
if ($preferred) {
$editor->setPreferredEditor($preferred);
}

View file

@ -123,7 +123,7 @@ EOTEXT
}
private function getBrowserCommand() {
$config = $this->getWorkingCopy()->getConfigFromAnySource('browser');
$config = $this->getConfigFromAnySource('browser');
if ($config) {
return $config;
}

View file

@ -42,11 +42,16 @@ EOTEXT
$settings = new ArcanistSettings();
$configuration_manager = $this->getConfigurationManager();
$configs = array(
'system' => self::readSystemArcConfig(),
'global' => self::readGlobalArcConfig(),
'project' => $this->getWorkingCopy()->getProjectConfig(),
'local' => $this->readLocalArcConfig(),
ArcanistConfigurationManager::CONFIG_SOURCE_SYSTEM =>
$configuration_manager->readSystemArcConfig(),
ArcanistConfigurationManager::CONFIG_SOURCE_USER =>
$configuration_manager->readUserArcConfig(),
ArcanistConfigurationManager::CONFIG_SOURCE_PROJECT =>
$this->getWorkingCopy()->readProjectConfig(),
ArcanistConfigurationManager::CONFIG_SOURCE_LOCAL =>
$configuration_manager->readLocalArcConfig(),
);
if ($argv) {
@ -65,7 +70,7 @@ EOTEXT
}
foreach ($configs as $name => $config) {
switch ($name) {
case 'project':
case ArcanistConfigurationManager::CONFIG_SOURCE_PROJECT:
// Respect older names in project config.
$val = $this->getWorkingCopy()->getConfig($key);
break;

View file

@ -53,10 +53,11 @@ EOTEXT
$uri = $this->determineConduitURI();
$this->setConduitURI($uri);
$configuration_manager = $this->getConfigurationManager();
echo "Installing certificate for '{$uri}'...\n";
$config = self::readUserConfigurationFile();
$config = $configuration_manager->readUserConfigurationFile();
echo "Trying to connect to server...\n";
$conduit = $this->establishConduit()->getConduit();
@ -106,7 +107,7 @@ EOTEXT
);
echo "Writing ~/.arcrc...\n";
self::writeUserConfigurationFile($config);
$configuration_manager->writeUserConfigurationFile($config);
echo phutil_console_format(
"<bg:green>** SUCCESS! **</bg> Certificate installed.\n");

View file

@ -242,8 +242,7 @@ EOTEXT
$this->branch = head($branch);
$this->keepBranch = $this->getArgument('keep-branch');
$working_copy = $this->getWorkingCopy();
$update_strategy = $working_copy->getConfigFromAnySource(
$update_strategy = $this->getConfigFromAnySource(
'arc.land.update.default',
'merge');
$this->shouldUpdateWithRebase = $update_strategy == 'rebase';
@ -261,7 +260,7 @@ EOTEXT
$onto_default = $this->isGit ? 'master' : 'default';
$onto_default = nonempty(
$working_copy->getConfigFromAnySource('arc.land.onto.default'),
$this->getConfigFromAnySource('arc.land.onto.default'),
$onto_default);
$this->onto = $this->getArgument('onto', $onto_default);
$this->ontoType = $this->getBranchType($this->onto);

View file

@ -182,10 +182,11 @@ EOTEXT
public function run() {
$console = PhutilConsole::getConsole();
$working_copy = $this->getWorkingCopy();
$configuration_manager = $this->getConfigurationManager();
$engine = $this->getArgument('engine');
if (!$engine) {
$engine = $working_copy->getConfigFromAnySource('lint.engine');
$engine = $configuration_manager->getConfigFromAnySource('lint.engine');
}
if (!$engine) {
@ -210,7 +211,7 @@ EOTEXT
"flag lints every file.");
}
if ($use_cache === null) {
$use_cache = (bool)$working_copy->getConfigFromAnySource(
$use_cache = (bool)$configuration_manager->getConfigFromAnySource(
'arc.lint.cache',
false);
}
@ -260,7 +261,7 @@ EOTEXT
$engine = newv($engine, array());
$this->engine = $engine;
$engine->setWorkingCopy($working_copy);
$engine->setWorkingCopy($working_copy); // todo setConfig?
$engine->setMinimumSeverity(
$this->getArgument('severity', self::DEFAULT_SEVERITY));

View file

@ -23,12 +23,12 @@ EOTEXT
Supports: cli
Sets an arc configuration option.
Options are either global (apply to all arc commands you invoke
Options are either user (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__
copy). By default, user configuration is written. Use __--local__
to write local configuration.
Global values are written to '~/.arcrc' on Linux and Mac OS X, and an
User 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.
@ -44,7 +44,7 @@ EOTEXT
'help' => 'Show available configuration values.',
),
'local' => array(
'help' => 'Set a local config value instead of a global one',
'help' => 'Set a local config value instead of a user one',
),
'*' => 'argv',
);
@ -64,15 +64,16 @@ EOTEXT
throw new ArcanistUsageException(
"Specify a key and a value, or --show.");
}
$configuration_manager = $this->getConfigurationManager();
$is_local = $this->getArgument('local');
if ($is_local) {
$config = $this->readLocalArcConfig();
$config = $configuration_manager->readLocalArcConfig();
$which = 'local';
} else {
$config = self::readGlobalArcConfig();
$which = 'global';
$config = $configuration_manager->readUserArcConfig();
$which = 'user';
}
$key = $argv[0];
@ -88,9 +89,9 @@ EOTEXT
if (!strlen($val)) {
unset($config[$key]);
if ($is_local) {
$this->writeLocalArcConfig($config);
$configuration_manager->writeLocalArcConfig($config);
} else {
self::writeGlobalArcConfig($config);
$configuration_manager->writeUserArcConfig($config);
}
$old = $settings->formatConfigValueForDisplay($key, $old);
@ -105,9 +106,9 @@ EOTEXT
$config[$key] = $val;
if ($is_local) {
$this->writeLocalArcConfig($config);
$configuration_manager->writeLocalArcConfig($config);
} else {
self::writeGlobalArcConfig($config);
$configuration_manager->writeUserArcConfig($config);
}
$val = $settings->formatConfigValueForDisplay($key, $val);
@ -124,7 +125,7 @@ EOTEXT
}
private function show() {
$config = self::readGlobalArcConfig();
$config = $this->getConfigurationManager()->readUserArcConfig();
$settings = new ArcanistSettings();
@ -143,7 +144,7 @@ EOTEXT
echo phutil_console_format(" Example: %s\n", $example);
}
if (strlen($value)) {
echo phutil_console_format(" Global Setting: %s\n", $value);
echo phutil_console_format(" User Setting: %s\n", $value);
}
echo "\n";
echo phutil_console_wrap($help, 4);

View file

@ -60,8 +60,11 @@ EOTEXT
// but commands can raise more detailed errors.
$working_copy = ArcanistWorkingCopyIdentity::newFromPath(getcwd());
if ($working_copy->getProjectRoot()) {
$repository_api = ArcanistRepositoryAPI::newAPIFromWorkingCopyIdentity(
$working_copy);
$configuration_manager = $this->getConfigurationManager();
$configuration_manager->setWorkingCopyIdentity($working_copy);
$repository_api = ArcanistRepositoryAPI::newAPIFromConfigurationManager(
$configuration_manager);
$vcs = $repository_api->getSourceControlSystemName();
}
@ -88,8 +91,8 @@ EOTEXT
}
// Also permit autocompletion of "arc alias" commands.
foreach (
ArcanistAliasWorkflow::getAliases($working_copy) as $key => $value) {
$aliases = ArcanistAliasWorkflow::getAliases($configuration_manager);
foreach ($aliases as $key => $value) {
$complete[] = $key;
}
@ -102,7 +105,7 @@ EOTEXT
$argv[1],
$arc_config,
array_slice($argv, 2),
$working_copy);
$configuration_manager);
if ($new_command) {
$workflow = $arc_config->buildWorkflow($new_command);
}

View file

@ -173,6 +173,7 @@ EOTEXT
$working_copy = ArcanistWorkingCopyIdentity::newFromRootAndConfigFile(
$project_root,
$this->getConfigurationManager(),
$config,
$config_file." (svnlook: {$transaction} {$repository})");

View file

@ -115,7 +115,7 @@ EOTEXT
$engine_class = $this->getArgument(
'engine',
$working_copy->getConfigFromAnySource('unit.engine'));
$this->getConfigurationManager()->getConfigFromAnySource('unit.engine'));
if (!$engine_class) {
throw new ArcanistNoEngineException(

View file

@ -47,8 +47,10 @@ EOTEXT
$working_copy = ArcanistWorkingCopyIdentity::newFromPath($root);
$repository_api = ArcanistRepositoryAPI::newAPIFromWorkingCopyIdentity(
$working_copy);
$configuration_manager = clone $this->getConfigurationManager();
$configuration_manager->setWorkingCopyIndentity($working_copy);
$repository_api = ArcanistRepositoryAPI::newAPIFromConfigurationManager(
$configuration_manager);
$this->setRepositoryAPI($repository_api);

View file

@ -12,11 +12,11 @@ final class ArcanistWorkingCopyIdentity {
protected $localConfig;
protected $projectConfig;
protected $runtimeConfig;
protected $projectRoot;
protected $localMetaDir;
public static function newDummyWorkingCopy() {
return new ArcanistWorkingCopyIdentity('/', array());
return new ArcanistWorkingCopyIdentity('/', array());
}
public static function newFromPath($path) {
@ -93,7 +93,7 @@ final class ArcanistWorkingCopyIdentity {
$this->projectRoot = $root;
$this->projectConfig = $config;
$this->localConfig = array();
$this->runtimeConfig = array();
$this->localMetaDir = null;
$vc_dirs = array(
'.git',
@ -107,15 +107,11 @@ final class ArcanistWorkingCopyIdentity {
$this->projectRoot);
if (Filesystem::pathExists($meta_path)) {
$found_meta_dir = true;
$this->localMetaDir = $meta_path;
$local_path = Filesystem::resolvePath(
'arc/config',
$meta_path);
if (Filesystem::pathExists($local_path)) {
$file = Filesystem::readFile($local_path);
if ($file) {
$this->localConfig = json_decode($file, true);
}
}
$this->localConfig = $this->readLocalArcConfig();
break;
}
}
@ -123,14 +119,15 @@ final class ArcanistWorkingCopyIdentity {
if (!$found_meta_dir) {
// Try for a single higher-level .svn directory as used by svn 1.7+
foreach (Filesystem::walkToRoot($this->projectRoot) as $parent_path) {
$meta_path = Filesystem::resolvePath(
'.svn',
$parent_path);
$local_path = Filesystem::resolvePath(
'.svn/arc/config',
$parent_path);
if (Filesystem::pathExists($local_path)) {
$file = Filesystem::readFile($local_path);
if ($file) {
$this->localConfig = json_decode($file, true);
}
$this->localMetaDir = $meta_path;
$this->localConfig = $this->readLocalArcConfig();
}
}
}
@ -149,14 +146,10 @@ final class ArcanistWorkingCopyIdentity {
return $this->projectRoot.'/'.$to_file;
}
public function getConduitURI() {
return $this->getConfig('conduit_uri');
}
/* -( Config )------------------------------------------------------------- */
public function getProjectConfig() {
public function readProjectConfig() {
return $this->projectConfig;
}
@ -195,7 +188,6 @@ final class ArcanistWorkingCopyIdentity {
return $pval;
}
/**
* Read a configuration directive from local configuration. This
* reads ONLY the per-working copy configuration,
@ -210,69 +202,46 @@ final class ArcanistWorkingCopyIdentity {
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
* 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.
* @return wild Value, or default value if not found.
*
* @task config
*/
public function getConfigFromAnySource($key, $default = null) {
$settings = new ArcanistSettings();
// try runtime config first
$pval = idx($this->runtimeConfig, $key);
// try local config
if ($pval === null) {
$pval = $this->getLocalConfig($key);
public function readLocalArcConfig() {
if (strlen($this->localMetaDir)) {
$local_path = Filesystem::resolvePath(
'arc/config',
$this->localMetaDir);
if (Filesystem::pathExists($local_path)) {
$file = Filesystem::readFile($local_path);
if ($file) {
return json_decode($file, true);
}
}
}
// then per-project config
if ($pval === null) {
$pval = $this->getConfig($key);
}
// now try global (i.e. user-level) config
if ($pval === null) {
$global_config = ArcanistBaseWorkflow::readGlobalArcConfig();
$pval = idx($global_config, $key);
}
// Finally, try system-level config.
if ($pval === null) {
$system_config = ArcanistBaseWorkflow::readSystemArcConfig();
$pval = idx($system_config, $key);
}
if ($pval === null) {
$pval = $default;
} else {
$pval = $settings->willReadValue($key, $pval);
}
return $pval;
return array();
}
/**
* Sets a runtime config value that takes precedence over any static
* config values.
*
* @param key Key to set.
* @param value The value of the key.
*
* @task config
*/
public function setRuntimeConfig($key, $value) {
$this->runtimeConfig[$key] = $value;
public function writeLocalArcConfig(array $config) {
$dir = $this->localMetaDir;
if (!strlen($dir)) {
return false;
}
return $this;
if (!Filesystem::pathExists($dir)) {
try {
Filesystem::createDirectory($dir);
} catch (Exception $ex) {
return false;
}
}
$json_encoder = new PhutilJSON();
$json = $json_encoder->encodeFormatted($config);
$config_file = Filesystem::resolvePath('arc/config', $dir);
try {
Filesystem::writeFile($config_file, $json);
} catch (FilesystemException $ex) {
return false;
}
return true;
}
}