1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-22 14:52:40 +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'); array_unshift($args, 'help');
} }
$global_config = ArcanistBaseWorkflow::readGlobalArcConfig(); $configuration_manager = new ArcanistConfigurationManager();
$system_config = ArcanistBaseWorkflow::readSystemArcConfig();
$global_config = $configuration_manager->readUserArcConfig();
$system_config = $configuration_manager->readSystemArcConfig();
if ($skip_arcconfig) { if ($skip_arcconfig) {
$working_copy = ArcanistWorkingCopyIdentity::newDummyWorkingCopy(); $working_copy = ArcanistWorkingCopyIdentity::newDummyWorkingCopy();
} else { } else {
$working_copy = $working_copy =
ArcanistWorkingCopyIdentity::newFromPath($working_directory); ArcanistWorkingCopyIdentity::newFromPath($working_directory);
} }
$configuration_manager->setWorkingCopyIdentity($working_copy);
reenter_if_this_is_arcanist_or_libphutil( reenter_if_this_is_arcanist_or_libphutil(
$console, $console,
@ -133,7 +136,7 @@ try {
$working_copy); $working_copy);
} }
$user_config = ArcanistBaseWorkflow::readUserConfigurationFile(); $user_config = $configuration_manager->readUserConfigurationFile();
$config_class = $working_copy->getConfig('arcanist_configuration'); $config_class = $working_copy->getConfig('arcanist_configuration');
if ($config_class) { if ($config_class) {
@ -144,7 +147,12 @@ try {
$command = strtolower($args[0]); $command = strtolower($args[0]);
$args = array_slice($args, 1); $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->setArcanistConfiguration($config);
$workflow->setCommand($command); $workflow->setCommand($command);
$workflow->setWorkingDirectory($working_directory); $workflow->setWorkingDirectory($working_directory);
@ -182,14 +190,16 @@ try {
"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.");
} }
$workflow->setWorkingCopy($working_copy); $configuration_manager->setWorkingCopyIdentity($working_copy);
} }
if ($force_conduit) { if ($force_conduit) {
$conduit_uri = $force_conduit; $conduit_uri = $force_conduit;
} else { } else {
if ($working_copy->getConduitURI()) { $project_conduit_uri =
$conduit_uri = $working_copy->getConduitURI(); $configuration_manager->getProjectConfig('conduit_uri');
if ($project_conduit_uri) {
$conduit_uri = $project_conduit_uri;
} else { } else {
$conduit_uri = idx($global_config, 'default'); $conduit_uri = idx($global_config, 'default');
} }
@ -207,14 +217,15 @@ try {
$workflow->setConduitURI($conduit_uri); $workflow->setConduitURI($conduit_uri);
// Apply global CA bundle from configs. // 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 = Filesystem::resolvePath(
$ca_bundle, $working_copy->getProjectRoot()); $ca_bundle, $working_copy->getProjectRoot());
HTTPSFuture::setGlobalCABundleFromPath($ca_bundle); HTTPSFuture::setGlobalCABundleFromPath($ca_bundle);
} }
$blind_key = 'https.blindly-trust-domains'; $blind_key = 'https.blindly-trust-domains';
$blind_trust = $working_copy->getConfigFromAnySource($blind_key); $blind_trust = $configuration_manager->getConfigFromAnySource($blind_key);
if ($blind_trust) { if ($blind_trust) {
HTTPSFuture::setBlindlyTrustDomains($blind_trust); HTTPSFuture::setBlindlyTrustDomains($blind_trust);
} }
@ -268,12 +279,13 @@ try {
} }
if ($need_repository_api || ($want_repository_api && $working_copy)) { if ($need_repository_api || ($want_repository_api && $working_copy)) {
$repository_api = ArcanistRepositoryAPI::newAPIFromWorkingCopyIdentity( $repository_api = ArcanistRepositoryAPI::newAPIFromConfigurationManager(
$working_copy); $configuration_manager);
$workflow->setRepositoryAPI($repository_api); $workflow->setRepositoryAPI($repository_api);
} }
$listeners = $working_copy->getConfigFromAnySource('events.listeners'); $listeners = $configuration_manager->getConfigFromAnySource(
'events.listeners');
if ($listeners) { if ($listeners) {
foreach ($listeners as $listener) { foreach ($listeners as $listener) {
$console->writeLog( $console->writeLog(

View file

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

View file

@ -78,7 +78,7 @@ class ArcanistConfiguration {
final public function selectWorkflow( final public function selectWorkflow(
&$command, &$command,
array &$args, array &$args,
ArcanistWorkingCopyIdentity $working_copy, ArcanistConfigurationManager $configuration_manager,
PhutilConsole $console) { PhutilConsole $console) {
// First, try to build a workflow with the exact name provided. We always // 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 // and substitute it. We do this only after trying to resolve the workflow
// normally to prevent you from doing silly things like aliasing 'alias' // normally to prevent you from doing silly things like aliasing 'alias'
// to something else. // to something else.
$aliases = ArcanistAliasWorkflow::getAliases($working_copy); $aliases = ArcanistAliasWorkflow::getAliases($configuration_manager);
list($new_command, $args) = ArcanistAliasWorkflow::resolveAliases( list($new_command, $args) = ArcanistAliasWorkflow::resolveAliases(
$command, $command,
$this, $this,
$args, $args,
$working_copy); $configuration_manager);
$full_alias = idx($aliases, $command, array()); $full_alias = idx($aliases, $command, array());
$full_alias = implode(' ', $full_alias); $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 * @task config
*/ */
private function getConfiguredScript() { private function getConfiguredScript() {
$working_copy = $this->getEngine()->getWorkingCopy();
$key = 'linter.scriptandregex.script'; $key = 'linter.scriptandregex.script';
$config = $working_copy->getConfigFromAnySource($key); $config = $this->getConfigFromAnySource($key);
if (!$config) { if (!$config) {
throw new ArcanistUsageException( throw new ArcanistUsageException(
@ -373,9 +372,8 @@ final class ArcanistScriptAndRegexLinter extends ArcanistLinter {
* @task config * @task config
*/ */
private function getConfiguredRegex() { private function getConfiguredRegex() {
$working_copy = $this->getEngine()->getWorkingCopy();
$key = 'linter.scriptandregex.regex'; $key = 'linter.scriptandregex.regex';
$config = $working_copy->getConfigFromAnySource($key); $config = $this->getConfigFromAnySource($key);
if (!$config) { if (!$config) {
throw new ArcanistUsageException( throw new ArcanistUsageException(

View file

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

View file

@ -38,10 +38,10 @@ final class ArcanistBaseCommitParser {
public function resolveBaseCommit(array $specs) { public function resolveBaseCommit(array $specs) {
$specs += array( $specs += array(
'args' => '', 'runtime' => '',
'local' => '', 'local' => '',
'project' => '', 'project' => '',
'global' => '', 'user' => '',
'system' => '', 'system' => '',
); );
@ -50,10 +50,10 @@ final class ArcanistBaseCommitParser {
} }
$this->try = array( $this->try = array(
'args', 'runtime',
'local', 'local',
'project', 'project',
'global', 'user',
'system', 'system',
); );
@ -116,6 +116,8 @@ final class ArcanistBaseCommitParser {
* Handle resolving "arc:*" rules. * Handle resolving "arc:*" rules.
*/ */
private function resolveArcRule($rule, $name, $source) { private function resolveArcRule($rule, $name, $source) {
$name = $this->updateLegacyRuleName($name);
switch ($name) { switch ($name) {
case 'verbose': case 'verbose':
$this->verbose = true; $this->verbose = true;
@ -126,9 +128,9 @@ final class ArcanistBaseCommitParser {
$this->api->setBaseCommitExplanation($reason); $this->api->setBaseCommitExplanation($reason);
return phutil_console_prompt('Against which commit?'); return phutil_console_prompt('Against which commit?');
case 'local': case 'local':
case 'global': case 'user':
case 'project': case 'project':
case 'args': case 'runtime':
case 'system': case 'system':
// Push the other source on top of the list. // Push the other source on top of the list.
array_unshift($this->try, $name); 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', 'Literal',
'xyz', 'xyz',
array( array(
'args' => 'literal:xyz', 'runtime' => 'literal:xyz',
)); ));
} }
@ -32,8 +32,8 @@ final class ArcanistBaseCommitParserTestCase extends ArcanistTestCase {
array( array(
'local' => 'literal:n', 'local' => 'literal:n',
'project' => 'literal:n', 'project' => 'literal:n',
'args' => 'literal:y', 'runtime' => 'literal:y',
'global' => 'literal:n', 'user' => 'literal:n',
)); ));
$this->assertCommit( $this->assertCommit(
@ -42,7 +42,7 @@ final class ArcanistBaseCommitParserTestCase extends ArcanistTestCase {
array( array(
'project' => 'literal:n', 'project' => 'literal:n',
'local' => 'literal:y', 'local' => 'literal:y',
'global' => 'literal:n', 'user' => 'literal:n',
)); ));
$this->assertCommit( $this->assertCommit(
@ -50,17 +50,42 @@ final class ArcanistBaseCommitParserTestCase extends ArcanistTestCase {
'y', 'y',
array( array(
'project' => 'literal:y', 'project' => 'literal:y',
'global' => 'literal:n', 'user' => 'literal:n',
)); ));
$this->assertCommit( $this->assertCommit(
'Order: Global', 'Order: Global',
'y', 'y',
array( 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() { public function testHalt() {
// 'arc:halt' should halt all processing. // 'arc:halt' should halt all processing.
@ -69,7 +94,7 @@ final class ArcanistBaseCommitParserTestCase extends ArcanistTestCase {
'Halt', 'Halt',
null, null,
array( array(
'args' => 'arc:halt', 'runtime' => 'arc:halt',
'local' => 'literal:xyz', 'local' => 'literal:xyz',
)); ));
} }
@ -82,17 +107,17 @@ final class ArcanistBaseCommitParserTestCase extends ArcanistTestCase {
'Yield', 'Yield',
'xyz', 'xyz',
array( array(
'args' => 'arc:yield, literal:abc', 'runtime' => 'arc:yield, literal:abc',
'local' => 'literal:xyz', 'local' => 'literal:xyz',
)); ));
// This one should return to 'args' after exhausting 'local'. // This one should return to 'runtime' after exhausting 'local'.
$this->assertCommit( $this->assertCommit(
'Yield + Return', 'Yield + Return',
'abc', 'abc',
array( array(
'args' => 'arc:yield, literal:abc', 'runtime' => 'arc:yield, literal:abc',
'local' => 'arc:skip', 'local' => 'arc:skip',
)); ));
} }
@ -105,25 +130,25 @@ final class ArcanistBaseCommitParserTestCase extends ArcanistTestCase {
'Jump', 'Jump',
'abc', 'abc',
array( array(
'args' => 'arc:project, arc:halt', 'runtime' => 'arc:project, arc:halt',
'local' => 'literal:abc', 'local' => 'literal:abc',
'project' => 'arc:global, arc:halt', 'project' => 'arc:user, arc:halt',
'global' => 'arc:local, arc:halt', 'user' => 'arc:local, arc:halt',
)); ));
} }
public function testJumpReturn() { public function testJumpReturn() {
// After jumping to project, we should return to 'args'. // After jumping to project, we should return to 'runtime'.
$this->assertCommit( $this->assertCommit(
'Jump Return', 'Jump Return',
'xyz', 'xyz',
array( array(
'args' => 'arc:project, literal:xyz', 'runtime' => 'arc:project, literal:xyz',
'local' => 'arc:halt', 'local' => 'arc:halt',
'project' => '', 'project' => '',
'global' => 'arc:halt', 'user' => 'arc:halt',
)); ));
} }
@ -140,8 +165,11 @@ final class ArcanistBaseCommitParserTestCase extends ArcanistTestCase {
// isolation for repository-oriented test cases. // isolation for repository-oriented test cases.
$root = dirname(phutil_get_library_root('arcanist')); $root = dirname(phutil_get_library_root('arcanist'));
$copy = ArcanistWorkingCopyIdentity::newFromPath($root); $working_copy = ArcanistWorkingCopyIdentity::newFromPath($root);
$repo = ArcanistRepositoryAPI::newAPIFromWorkingCopyIdentity($copy); $configuration_manager = new ArcanistConfigurationManager();
$configuration_manager->setWorkingCopyIdentity($working_copy);
$repo = ArcanistRepositoryAPI::newAPIFromConfigurationManager(
$configuration_manager);
return new ArcanistBaseCommitParser($repo); return new ArcanistBaseCommitParser($repo);
} }

View file

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

View file

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

View file

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

View file

@ -32,9 +32,11 @@ final class ArcanistRepositoryAPIStateTestCase extends ArcanistTestCase {
$fixture_path = $fixture->getPath(); $fixture_path = $fixture->getPath();
$working_copy = ArcanistWorkingCopyIdentity::newFromPath($fixture_path); $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'); $api->setBaseCommitArgumentRules('arc:this');
if ($api instanceof ArcanistSubversionAPI) { if ($api instanceof ArcanistSubversionAPI) {

View file

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

View file

@ -55,6 +55,7 @@ abstract class ArcanistBaseWorkflow extends Phobject {
private $userPHID; private $userPHID;
private $userName; private $userName;
private $repositoryAPI; private $repositoryAPI;
private $configurationManager;
private $workingCopy; private $workingCopy;
private $arguments; private $arguments;
private $passedArguments; private $passedArguments;
@ -182,8 +183,8 @@ abstract class ArcanistBaseWorkflow extends Phobject {
$this->conduit->setTimeout($this->conduitTimeout); $this->conduit->setTimeout($this->conduitTimeout);
} }
$user = $this->getConfigFromWhateverSourceAvailiable('http.basicauth.user'); $user = $this->getConfigFromAnySource('http.basicauth.user');
$pass = $this->getConfigFromWhateverSourceAvailiable('http.basicauth.pass'); $pass = $this->getConfigFromAnySource('http.basicauth.pass');
if ($user !== null && $pass !== null) { if ($user !== null && $pass !== null) {
$this->conduit->setBasicAuthCredentials($user, $pass); $this->conduit->setBasicAuthCredentials($user, $pass);
} }
@ -191,21 +192,8 @@ abstract class ArcanistBaseWorkflow extends Phobject {
return $this; return $this;
} }
final public function getConfigFromWhateverSourceAvailiable($key) { final public function getConfigFromAnySource($key) {
if ($this->requiresWorkingCopy()) { return $this->configurationManager->getConfigFromAnySource($key);
$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;
}
} }
@ -513,6 +501,17 @@ abstract class ArcanistBaseWorkflow extends Phobject {
return $this->arcanistConfiguration; 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() { public function requiresWorkingCopy() {
return false; return false;
} }
@ -565,6 +564,7 @@ abstract class ArcanistBaseWorkflow extends Phobject {
$workflow = $arc_config->buildWorkflow($command); $workflow = $arc_config->buildWorkflow($command);
$workflow->setParentWorkflow($this); $workflow->setParentWorkflow($this);
$workflow->setCommand($command); $workflow->setCommand($command);
$workflow->setConfigurationManager($this->getConfigurationManager());
if ($this->repositoryAPI) { if ($this->repositoryAPI) {
$workflow->setRepositoryAPI($this->repositoryAPI); $workflow->setRepositoryAPI($this->repositoryAPI);
@ -732,13 +732,14 @@ abstract class ArcanistBaseWorkflow extends Phobject {
} }
public function getWorkingCopy() { public function getWorkingCopy() {
if (!$this->workingCopy) { $working_copy = $this->getConfigurationManager()->getWorkingCopyIdentity();
if (!$working_copy) {
$workflow = get_class($this); $workflow = get_class($this);
throw new Exception( throw new Exception(
"This workflow ('{$workflow}') requires a working copy, override ". "This workflow ('{$workflow}') requires a working copy, override ".
"requiresWorkingCopy() to return true."); "requiresWorkingCopy() to return true.");
} }
return $this->workingCopy; return $working_copy;
} }
public function setWorkingCopy( public function setWorkingCopy(
@ -856,7 +857,7 @@ abstract class ArcanistBaseWorkflow extends Phobject {
$api->addToCommit($unstaged); $api->addToCommit($unstaged);
$must_commit += array_flip($unstaged); $must_commit += array_flip($unstaged);
} else { } else {
$permit_autostash = $this->getWorkingCopy()->getConfigFromAnySource( $permit_autostash = $this->getConfigFromAnySource(
'arc.autostash', 'arc.autostash',
false); false);
if ($permit_autostash && $api->canStashChanges()) { if ($permit_autostash && $api->canStashChanges()) {
@ -1167,114 +1168,6 @@ abstract class ArcanistBaseWorkflow extends Phobject {
return $argv; 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 * Write a message to stderr so that '--json' flags or stdout which is meant
* to be piped somewhere aren't disrupted. * to be piped somewhere aren't disrupted.
@ -1288,9 +1181,8 @@ abstract class ArcanistBaseWorkflow extends Phobject {
protected function isHistoryImmutable() { protected function isHistoryImmutable() {
$repository_api = $this->getRepositoryAPI(); $repository_api = $this->getRepositoryAPI();
$working_copy = $this->getWorkingCopy();
$config = $working_copy->getConfigFromAnySource('history.immutable'); $config = $this->getConfigFromAnySource('history.immutable');
if ($config !== null) { if ($config !== null) {
return $config; return $config;
} }
@ -1530,7 +1422,7 @@ abstract class ArcanistBaseWorkflow extends Phobject {
protected function newInteractiveEditor($text) { protected function newInteractiveEditor($text) {
$editor = new PhutilInteractiveEditor($text); $editor = new PhutilInteractiveEditor($text);
$preferred = $this->getWorkingCopy()->getConfigFromAnySource('editor'); $preferred = $this->getConfigFromAnySource('editor');
if ($preferred) { if ($preferred) {
$editor->setPreferredEditor($preferred); $editor->setPreferredEditor($preferred);
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -23,12 +23,12 @@ EOTEXT
Supports: cli Supports: cli
Sets an arc configuration option. 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 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. 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 undisclosed location on Windows. Local values are written to an arc
directory under either .git, .hg, or .svn as appropriate. directory under either .git, .hg, or .svn as appropriate.
@ -44,7 +44,7 @@ EOTEXT
'help' => 'Show available configuration values.', 'help' => 'Show available configuration values.',
), ),
'local' => array( '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', '*' => 'argv',
); );
@ -64,15 +64,16 @@ EOTEXT
throw new ArcanistUsageException( throw new ArcanistUsageException(
"Specify a key and a value, or --show."); "Specify a key and a value, or --show.");
} }
$configuration_manager = $this->getConfigurationManager();
$is_local = $this->getArgument('local'); $is_local = $this->getArgument('local');
if ($is_local) { if ($is_local) {
$config = $this->readLocalArcConfig(); $config = $configuration_manager->readLocalArcConfig();
$which = 'local'; $which = 'local';
} else { } else {
$config = self::readGlobalArcConfig(); $config = $configuration_manager->readUserArcConfig();
$which = 'global'; $which = 'user';
} }
$key = $argv[0]; $key = $argv[0];
@ -88,9 +89,9 @@ EOTEXT
if (!strlen($val)) { if (!strlen($val)) {
unset($config[$key]); unset($config[$key]);
if ($is_local) { if ($is_local) {
$this->writeLocalArcConfig($config); $configuration_manager->writeLocalArcConfig($config);
} else { } else {
self::writeGlobalArcConfig($config); $configuration_manager->writeUserArcConfig($config);
} }
$old = $settings->formatConfigValueForDisplay($key, $old); $old = $settings->formatConfigValueForDisplay($key, $old);
@ -105,9 +106,9 @@ EOTEXT
$config[$key] = $val; $config[$key] = $val;
if ($is_local) { if ($is_local) {
$this->writeLocalArcConfig($config); $configuration_manager->writeLocalArcConfig($config);
} else { } else {
self::writeGlobalArcConfig($config); $configuration_manager->writeUserArcConfig($config);
} }
$val = $settings->formatConfigValueForDisplay($key, $val); $val = $settings->formatConfigValueForDisplay($key, $val);
@ -124,7 +125,7 @@ EOTEXT
} }
private function show() { private function show() {
$config = self::readGlobalArcConfig(); $config = $this->getConfigurationManager()->readUserArcConfig();
$settings = new ArcanistSettings(); $settings = new ArcanistSettings();
@ -143,7 +144,7 @@ EOTEXT
echo phutil_console_format(" Example: %s\n", $example); echo phutil_console_format(" Example: %s\n", $example);
} }
if (strlen($value)) { if (strlen($value)) {
echo phutil_console_format(" Global Setting: %s\n", $value); echo phutil_console_format(" User Setting: %s\n", $value);
} }
echo "\n"; echo "\n";
echo phutil_console_wrap($help, 4); echo phutil_console_wrap($help, 4);

View file

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

View file

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

View file

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

View file

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

View file

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