Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14290605
D19695.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
52 KB
Referenced Files
None
Subscribers
None
D19695.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D19695: [Wilds] Flesh out most of the new Config objects
Attached
Detach File
Event Timeline
Log In to Comment