Page MenuHomePhabricator

D19695.diff
No OneTemporary

D19695.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -51,10 +51,10 @@
'ArcanistAbstractPrivateMethodXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistAbstractPrivateMethodXHPASTLinterRuleTestCase.php',
'ArcanistAliasFunctionXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistAliasFunctionXHPASTLinterRule.php',
'ArcanistAliasFunctionXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistAliasFunctionXHPASTLinterRuleTestCase.php',
- 'ArcanistAliasWorkflow' => 'toolset/ArcanistAliasWorkflow.php',
+ 'ArcanistAliasWorkflow' => 'toolset/workflow/ArcanistAliasWorkflow.php',
'ArcanistAmendWorkflow' => 'workflow/ArcanistAmendWorkflow.php',
'ArcanistAnoidWorkflow' => 'workflow/ArcanistAnoidWorkflow.php',
- 'ArcanistArcConfigurationEngineExtension' => 'config/ArcanistArcConfigurationEngineExtension.php',
+ 'ArcanistArcConfigurationEngineExtension' => 'config/arc/ArcanistArcConfigurationEngineExtension.php',
'ArcanistArcToolset' => 'toolset/ArcanistArcToolset.php',
'ArcanistArcWorkflow' => 'workflow/ArcanistArcWorkflow.php',
'ArcanistArrayCombineXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistArrayCombineXHPASTLinterRule.php',
@@ -134,9 +134,8 @@
'ArcanistConcatenationOperatorXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistConcatenationOperatorXHPASTLinterRule.php',
'ArcanistConcatenationOperatorXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistConcatenationOperatorXHPASTLinterRuleTestCase.php',
'ArcanistConduitCall' => 'conduit/ArcanistConduitCall.php',
- 'ArcanistConduitConfigurationEngineExtension' => 'config/ArcanistConduitConfigurationEngineExtension.php',
'ArcanistConduitEngine' => 'conduit/ArcanistConduitEngine.php',
- 'ArcanistConfigOption' => 'config/ArcanistConfigOption.php',
+ 'ArcanistConfigOption' => 'config/option/ArcanistConfigOption.php',
'ArcanistConfigurationDrivenLintEngine' => 'lint/engine/ArcanistConfigurationDrivenLintEngine.php',
'ArcanistConfigurationDrivenUnitTestEngine' => 'unit/engine/ArcanistConfigurationDrivenUnitTestEngine.php',
'ArcanistConfigurationEngine' => 'config/ArcanistConfigurationEngine.php',
@@ -144,6 +143,7 @@
'ArcanistConfigurationManager' => 'configuration/ArcanistConfigurationManager.php',
'ArcanistConfigurationSource' => 'config/source/ArcanistConfigurationSource.php',
'ArcanistConfigurationSourceList' => 'config/ArcanistConfigurationSourceList.php',
+ 'ArcanistConfigurationSourceValue' => 'config/ArcanistConfigurationSourceValue.php',
'ArcanistConsoleLintRenderer' => 'lint/renderer/ArcanistConsoleLintRenderer.php',
'ArcanistConsoleLintRendererTestCase' => 'lint/renderer/__tests__/ArcanistConsoleLintRendererTestCase.php',
'ArcanistConstructorParenthesesXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistConstructorParenthesesXHPASTLinterRule.php',
@@ -164,6 +164,7 @@
'ArcanistDefaultsConfigurationSource' => 'config/source/ArcanistDefaultsConfigurationSource.php',
'ArcanistDeprecationXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistDeprecationXHPASTLinterRule.php',
'ArcanistDeprecationXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistDeprecationXHPASTLinterRuleTestCase.php',
+ 'ArcanistDictionaryConfigurationSource' => 'config/source/ArcanistDictionaryConfigurationSource.php',
'ArcanistDiffByteSizeException' => 'exception/ArcanistDiffByteSizeException.php',
'ArcanistDiffChange' => 'parser/diff/ArcanistDiffChange.php',
'ArcanistDiffChangeType' => 'parser/diff/ArcanistDiffChangeType.php',
@@ -217,7 +218,7 @@
'ArcanistFutureLinter' => 'lint/linter/ArcanistFutureLinter.php',
'ArcanistGeneratedLinter' => 'lint/linter/ArcanistGeneratedLinter.php',
'ArcanistGeneratedLinterTestCase' => 'lint/linter/__tests__/ArcanistGeneratedLinterTestCase.php',
- 'ArcanistGetConfigWorkflow' => 'workflow/ArcanistGetConfigWorkflow.php',
+ 'ArcanistGetConfigWorkflow' => 'toolset/workflow/ArcanistGetConfigWorkflow.php',
'ArcanistGitAPI' => 'repository/api/ArcanistGitAPI.php',
'ArcanistGitCommitMessageHardpointLoader' => 'loader/ArcanistGitCommitMessageHardpointLoader.php',
'ArcanistGitHardpointLoader' => 'loader/ArcanistGitHardpointLoader.php',
@@ -234,7 +235,7 @@
'ArcanistHLintLinter' => 'lint/linter/ArcanistHLintLinter.php',
'ArcanistHLintLinterTestCase' => 'lint/linter/__tests__/ArcanistHLintLinterTestCase.php',
'ArcanistHardpointLoader' => 'loader/ArcanistHardpointLoader.php',
- 'ArcanistHelpWorkflow' => 'toolset/ArcanistHelpWorkflow.php',
+ 'ArcanistHelpWorkflow' => 'toolset/workflow/ArcanistHelpWorkflow.php',
'ArcanistHexadecimalNumericScalarCasingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistHexadecimalNumericScalarCasingXHPASTLinterRule.php',
'ArcanistHexadecimalNumericScalarCasingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistHexadecimalNumericScalarCasingXHPASTLinterRuleTestCase.php',
'ArcanistHgClientChannel' => 'hgdaemon/ArcanistHgClientChannel.php',
@@ -405,6 +406,7 @@
'ArcanistRubyLinter' => 'lint/linter/ArcanistRubyLinter.php',
'ArcanistRubyLinterTestCase' => 'lint/linter/__tests__/ArcanistRubyLinterTestCase.php',
'ArcanistRuntimeConfigurationSource' => 'config/source/ArcanistRuntimeConfigurationSource.php',
+ 'ArcanistScalarConfigOption' => 'config/option/ArcanistScalarConfigOption.php',
'ArcanistScriptAndRegexLinter' => 'lint/linter/ArcanistScriptAndRegexLinter.php',
'ArcanistSelfClassReferenceXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistSelfClassReferenceXHPASTLinterRule.php',
'ArcanistSelfClassReferenceXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistSelfClassReferenceXHPASTLinterRuleTestCase.php',
@@ -412,10 +414,10 @@
'ArcanistSelfMemberReferenceXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistSelfMemberReferenceXHPASTLinterRuleTestCase.php',
'ArcanistSemicolonSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistSemicolonSpacingXHPASTLinterRule.php',
'ArcanistSemicolonSpacingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistSemicolonSpacingXHPASTLinterRuleTestCase.php',
- 'ArcanistSetConfigWorkflow' => 'workflow/ArcanistSetConfigWorkflow.php',
+ 'ArcanistSetConfigWorkflow' => 'toolset/workflow/ArcanistSetConfigWorkflow.php',
'ArcanistSetting' => 'configuration/ArcanistSetting.php',
'ArcanistSettings' => 'configuration/ArcanistSettings.php',
- 'ArcanistShellCompleteWorkflow' => 'toolset/ArcanistShellCompleteWorkflow.php',
+ 'ArcanistShellCompleteWorkflow' => 'toolset/workflow/ArcanistShellCompleteWorkflow.php',
'ArcanistSingleLintEngine' => 'lint/engine/ArcanistSingleLintEngine.php',
'ArcanistSlownessXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistSlownessXHPASTLinterRule.php',
'ArcanistSlownessXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistSlownessXHPASTLinterRuleTestCase.php',
@@ -425,6 +427,7 @@
'ArcanistStaticThisXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistStaticThisXHPASTLinterRule.php',
'ArcanistStaticThisXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistStaticThisXHPASTLinterRuleTestCase.php',
'ArcanistStopWorkflow' => 'workflow/ArcanistStopWorkflow.php',
+ 'ArcanistStringConfigOption' => 'config/option/ArcanistStringConfigOption.php',
'ArcanistSubversionAPI' => 'repository/api/ArcanistSubversionAPI.php',
'ArcanistSubversionWorkingCopy' => 'workingcopy/ArcanistSubversionWorkingCopy.php',
'ArcanistSummaryLintRenderer' => 'lint/renderer/ArcanistSummaryLintRenderer.php',
@@ -483,9 +486,10 @@
'ArcanistVariableReferenceSpacingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistVariableReferenceSpacingXHPASTLinterRuleTestCase.php',
'ArcanistVariableVariableXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistVariableVariableXHPASTLinterRule.php',
'ArcanistVariableVariableXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistVariableVariableXHPASTLinterRuleTestCase.php',
- 'ArcanistVersionWorkflow' => 'toolset/ArcanistVersionWorkflow.php',
+ 'ArcanistVersionWorkflow' => 'toolset/workflow/ArcanistVersionWorkflow.php',
'ArcanistWeldWorkflow' => 'workflow/ArcanistWeldWorkflow.php',
'ArcanistWhichWorkflow' => 'workflow/ArcanistWhichWorkflow.php',
+ 'ArcanistWildConfigOption' => 'config/option/ArcanistWildConfigOption.php',
'ArcanistWorkflow' => 'toolset/ArcanistWorkflow.php',
'ArcanistWorkingCopy' => 'workingcopy/ArcanistWorkingCopy.php',
'ArcanistWorkingCopyConfigurationSource' => 'config/source/ArcanistWorkingCopyConfigurationSource.php',
@@ -1226,7 +1230,6 @@
'ArcanistConcatenationOperatorXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistConcatenationOperatorXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistConduitCall' => 'Phobject',
- 'ArcanistConduitConfigurationEngineExtension' => 'ArcanistConfigurationEngineExtension',
'ArcanistConduitEngine' => 'Phobject',
'ArcanistConfigOption' => 'Phobject',
'ArcanistConfigurationDrivenLintEngine' => 'ArcanistLintEngine',
@@ -1236,6 +1239,7 @@
'ArcanistConfigurationManager' => 'Phobject',
'ArcanistConfigurationSource' => 'Phobject',
'ArcanistConfigurationSourceList' => 'Phobject',
+ 'ArcanistConfigurationSourceValue' => 'Phobject',
'ArcanistConsoleLintRenderer' => 'ArcanistLintRenderer',
'ArcanistConsoleLintRendererTestCase' => 'PhutilTestCase',
'ArcanistConstructorParenthesesXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
@@ -1253,9 +1257,10 @@
'ArcanistDeclarationParenthesesXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistDefaultParametersXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistDefaultParametersXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
- 'ArcanistDefaultsConfigurationSource' => 'ArcanistConfigurationSource',
+ 'ArcanistDefaultsConfigurationSource' => 'ArcanistDictionaryConfigurationSource',
'ArcanistDeprecationXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistDeprecationXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
+ 'ArcanistDictionaryConfigurationSource' => 'ArcanistConfigurationSource',
'ArcanistDiffByteSizeException' => 'Exception',
'ArcanistDiffChange' => 'Phobject',
'ArcanistDiffChangeType' => 'Phobject',
@@ -1298,7 +1303,7 @@
'ArcanistFileUploader' => 'Phobject',
'ArcanistFilenameLinter' => 'ArcanistLinter',
'ArcanistFilenameLinterTestCase' => 'ArcanistLinterTestCase',
- 'ArcanistFilesystemConfigurationSource' => 'ArcanistConfigurationSource',
+ 'ArcanistFilesystemConfigurationSource' => 'ArcanistDictionaryConfigurationSource',
'ArcanistFlagWorkflow' => 'ArcanistWorkflow',
'ArcanistFlake8Linter' => 'ArcanistExternalLinter',
'ArcanistFlake8LinterTestCase' => 'ArcanistExternalLinterTestCase',
@@ -1496,7 +1501,8 @@
'ArcanistRuboCopLinterTestCase' => 'ArcanistExternalLinterTestCase',
'ArcanistRubyLinter' => 'ArcanistExternalLinter',
'ArcanistRubyLinterTestCase' => 'ArcanistExternalLinterTestCase',
- 'ArcanistRuntimeConfigurationSource' => 'ArcanistConfigurationSource',
+ 'ArcanistRuntimeConfigurationSource' => 'ArcanistDictionaryConfigurationSource',
+ 'ArcanistScalarConfigOption' => 'ArcanistConfigOption',
'ArcanistScriptAndRegexLinter' => 'ArcanistLinter',
'ArcanistSelfClassReferenceXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistSelfClassReferenceXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
@@ -1517,6 +1523,7 @@
'ArcanistStaticThisXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistStaticThisXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistStopWorkflow' => 'ArcanistPhrequentWorkflow',
+ 'ArcanistStringConfigOption' => 'ArcanistScalarConfigOption',
'ArcanistSubversionAPI' => 'ArcanistRepositoryAPI',
'ArcanistSubversionWorkingCopy' => 'ArcanistWorkingCopy',
'ArcanistSummaryLintRenderer' => 'ArcanistLintRenderer',
@@ -1578,6 +1585,7 @@
'ArcanistVersionWorkflow' => 'ArcanistWorkflow',
'ArcanistWeldWorkflow' => 'ArcanistWorkflow',
'ArcanistWhichWorkflow' => 'ArcanistWorkflow',
+ 'ArcanistWildConfigOption' => 'ArcanistConfigOption',
'ArcanistWorkflow' => 'Phobject',
'ArcanistWorkingCopy' => 'Phobject',
'ArcanistWorkingCopyConfigurationSource' => 'ArcanistFilesystemConfigurationSource',
diff --git a/src/config/ArcanistArcConfigurationEngineExtension.php b/src/config/ArcanistArcConfigurationEngineExtension.php
deleted file mode 100644
--- a/src/config/ArcanistArcConfigurationEngineExtension.php
+++ /dev/null
@@ -1,6 +0,0 @@
-<?php
-
-final class ArcanistArcConfigurationEngineExtension
- extends ArcanistConfigurationEngineExtension {
-
-}
diff --git a/src/config/ArcanistConduitConfigurationEngineExtension.php b/src/config/ArcanistConduitConfigurationEngineExtension.php
deleted file mode 100644
--- a/src/config/ArcanistConduitConfigurationEngineExtension.php
+++ /dev/null
@@ -1,6 +0,0 @@
-<?php
-
-final class ArcanistConduitConfigurationEngineExtension
- extends ArcanistConfigurationEngineExtension {
-
-}
diff --git a/src/config/ArcanistConfigOption.php b/src/config/ArcanistConfigOption.php
deleted file mode 100644
--- a/src/config/ArcanistConfigOption.php
+++ /dev/null
@@ -1,6 +0,0 @@
-<?php
-
-abstract class ArcanistConfigOption
- extends Phobject {
-
-}
diff --git a/src/config/ArcanistConfigurationEngine.php b/src/config/ArcanistConfigurationEngine.php
--- a/src/config/ArcanistConfigurationEngine.php
+++ b/src/config/ArcanistConfigurationEngine.php
@@ -5,6 +5,7 @@
private $workingCopy;
private $arguments;
+ private $toolset;
public function setWorkingCopy(ArcanistWorkingCopy $working_copy) {
$this->workingCopy = $working_copy;
@@ -95,4 +96,120 @@
}
}
+ public function newDefaults() {
+ $map = $this->newConfigOptionsMap();
+ return mpull($map, 'getDefaultValue');
+ }
+
+ public function newConfigOptionsMap() {
+ $extensions = $this->newEngineExtensions();
+
+ $map = array();
+ $alias_map = array();
+ foreach ($extensions as $extension) {
+ $options = $extension->newConfigurationOptions();
+
+ foreach ($options as $option) {
+ $key = $option->getKey();
+
+ $this->validateConfigOptionKey($key, $extension);
+
+ if (isset($map[$key])) {
+ throw new Exception(
+ pht(
+ 'Configuration option ("%s") defined by extension "%s" '.
+ 'conflicts with an existing option. Each option must have '.
+ 'a unique key.',
+ $key,
+ get_class($extension)));
+ }
+
+ if (isset($alias_map[$key])) {
+ throw new Exception(
+ pht(
+ 'Configuration option ("%s") defined by extension "%s" '.
+ 'conflicts with an alias for another option ("%s"). The '.
+ 'key and aliases of each option must be unique.',
+ $key,
+ get_class($extension),
+ $alias_map[$key]->getKey()));
+ }
+
+ $map[$key] = $option;
+
+ foreach ($option->getAliases() as $alias) {
+ $this->validateConfigOptionKey($alias, $extension, $key);
+
+ if (isset($map[$alias])) {
+ throw new Exception(
+ pht(
+ 'Configuration option ("%s") defined by extension "%s" '.
+ 'has an alias ("%s") which conflicts with an existing '.
+ 'option. The key and aliases of each option must be '.
+ 'unique.',
+ $key,
+ get_class($extension),
+ $alias));
+ }
+
+ if (isset($alias_map[$alias])) {
+ throw new Exception(
+ pht(
+ 'Configuration option ("%s") defined by extension "%s" '.
+ 'has an alias ("%s") which conflicts with the alias of '.
+ 'another configuration option ("%s"). The key and aliases '.
+ 'of each option must be unique.',
+ $key,
+ get_class($extension),
+ $alias,
+ $alias_map[$alias]->getKey()));
+ }
+
+ $alias_map[$alias] = $option;
+ }
+ }
+ }
+
+ return $map;
+ }
+
+ private function validateConfigOptionKey(
+ $key,
+ ArcanistConfigurationEngineExtension $extension,
+ $is_alias_of = null) {
+
+ $is_ok = preg_match('(^[a-z][a-z0-9._-]{2,}\z)', $key);
+ if (!$is_ok) {
+ if ($is_alias_of === null) {
+ throw new Exception(
+ pht(
+ 'Extension ("%s") defines invalid configuration with key "%s". '.
+ 'Configuration keys: may only contain lowercase letters, '.
+ 'numbers, hyphens, underscores, and periods; must start with a '.
+ 'letter; and must be at least three characters long.',
+ get_class($extension),
+ $key));
+ } else {
+ throw new Exception(
+ pht(
+ 'Extension ("%s") defines invalid alias ("%s") for configuration '.
+ 'key ("%s"). Configuration keys and aliases: may only contain '.
+ 'lowercase letters, numbers, hyphens, underscores, and periods; '.
+ 'must start with a letter; and must be at least three characters '.
+ 'long.',
+ get_class($extension),
+ $key,
+ $is_alias_of));
+ }
+ }
+ }
+
+ private function newEngineExtensions() {
+ return id(new PhutilClassMapQuery())
+ ->setAncestorClass('ArcanistConfigurationEngineExtension')
+ ->setUniqueMethod('getExtensionKey')
+ ->setContinueOnFailure(true)
+ ->execute();
+ }
+
}
diff --git a/src/config/ArcanistConfigurationEngineExtension.php b/src/config/ArcanistConfigurationEngineExtension.php
--- a/src/config/ArcanistConfigurationEngineExtension.php
+++ b/src/config/ArcanistConfigurationEngineExtension.php
@@ -1,6 +1,10 @@
<?php
-final class ArcanistConfigurationEngineExtension
+abstract class ArcanistConfigurationEngineExtension
extends Phobject {
+ final public function getExtensionKey() {
+ return $this->getPhobjectClassConstant('EXTENSIONKEY');
+ }
+
}
diff --git a/src/config/ArcanistConfigurationSourceList.php b/src/config/ArcanistConfigurationSourceList.php
--- a/src/config/ArcanistConfigurationSourceList.php
+++ b/src/config/ArcanistConfigurationSourceList.php
@@ -4,10 +4,129 @@
extends Phobject {
private $sources = array();
+ private $configOptions;
public function addSource(ArcanistConfigurationSource $source) {
$this->sources[] = $source;
return $this;
}
+ public function getSources() {
+ return $this->sources;
+ }
+
+ public function getConfig($key) {
+ $option = $this->getConfigOption($key);
+ $values = $this->getStorageValueList($key);
+ return $option->getValueFromStorageValueList($values);
+ }
+
+ public function getStorageValueList($key) {
+ $values = array();
+
+ foreach ($this->getSources() as $source) {
+ if ($source->hasValueForKey($key)) {
+ $value = $source->getValueForKey($key);
+ $values[] = new ArcanistConfigurationSourceValue(
+ $source,
+ $source->getValueForKey($key));
+ }
+ }
+
+ return $values;
+ }
+
+ public function getConfigOption($key) {
+ $options = $this->getConfigOptions();
+
+ if (!isset($options[$key])) {
+ throw new Exception(
+ pht(
+ 'Configuration option ("%s") is unrecognized. You can only read '.
+ 'recognized configuration options.',
+ $key));
+ }
+
+ return $options[$key];
+ }
+
+ public function setConfigOptions(array $config_options) {
+ assert_instances_of($config_options, 'ArcanistConfigOption');
+
+ $config_options = mpull($config_options, null, 'getKey');
+ $this->configOptions = $config_options;
+
+ return $this;
+ }
+
+ public function getConfigOptions() {
+ if ($this->configOptions === null) {
+ throw new PhutilInvalidStateException('setConfigOptions');
+ }
+
+ return $this->configOptions;
+ }
+
+ public function validateConfiguration() {
+ $options = $this->getConfigOptions();
+
+ $aliases = array();
+ foreach ($options as $key => $option) {
+ foreach ($option->getAliases() as $alias) {
+ $aliases[$alias] = $key;
+ }
+ }
+
+ // TOOLSETS: Handle the case where config specifies both a value and an
+ // alias for that value. The alias should be ignored and we should emit
+ // a warning. This also needs to be implemented when actually reading
+ // configuration.
+
+ $value_lists = array();
+ foreach ($this->getSources() as $source) {
+ $keys = $source->getAllKeys();
+ foreach ($keys as $key) {
+ $resolved_key = idx($aliases, $key, $key);
+ $option = idx($options, $resolved_key);
+
+ // If there's no option object for this config, this value is
+ // unrecognized. Sources are free to handle this however they want:
+ // for config files we emit a warning; for "--config" we fatal.
+
+ if (!$option) {
+ $source->didReadUnknownOption($key);
+ continue;
+ }
+
+ $raw_value = $source->getValueForKey($key);
+
+ // Make sure we can convert whatever value the configuration source is
+ // providing into a legitimate runtime value.
+ try {
+ $value = $raw_value;
+ if ($source->isStringSource()) {
+ $value = $option->getStorageValueFromStringValue($value);
+ }
+ $option->getValueFromStorageValue($value);
+
+ $value_lists[$resolved_key][] = new ArcanistConfigurationSourceValue(
+ $source,
+ $raw_value);
+ } catch (Exception $ex) {
+ throw $ex;
+ }
+ }
+ }
+
+ // Make sure each value list can be merged.
+ foreach ($value_lists as $key => $value_list) {
+ try {
+ $options[$key]->getValueFromStorageValueList($value_list);
+ } catch (Exception $ex) {
+ throw $ex;
+ }
+ }
+
+ }
+
}
\ No newline at end of file
diff --git a/src/config/ArcanistConfigurationSourceValue.php b/src/config/ArcanistConfigurationSourceValue.php
new file mode 100644
--- /dev/null
+++ b/src/config/ArcanistConfigurationSourceValue.php
@@ -0,0 +1,24 @@
+<?php
+
+final class ArcanistConfigurationSourceValue
+ extends Phobject {
+
+ private $source;
+ private $value;
+
+ public function __construct(ArcanistConfigurationSource $source, $value) {
+ $this->source = $source;
+ $this->value = $value;
+ }
+
+ public function getConfigurationSource() {
+ return $this->source;
+ }
+
+ public function getValue() {
+ return $this->value;
+ }
+
+}
+
+
diff --git a/src/config/arc/ArcanistArcConfigurationEngineExtension.php b/src/config/arc/ArcanistArcConfigurationEngineExtension.php
new file mode 100644
--- /dev/null
+++ b/src/config/arc/ArcanistArcConfigurationEngineExtension.php
@@ -0,0 +1,166 @@
+<?php
+
+final class ArcanistArcConfigurationEngineExtension
+ extends ArcanistConfigurationEngineExtension {
+
+ const EXTENSIONKEY = 'arc';
+
+ public function newConfigurationOptions() {
+ // TOOLSETS: Restore "load", and maybe this other stuff.
+
+/*
+ 'load' => array(
+ 'type' => 'list',
+ 'legacy' => 'phutil_libraries',
+ 'help' => pht(
+ 'A list of paths to phutil libraries that should be loaded at '.
+ 'startup. This can be used to make classes available, like lint '.
+ 'or unit test engines.'),
+ 'default' => array(),
+ 'example' => '["/var/arc/customlib/src"]',
+ ),
+
+ 'arc.feature.start.default' => array(
+ 'type' => 'string',
+ 'help' => pht(
+ 'The name of the default branch to create the new feature branch '.
+ 'off of.'),
+ 'example' => '"develop"',
+ ),
+ 'arc.land.onto.default' => array(
+ 'type' => 'string',
+ 'help' => pht(
+ 'The name of the default branch to land changes onto when '.
+ '`%s` is run.',
+ 'arc land'),
+ 'example' => '"develop"',
+ ),
+
+ 'arc.autostash' => array(
+ 'type' => 'bool',
+ 'help' => pht(
+ 'Whether %s should permit the automatic stashing of changes in the '.
+ 'working directory when requiring a clean working copy. This option '.
+ 'should only be used when users understand how to restore their '.
+ 'working directory from the local stash if an Arcanist operation '.
+ 'causes an unrecoverable error.',
+ 'arc'),
+ 'default' => false,
+ 'example' => 'false',
+ ),
+
+ 'aliases' => array(
+ 'type' => 'aliases',
+ 'help' => pht(
+ 'Configured command aliases. Use "arc alias" to define aliases.'),
+ ),
+
+ 'history.immutable' => array(
+ 'type' => 'bool',
+ 'legacy' => 'immutable_history',
+ 'help' => pht(
+ 'If true, %s will never change repository history (e.g., through '.
+ 'amending or rebasing). Defaults to true in Mercurial and false in '.
+ 'Git. This setting has no effect in Subversion.',
+ 'arc'),
+ 'example' => 'false',
+ ),
+ 'editor' => array(
+ 'type' => 'string',
+ 'help' => pht(
+ 'Command to use to invoke an interactive editor, like `%s` or `%s`. '.
+ 'This setting overrides the %s environmental variable.',
+ 'nano',
+ 'vim',
+ 'EDITOR'),
+ 'example' => '"nano"',
+ ),
+ 'https.cabundle' => array(
+ 'type' => 'string',
+ 'help' => pht(
+ "Path to a custom CA bundle file to be used for arcanist's cURL ".
+ "calls. This is used primarily when your conduit endpoint is ".
+ "behind HTTPS signed by your organization's internal CA."),
+ 'example' => 'support/yourca.pem',
+ ),
+ 'https.blindly-trust-domains' => array(
+ 'type' => 'list',
+ 'help' => pht(
+ 'List of domains to blindly trust SSL certificates for. '.
+ 'Disables peer verification.'),
+ 'default' => array(),
+ 'example' => '["secure.mycompany.com"]',
+ ),
+ 'browser' => array(
+ 'type' => 'string',
+ 'help' => pht('Command to use to invoke a web browser.'),
+ 'example' => '"gnome-www-browser"',
+ ),
+ 'http.basicauth.user' => array(
+ 'type' => 'string',
+ 'help' => pht('Username to use for basic auth over HTTP transports.'),
+ 'example' => '"bob"',
+ ),
+ 'http.basicauth.pass' => array(
+ 'type' => 'string',
+ 'help' => pht('Password to use for basic auth over HTTP transports.'),
+ 'example' => '"bobhasasecret"',
+ ),
+
+*/
+
+
+
+ return array(
+ id(new ArcanistStringConfigOption())
+ ->setKey('base')
+ ->setSummary(pht('Ruleset for selecting commit ranges.'))
+ ->setHelp(
+ pht(
+ 'Base commit ruleset to invoke when determining the start of a '.
+ 'commit range. See "Arcanist User Guide: Commit Ranges" for '.
+ 'details.'))
+ ->setExamples(
+ array(
+ 'arc:amended, arc:prompt',
+ )),
+ id(new ArcanistStringConfigOption())
+ ->setKey('repository')
+ ->setAliases(
+ array(
+ 'repository.callsign',
+ ))
+ ->setSummary(pht('Repository for the current working copy.'))
+ ->setHelp(
+ pht(
+ 'Associate the working copy with a specific Phabricator '.
+ 'repository. Normally, `arc` can figure this association out on '.
+ 'its own, but if your setup is unusual you can use this option '.
+ 'to tell it what the desired value is.'))
+ ->setExamples(
+ array(
+ 'libexample',
+ 'XYZ',
+ 'R123',
+ '123',
+ )),
+ id(new ArcanistStringConfigOption())
+ ->setKey('phabricator.uri')
+ ->setAliases(
+ array(
+ 'conduit_uri',
+ 'default',
+ ))
+ ->setSummary(pht('Phabricator install to connect to.'))
+ ->setHelp(
+ pht(
+ 'Associates this working copy with a specific installation of '.
+ 'Phabricator.'))
+ ->setExamples(
+ array(
+ 'https://phabricator.mycompany.com/',
+ )),
+ );
+ }
+
+}
diff --git a/src/config/option/ArcanistConfigOption.php b/src/config/option/ArcanistConfigOption.php
new file mode 100644
--- /dev/null
+++ b/src/config/option/ArcanistConfigOption.php
@@ -0,0 +1,88 @@
+<?php
+
+abstract class ArcanistConfigOption
+ extends Phobject {
+
+ private $key;
+ private $help;
+ private $summary;
+ private $aliases = array();
+ private $examples = array();
+ private $defaultValue;
+
+ public function setKey($key) {
+ $this->key = $key;
+ return $this;
+ }
+
+ public function getKey() {
+ return $this->key;
+ }
+
+ public function setAliases($aliases) {
+ $this->aliases = $aliases;
+ return $this;
+ }
+
+ public function getAliases() {
+ return $this->aliases;
+ }
+
+ public function setSummary($summary) {
+ $this->summary = $summary;
+ return $this;
+ }
+
+ public function getSummary() {
+ return $this->summary;
+ }
+
+ public function setHelp($help) {
+ $this->help = $help;
+ return $this;
+ }
+
+ public function getHelp() {
+ return $this->help;
+ }
+
+ public function setExamples(array $examples) {
+ $this->examples = $examples;
+ return $this;
+ }
+
+ public function getExamples() {
+ return $this->examples;
+ }
+
+ public function setDefaultValue($default_value) {
+ $this->defaultValue = $default_value;
+ return $this;
+ }
+
+ public function getDefaultValue() {
+ return $this->defaultValue;
+ }
+
+ abstract public function getType();
+
+ abstract public function getValueFromStorageValueList(array $list);
+ abstract public function getStorageValueFromStringValue($value);
+ abstract public function getValueFromStorageValue($value);
+ abstract public function getDisplayValueFromValue($value);
+
+ protected function getStorageValueFromSourceValue(
+ ArcanistConfigurationSourceValue $source_value) {
+
+ $value = $source_value->getValue();
+ $source = $source_value->getConfigurationSource();
+
+ if ($source->isStringSource()) {
+ $value = $this->getStorageValueFromStringValue($value);
+ }
+
+ return $value;
+ }
+
+
+}
diff --git a/src/config/option/ArcanistScalarConfigOption.php b/src/config/option/ArcanistScalarConfigOption.php
new file mode 100644
--- /dev/null
+++ b/src/config/option/ArcanistScalarConfigOption.php
@@ -0,0 +1,19 @@
+<?php
+
+abstract class ArcanistScalarConfigOption
+ extends ArcanistConfigOption {
+
+ public function getValueFromStorageValueList(array $list) {
+ assert_instances_of($list, 'ArcanistConfigurationSourceValue');
+
+ $source_value = last($list);
+ $storage_value = $this->getStorageValueFromSourceValue($source_value);
+
+ return $this->getValueFromStorageValue($storage_value);
+ }
+
+ public function getValueFromStorageValue($value) {
+ return $value;
+ }
+
+}
diff --git a/src/config/option/ArcanistStringConfigOption.php b/src/config/option/ArcanistStringConfigOption.php
new file mode 100644
--- /dev/null
+++ b/src/config/option/ArcanistStringConfigOption.php
@@ -0,0 +1,18 @@
+<?php
+
+final class ArcanistStringConfigOption
+ extends ArcanistScalarConfigOption {
+
+ public function getType() {
+ return 'string';
+ }
+
+ public function getStorageValueFromStringValue($value) {
+ return (string)$value;
+ }
+
+ public function getDisplayValueFromValue($value) {
+ return $value;
+ }
+
+}
diff --git a/src/config/option/ArcanistWildConfigOption.php b/src/config/option/ArcanistWildConfigOption.php
new file mode 100644
--- /dev/null
+++ b/src/config/option/ArcanistWildConfigOption.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * This option type makes it easier to manage unknown options with unknown
+ * types.
+ */
+final class ArcanistWildConfigOption
+ extends ArcanistConfigOption {
+
+ public function getType() {
+ return 'wild';
+ }
+
+ public function getStorageValueFromStringValue($value) {
+ return (string)$value;
+ }
+
+ public function getDisplayValueFromValue($value) {
+ return json_encode($value);
+ }
+
+ public function getValueFromStorageValueList(array $list) {
+ assert_instances_of($list, 'ArcanistConfigurationSourceValue');
+
+ $source_value = last($list);
+ $storage_value = $this->getStorageValueFromSourceValue($source_value);
+
+ return $this->getValueFromStorageValue($storage_value);
+ }
+
+ public function getValueFromStorageValue($value) {
+ return $value;
+ }
+
+}
diff --git a/src/config/source/ArcanistConfigurationSource.php b/src/config/source/ArcanistConfigurationSource.php
--- a/src/config/source/ArcanistConfigurationSource.php
+++ b/src/config/source/ArcanistConfigurationSource.php
@@ -3,4 +3,27 @@
abstract class ArcanistConfigurationSource
extends Phobject {
+ abstract public function getSourceDisplayName();
+ abstract public function getAllKeys();
+ abstract public function hasValueForKey($key);
+ abstract public function getValueForKey($key);
+
+ public function isStringSource() {
+ return false;
+ }
+
+ public function didReadUnknownOption($key) {
+ // TOOLSETS: Standardize this kind of messaging? On ArcanistRuntime?
+
+ fprintf(
+ STDERR,
+ tsprintf(
+ "<bg:yellow>** %s **</bg> %s\n",
+ pht('WARNING'),
+ pht(
+ 'Ignoring unrecognized configuration option ("%s") from source: %s.',
+ $key,
+ $this->getSourceDisplayName())));
+ }
+
}
\ No newline at end of file
diff --git a/src/config/source/ArcanistDefaultsConfigurationSource.php b/src/config/source/ArcanistDefaultsConfigurationSource.php
--- a/src/config/source/ArcanistDefaultsConfigurationSource.php
+++ b/src/config/source/ArcanistDefaultsConfigurationSource.php
@@ -1,6 +1,17 @@
<?php
final class ArcanistDefaultsConfigurationSource
- extends ArcanistConfigurationSource {
+ extends ArcanistDictionaryConfigurationSource {
+
+ public function getSourceDisplayName() {
+ return pht('Builtin Defaults');
+ }
+
+ public function __construct() {
+ $values = id(new ArcanistConfigurationEngine())
+ ->newDefaults();
+
+ parent::__construct($values);
+ }
}
\ No newline at end of file
diff --git a/src/config/source/ArcanistDictionaryConfigurationSource.php b/src/config/source/ArcanistDictionaryConfigurationSource.php
new file mode 100644
--- /dev/null
+++ b/src/config/source/ArcanistDictionaryConfigurationSource.php
@@ -0,0 +1,32 @@
+<?php
+
+abstract class ArcanistDictionaryConfigurationSource
+ extends ArcanistConfigurationSource {
+
+ private $values;
+
+ public function __construct(array $dictionary) {
+ $this->values = $dictionary;
+ }
+
+ public function getAllKeys() {
+ return array_keys($this->values);
+ }
+
+ public function hasValueForKey($key) {
+ return array_key_exists($key, $this->values);
+ }
+
+ public function getValueForKey($key) {
+ if (!$this->hasValueForKey($key)) {
+ throw new Exception(
+ pht(
+ 'Configuration source ("%s") has no value for key ("%s").',
+ get_class($this),
+ $key));
+ }
+
+ return $this->values[$key];
+ }
+
+}
\ No newline at end of file
diff --git a/src/config/source/ArcanistFileConfigurationSource.php b/src/config/source/ArcanistFileConfigurationSource.php
--- a/src/config/source/ArcanistFileConfigurationSource.php
+++ b/src/config/source/ArcanistFileConfigurationSource.php
@@ -3,4 +3,9 @@
final class ArcanistFileConfigurationSource
extends ArcanistConfigurationSource {
+ public function getFileKindDisplayName() {
+ return pht('Config File');
+ }
+
+
}
\ No newline at end of file
diff --git a/src/config/source/ArcanistFilesystemConfigurationSource.php b/src/config/source/ArcanistFilesystemConfigurationSource.php
--- a/src/config/source/ArcanistFilesystemConfigurationSource.php
+++ b/src/config/source/ArcanistFilesystemConfigurationSource.php
@@ -1,6 +1,32 @@
<?php
abstract class ArcanistFilesystemConfigurationSource
- extends ArcanistConfigurationSource {
+ extends ArcanistDictionaryConfigurationSource {
+
+ private $path;
+
+ public function __construct($path) {
+ $this->path = $path;
+
+ $values = array();
+ if (Filesystem::pathExists($path)) {
+ $contents = Filesystem::readFile($path);
+ if (strlen(trim($contents))) {
+ $values = phutil_json_decode($contents);
+ }
+ }
+
+ parent::__construct($values);
+ }
+
+ public function getPath() {
+ return $this->path;
+ }
+
+ public function getSourceDisplayName() {
+ return pht('%s (%s)', $this->getFileKindDisplayName(), $this->getPath());
+ }
+
+ abstract public function getFileKindDisplayName();
}
\ No newline at end of file
diff --git a/src/config/source/ArcanistLocalConfigurationSource.php b/src/config/source/ArcanistLocalConfigurationSource.php
--- a/src/config/source/ArcanistLocalConfigurationSource.php
+++ b/src/config/source/ArcanistLocalConfigurationSource.php
@@ -3,4 +3,8 @@
final class ArcanistLocalConfigurationSource
extends ArcanistWorkingCopyConfigurationSource {
+ public function getFileKindDisplayName() {
+ return pht('Local Config File');
+ }
+
}
\ No newline at end of file
diff --git a/src/config/source/ArcanistProjectConfigurationSource.php b/src/config/source/ArcanistProjectConfigurationSource.php
--- a/src/config/source/ArcanistProjectConfigurationSource.php
+++ b/src/config/source/ArcanistProjectConfigurationSource.php
@@ -3,4 +3,8 @@
final class ArcanistProjectConfigurationSource
extends ArcanistWorkingCopyConfigurationSource {
+ public function getFileKindDisplayName() {
+ return pht('Project Config File');
+ }
+
}
\ No newline at end of file
diff --git a/src/config/source/ArcanistRuntimeConfigurationSource.php b/src/config/source/ArcanistRuntimeConfigurationSource.php
--- a/src/config/source/ArcanistRuntimeConfigurationSource.php
+++ b/src/config/source/ArcanistRuntimeConfigurationSource.php
@@ -1,6 +1,49 @@
<?php
final class ArcanistRuntimeConfigurationSource
- extends ArcanistConfigurationSource {
+ extends ArcanistDictionaryConfigurationSource {
+
+ public function __construct(array $argv) {
+ $map = array();
+ foreach ($argv as $raw) {
+ $parts = explode('=', $raw, 2);
+ if (count($parts) !== 2) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Configuration option "%s" is not valid. Configuration options '.
+ 'passed with command line flags must be in the form "name=value".',
+ $raw));
+ }
+
+ list($key, $value) = $parts;
+ if (isset($map[$key])) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Configuration option "%s" was provided multiple times with '.
+ '"--config" flags. Specify each option no more than once.',
+ $key));
+ }
+
+ $map[$key] = $value;
+ }
+
+ parent::__construct($map);
+ }
+
+ public function didReadUnknownOption($key) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Configuration option ("%s") specified with "--config" flag is not '.
+ 'a recognized option.',
+ $key));
+ }
+
+ public function getSourceDisplayName() {
+ return pht('Runtime "--config" Flags');
+ }
+
+ public function isStringSource() {
+ return true;
+ }
}
\ No newline at end of file
diff --git a/src/config/source/ArcanistSystemConfigurationSource.php b/src/config/source/ArcanistSystemConfigurationSource.php
--- a/src/config/source/ArcanistSystemConfigurationSource.php
+++ b/src/config/source/ArcanistSystemConfigurationSource.php
@@ -3,4 +3,8 @@
final class ArcanistSystemConfigurationSource
extends ArcanistFilesystemConfigurationSource {
+ public function getFileKindDisplayName() {
+ return pht('System Config File');
+ }
+
}
\ No newline at end of file
diff --git a/src/config/source/ArcanistUserConfigurationSource.php b/src/config/source/ArcanistUserConfigurationSource.php
--- a/src/config/source/ArcanistUserConfigurationSource.php
+++ b/src/config/source/ArcanistUserConfigurationSource.php
@@ -3,4 +3,8 @@
final class ArcanistUserConfigurationSource
extends ArcanistFilesystemConfigurationSource {
+ public function getFileKindDisplayName() {
+ return pht('User Config File');
+ }
+
}
\ No newline at end of file
diff --git a/src/toolset/ArcanistWorkflow.php b/src/toolset/ArcanistWorkflow.php
--- a/src/toolset/ArcanistWorkflow.php
+++ b/src/toolset/ArcanistWorkflow.php
@@ -4,7 +4,8 @@
private $toolset;
private $arguments;
-
+ private $configurationEngine;
+ private $configurationSourceList;
/**
* Return the command used to invoke this workflow from the command like,
@@ -51,6 +52,26 @@
return $this;
}
+ final public function setConfigurationSourceList(
+ ArcanistConfigurationSourceList $config) {
+ $this->configurationSourceList = $config;
+ return $this;
+ }
+
+ final public function getConfigurationSourceList() {
+ return $this->configurationSourceList;
+ }
+
+ final public function setConfigurationEngine(
+ ArcanistConfigurationEngine $configuration_engine) {
+ $this->configurationEngine = $configuration_engine;
+ return $this;
+ }
+
+ final public function getConfigurationEngine() {
+ return $this->configurationEngine;
+ }
+
final protected function getToolsetKey() {
return $this->getToolset()->getToolsetKey();
}
diff --git a/src/toolset/ArcanistAliasWorkflow.php b/src/toolset/workflow/ArcanistAliasWorkflow.php
rename from src/toolset/ArcanistAliasWorkflow.php
rename to src/toolset/workflow/ArcanistAliasWorkflow.php
diff --git a/src/toolset/workflow/ArcanistGetConfigWorkflow.php b/src/toolset/workflow/ArcanistGetConfigWorkflow.php
new file mode 100644
--- /dev/null
+++ b/src/toolset/workflow/ArcanistGetConfigWorkflow.php
@@ -0,0 +1,137 @@
+<?php
+
+/**
+ * Read configuration settings.
+ */
+final class ArcanistGetConfigWorkflow
+ extends ArcanistWorkflow {
+
+ public function getWorkflowName() {
+ return 'get-config';
+ }
+
+ public function getCommandSynopses() {
+ return phutil_console_format(<<<EOTEXT
+ **get-config** [__options__] -- [__name__ ...]
+EOTEXT
+ );
+ }
+
+ public function getCommandHelp() {
+ return phutil_console_format(<<<EOTEXT
+ Supports: cli
+ Reads an arc configuration option. With no argument, reads all
+ options.
+
+ With __--verbose__, shows detailed information about one or more
+ options.
+EOTEXT
+ );
+ }
+
+ public function getArguments() {
+ return array(
+ 'verbose' => array(
+ 'help' => pht('Show detailed information about options.'),
+ ),
+ '*' => 'argv',
+ );
+ }
+
+ public function runWorkflow() {
+ $argv = $this->getArgument('argv');
+ $is_verbose = $this->getArgument('verbose');
+
+ $source_list = $this->getConfigurationSourceList();
+ $config_engine = $this->getConfigurationEngine();
+
+ $options_map = $config_engine->newConfigOptionsMap();
+
+ $all_keys = array();
+ $alias_map = array();
+ foreach ($options_map as $key => $config_option) {
+ $all_keys[$key] = $key;
+ foreach ($config_option->getAliases() as $alias) {
+ $alias_map[$alias] = $key;
+ }
+ }
+
+ foreach ($source_list->getSources() as $source) {
+ foreach ($source->getAllKeys() as $key) {
+ $all_keys[$key] = $key;
+ }
+ }
+
+ ksort($all_keys);
+
+ $defaults_map = $config_engine->newDefaults();
+
+ foreach ($all_keys as $key) {
+ $option = idx($options_map, $key);
+
+ if ($option) {
+ $option_summary = $option->getSummary();
+ $option_help = $option->getHelp();
+ } else {
+ $option_summary = pht('(This option is unrecognized.)');
+ $option_help = $option_summary;
+ }
+
+ if ($option) {
+ $formatter = $option;
+ } else {
+ $formatter = new ArcanistWildConfigOption();
+ }
+
+ if (!$is_verbose) {
+ echo tsprintf(
+ "**%s**\n%R\n\n",
+ $key,
+ $option_summary);
+ } else {
+ echo tsprintf(
+ "**%s**\n\n%R\n\n",
+ $key,
+ $option_help);
+ }
+
+ // NOTE: We can only get configuration from a SourceList if the option is
+ // a recognized option, so skip this part if the option isn't known.
+ if ($option) {
+ $value = $source_list->getConfig($key);
+ $display_value = $formatter->getDisplayValueFromValue($value);
+
+ echo tsprintf("%s: %s\n", pht('Value'), $display_value);
+
+ $default_value = idx($defaults_map, $key);
+ $display_default = $formatter->getDisplayValueFromValue($value);
+
+ echo tsprintf("%s: %s\n", pht('Default'), $display_default);
+ }
+
+ foreach ($source_list->getSources() as $source) {
+ if ($source->hasValueForKey($key)) {
+ $source_value = $source->getValueForKey($key);
+ $source_value = $formatter->getValueFromStorageValue($source_value);
+ $source_display = $formatter->getDisplayValueFromValue($source_value);
+ } else {
+ $source_display = pht('-');
+ }
+
+ echo tsprintf(
+ "%s: %s\n",
+ $source->getSourceDisplayName(),
+ $source_display);
+ }
+ }
+
+ // if (!$verbose) {
+ // $console->writeOut(
+ // "(%s)\n",
+ // pht('Run with %s for more details.', '--verbose'));
+ // }
+
+ return 0;
+ }
+
+}
diff --git a/src/toolset/ArcanistHelpWorkflow.php b/src/toolset/workflow/ArcanistHelpWorkflow.php
rename from src/toolset/ArcanistHelpWorkflow.php
rename to src/toolset/workflow/ArcanistHelpWorkflow.php
diff --git a/src/workflow/ArcanistSetConfigWorkflow.php b/src/toolset/workflow/ArcanistSetConfigWorkflow.php
rename from src/workflow/ArcanistSetConfigWorkflow.php
rename to src/toolset/workflow/ArcanistSetConfigWorkflow.php
diff --git a/src/toolset/ArcanistShellCompleteWorkflow.php b/src/toolset/workflow/ArcanistShellCompleteWorkflow.php
rename from src/toolset/ArcanistShellCompleteWorkflow.php
rename to src/toolset/workflow/ArcanistShellCompleteWorkflow.php
diff --git a/src/toolset/ArcanistVersionWorkflow.php b/src/toolset/workflow/ArcanistVersionWorkflow.php
rename from src/toolset/ArcanistVersionWorkflow.php
rename to src/toolset/workflow/ArcanistVersionWorkflow.php
diff --git a/src/workflow/ArcanistGetConfigWorkflow.php b/src/workflow/ArcanistGetConfigWorkflow.php
deleted file mode 100644
--- a/src/workflow/ArcanistGetConfigWorkflow.php
+++ /dev/null
@@ -1,173 +0,0 @@
-<?php
-
-/**
- * Read configuration settings.
- */
-final class ArcanistGetConfigWorkflow extends ArcanistWorkflow {
-
- public function getWorkflowName() {
- return 'get-config';
- }
-
- public function getCommandSynopses() {
- return phutil_console_format(<<<EOTEXT
- **get-config** [__options__] -- [__name__ ...]
-EOTEXT
- );
- }
-
- public function getCommandHelp() {
- return phutil_console_format(<<<EOTEXT
- Supports: cli
- Reads an arc configuration option. With no argument, reads all
- options.
-
- With __--verbose__, shows detailed information about one or more
- options.
-EOTEXT
- );
- }
-
- public function getArguments() {
- return array(
- 'verbose' => array(
- 'help' => pht('Show detailed information about options.'),
- ),
- '*' => 'argv',
- );
- }
-
- public function desiresRepositoryAPI() {
- return true;
- }
-
- public function run() {
- $argv = $this->getArgument('argv');
- $verbose = $this->getArgument('verbose');
-
- $settings = new ArcanistSettings();
-
- $configuration_manager = $this->getConfigurationManager();
- $configs = array(
- ArcanistConfigurationManager::CONFIG_SOURCE_LOCAL =>
- $configuration_manager->readLocalArcConfig(),
- ArcanistConfigurationManager::CONFIG_SOURCE_PROJECT =>
- $this->getWorkingCopy()->readProjectConfig(),
- ArcanistConfigurationManager::CONFIG_SOURCE_USER =>
- $configuration_manager->readUserArcConfig(),
- ArcanistConfigurationManager::CONFIG_SOURCE_SYSTEM =>
- $configuration_manager->readSystemArcConfig(),
- ArcanistConfigurationManager::CONFIG_SOURCE_DEFAULT =>
- $configuration_manager->readDefaultConfig(),
- );
-
- if ($argv) {
- $keys = $argv;
- } else {
- $keys = array_mergev(array_map('array_keys', $configs));
- $keys = array_merge($keys, $settings->getAllKeys());
- $keys = array_unique($keys);
- sort($keys);
- }
-
- $console = PhutilConsole::getConsole();
- $multi = (count($keys) > 1);
-
- foreach ($keys as $key) {
- $console->writeOut("**%s**\n\n", $key);
-
- if ($verbose) {
- $help = $settings->getHelp($key);
- if (!$help) {
- $help = pht(
- '(This configuration value is not recognized by arc. It may '.
- 'be misspelled or out of date.)');
- }
-
- $console->writeOut("%s\n\n", phutil_console_wrap($help, 4));
-
- $console->writeOut(
- "%s: %s\n\n",
- sprintf('% 20.20s', pht('Example Value')),
- $settings->getExample($key));
-
- }
-
- $values = array();
- foreach ($configs as $config_key => $config) {
- if (array_key_exists($key, $config)) {
- $values[$config_key] = $config[$key];
- } else {
- // If we didn't find a value, look for a legacy value.
- $source_project = ArcanistConfigurationManager::CONFIG_SOURCE_PROJECT;
- if ($config_key === $source_project) {
- $legacy_name = $settings->getLegacyName($key);
- if (array_key_exists($legacy_name, $config)) {
- $values[$config_key] = $config[$legacy_name];
- }
- }
- }
- }
-
- $console->writeOut(
- '%s: ',
- sprintf('% 20.20s', pht('Current Value')));
-
- if ($values) {
- $value = head($values);
- $value = $settings->formatConfigValueForDisplay($key, $value);
- $console->writeOut("%s\n", $value);
- } else {
- $console->writeOut("-\n");
- }
-
- $console->writeOut(
- '%s: ',
- sprintf('% 20.20s', pht('Current Source')));
-
- if ($values) {
- $source = head_key($values);
- $console->writeOut("%s\n", $source);
- } else {
- $console->writeOut("-\n");
- }
-
- if ($verbose) {
- $console->writeOut("\n");
-
- foreach ($configs as $name => $config) {
- $have_value = false;
- if (array_key_exists($name, $values)) {
- $have_value = true;
- $value = $values[$name];
- }
-
- $console->writeOut(
- '%s: ',
- sprintf('% 20.20s', pht('%s Value', $name)));
-
- if ($have_value) {
- $console->writeOut(
- "%s\n",
- $settings->formatConfigValueForDisplay($key, $value));
- } else {
- $console->writeOut("-\n");
- }
- }
- }
-
- if ($multi) {
- echo "\n";
- }
- }
-
- if (!$verbose) {
- $console->writeOut(
- "(%s)\n",
- pht('Run with %s for more details.', '--verbose'));
- }
-
- return 0;
- }
-
-}
diff --git a/support/ArcanistRuntime.php b/support/ArcanistRuntime.php
--- a/support/ArcanistRuntime.php
+++ b/support/ArcanistRuntime.php
@@ -65,10 +65,17 @@
$args->parsePartial($config_args, true);
- $config = $this->loadConfiguration($args);
+ $config_engine = $this->loadConfiguration($args);
+ $config = $config_engine->newConfigurationSourceList();
$this->loadLibraries($args, $config);
+ // Now that we've loaded libraries, we can validate configuration.
+ // Do this before continuing since configuration can impact other
+ // behaviors immediately and we want to catch any issues right away.
+ $config->setConfigOptions($config_engine->newConfigOptionsMap());
+ $config->validateConfiguration();
+
$toolset = $this->newToolset($argv);
$args->parsePartial($toolset->getToolsetArguments());
@@ -78,6 +85,10 @@
$phutil_workflows = array();
foreach ($workflows as $key => $workflow) {
$phutil_workflows[$key] = $workflow->newPhutilWorkflow();
+
+ $workflow
+ ->setConfigurationEngine($config_engine)
+ ->setConfigurationSourceList($config);
}
$unconsumed_argv = $args->getUnconsumedArgumentVector();
@@ -218,7 +229,7 @@
$engine->setWorkingCopy($working_copy);
}
- return $engine->newConfigurationSourceList();
+ return $engine;
}
private function loadLibraries(
@@ -462,6 +473,8 @@
array $argv,
ArcanistConfigurationSourceList $config) {
+ return $argv;
+
$command = head($argv);
// If this is a match for a recognized workflow, just return the arguments

File Metadata

Mime Type
text/plain
Expires
Wed, Dec 18, 8:53 AM (29 m, 19 s)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6901528
Default Alt Text
D19695.diff (52 KB)

Event Timeline