Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15331947
D19697.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
51 KB
Referenced Files
None
Subscribers
None
D19697.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
@@ -49,9 +49,13 @@
'ArcanistAbstractMethodBodyXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistAbstractMethodBodyXHPASTLinterRuleTestCase.php',
'ArcanistAbstractPrivateMethodXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistAbstractPrivateMethodXHPASTLinterRule.php',
'ArcanistAbstractPrivateMethodXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistAbstractPrivateMethodXHPASTLinterRuleTestCase.php',
+ 'ArcanistAlias' => 'toolset/ArcanistAlias.php',
+ 'ArcanistAliasEffect' => 'toolset/ArcanistAliasEffect.php',
+ 'ArcanistAliasEngine' => 'toolset/ArcanistAliasEngine.php',
'ArcanistAliasFunctionXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistAliasFunctionXHPASTLinterRule.php',
'ArcanistAliasFunctionXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistAliasFunctionXHPASTLinterRuleTestCase.php',
'ArcanistAliasWorkflow' => 'toolset/workflow/ArcanistAliasWorkflow.php',
+ 'ArcanistAliasesConfigOption' => 'config/option/ArcanistAliasesConfigOption.php',
'ArcanistAmendWorkflow' => 'workflow/ArcanistAmendWorkflow.php',
'ArcanistAnoidWorkflow' => 'workflow/ArcanistAnoidWorkflow.php',
'ArcanistArcConfigurationEngineExtension' => 'config/arc/ArcanistArcConfigurationEngineExtension.php',
@@ -303,6 +307,7 @@
'ArcanistLintersWorkflow' => 'workflow/ArcanistLintersWorkflow.php',
'ArcanistListAssignmentXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistListAssignmentXHPASTLinterRule.php',
'ArcanistListAssignmentXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistListAssignmentXHPASTLinterRuleTestCase.php',
+ 'ArcanistListConfigOption' => 'config/option/ArcanistListConfigOption.php',
'ArcanistListWorkflow' => 'workflow/ArcanistListWorkflow.php',
'ArcanistLocalConfigurationSource' => 'config/source/ArcanistLocalConfigurationSource.php',
'ArcanistLogicalOperatorsXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistLogicalOperatorsXHPASTLinterRule.php',
@@ -491,6 +496,8 @@
'ArcanistWhichWorkflow' => 'workflow/ArcanistWhichWorkflow.php',
'ArcanistWildConfigOption' => 'config/option/ArcanistWildConfigOption.php',
'ArcanistWorkflow' => 'toolset/ArcanistWorkflow.php',
+ 'ArcanistWorkflowArgument' => 'toolset/ArcanistWorkflowArgument.php',
+ 'ArcanistWorkflowInformation' => 'toolset/ArcanistWorkflowInformation.php',
'ArcanistWorkingCopy' => 'workingcopy/ArcanistWorkingCopy.php',
'ArcanistWorkingCopyConfigurationSource' => 'config/source/ArcanistWorkingCopyConfigurationSource.php',
'ArcanistWorkingCopyStateRef' => 'ref/ArcanistWorkingCopyStateRef.php',
@@ -1145,9 +1152,13 @@
'ArcanistAbstractMethodBodyXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistAbstractPrivateMethodXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistAbstractPrivateMethodXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
+ 'ArcanistAlias' => 'Phobject',
+ 'ArcanistAliasEffect' => 'Phobject',
+ 'ArcanistAliasEngine' => 'Phobject',
'ArcanistAliasFunctionXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistAliasFunctionXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistAliasWorkflow' => 'ArcanistWorkflow',
+ 'ArcanistAliasesConfigOption' => 'ArcanistListConfigOption',
'ArcanistAmendWorkflow' => 'ArcanistWorkflow',
'ArcanistAnoidWorkflow' => 'ArcanistWorkflow',
'ArcanistArcConfigurationEngineExtension' => 'ArcanistConfigurationEngineExtension',
@@ -1399,6 +1410,7 @@
'ArcanistLintersWorkflow' => 'ArcanistWorkflow',
'ArcanistListAssignmentXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistListAssignmentXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
+ 'ArcanistListConfigOption' => 'ArcanistConfigOption',
'ArcanistListWorkflow' => 'ArcanistWorkflow',
'ArcanistLocalConfigurationSource' => 'ArcanistWorkingCopyConfigurationSource',
'ArcanistLogicalOperatorsXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
@@ -1587,6 +1599,8 @@
'ArcanistWhichWorkflow' => 'ArcanistWorkflow',
'ArcanistWildConfigOption' => 'ArcanistConfigOption',
'ArcanistWorkflow' => 'Phobject',
+ 'ArcanistWorkflowArgument' => 'Phobject',
+ 'ArcanistWorkflowInformation' => 'Phobject',
'ArcanistWorkingCopy' => 'Phobject',
'ArcanistWorkingCopyConfigurationSource' => 'ArcanistFilesystemConfigurationSource',
'ArcanistWorkingCopyStateRef' => 'ArcanistRef',
diff --git a/src/config/ArcanistConfigurationSourceList.php b/src/config/ArcanistConfigurationSourceList.php
--- a/src/config/ArcanistConfigurationSourceList.php
+++ b/src/config/ArcanistConfigurationSourceList.php
@@ -15,16 +15,80 @@
return $this->sources;
}
+ private function getSourcesWithScopes($scopes) {
+ if ($scopes !== null) {
+ $scopes = array_fuse($scopes);
+ }
+
+ $results = array();
+ foreach ($this->getSources() as $source) {
+ if ($scopes !== null) {
+ $scope = $source->getConfigurationSourceScope();
+ if ($scope === null) {
+ continue;
+ }
+ if (!isset($scopes[$scope])) {
+ continue;
+ }
+ }
+
+ $results[] = $source;
+ }
+
+ return $results;
+ }
+
+ public function getWritableSourceFromScope($scope) {
+ $sources = $this->getSourcesWithScopes(array($scope));
+
+ $writable = array();
+ foreach ($sources as $source) {
+ if (!$source->isWritableConfigurationSource()) {
+ continue;
+ }
+
+ $writable[] = $source;
+ }
+
+ if (!$writable) {
+ throw new Exception(
+ pht(
+ 'Unable to write configuration: there is no writable configuration '.
+ 'source in the "%s" scope.',
+ $scope));
+ }
+
+ if (count($writable) > 1) {
+ throw new Exception(
+ pht(
+ 'Unable to write configuration: more than one writable source '.
+ 'exists in the "%s" scope.',
+ $scope));
+ }
+
+ return head($writable);
+ }
+
public function getConfig($key) {
$option = $this->getConfigOption($key);
$values = $this->getStorageValueList($key);
return $option->getValueFromStorageValueList($values);
}
+ public function getConfigFromScopes($key, array $scopes) {
+ $option = $this->getConfigOption($key);
+ $values = $this->getStorageValueListFromScopes($key, $scopes);
+ return $option->getValueFromStorageValueList($values);
+ }
+
public function getStorageValueList($key) {
+ return $this->getStorageValueListFromScopes($key, null);
+ }
+
+ private function getStorageValueListFromScopes($key, $scopes) {
$values = array();
- foreach ($this->getSources() as $source) {
+ foreach ($this->getSourcesWithScopes($scopes) as $source) {
if ($source->hasValueForKey($key)) {
$value = $source->getValueForKey($key);
$values[] = new ArcanistConfigurationSourceValue(
@@ -113,7 +177,13 @@
$source,
$raw_value);
} catch (Exception $ex) {
- throw $ex;
+ throw new PhutilProxyException(
+ pht(
+ 'Configuration value ("%s") defined in source "%s" is not '.
+ 'valid.',
+ $key,
+ $source->getSourceDisplayName()),
+ $ex);
}
}
}
diff --git a/src/config/arc/ArcanistArcConfigurationEngineExtension.php b/src/config/arc/ArcanistArcConfigurationEngineExtension.php
--- a/src/config/arc/ArcanistArcConfigurationEngineExtension.php
+++ b/src/config/arc/ArcanistArcConfigurationEngineExtension.php
@@ -5,6 +5,8 @@
const EXTENSIONKEY = 'arc';
+ const KEY_ALIASES = 'aliases';
+
public function newConfigurationOptions() {
// TOOLSETS: Restore "load", and maybe this other stuff.
@@ -49,12 +51,6 @@
'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',
@@ -160,6 +156,14 @@
array(
'https://phabricator.mycompany.com/',
)),
+ id(new ArcanistAliasesConfigOption())
+ ->setKey(self::KEY_ALIASES)
+ ->setDefaultValue(array())
+ ->setSummary(pht('List of command aliases.'))
+ ->setHelp(
+ pht(
+ 'Configured command aliases. Use the "alias" workflow to define '.
+ 'aliases.')),
);
}
diff --git a/src/config/option/ArcanistAliasesConfigOption.php b/src/config/option/ArcanistAliasesConfigOption.php
new file mode 100644
--- /dev/null
+++ b/src/config/option/ArcanistAliasesConfigOption.php
@@ -0,0 +1,36 @@
+<?php
+
+final class ArcanistAliasesConfigOption
+ extends ArcanistListConfigOption {
+
+ public function getType() {
+ return 'list<alias>';
+ }
+
+ public function getValueFromStorageValue($value) {
+ if (!is_array($value)) {
+ throw new Exception(pht('Expected a list or dictionary!'));
+ }
+
+ $aliases = array();
+ foreach ($value as $key => $spec) {
+ $aliases[] = ArcanistAlias::newFromConfig($key, $spec);
+ }
+
+ return $aliases;
+ }
+
+ protected function didReadStorageValueList(array $list) {
+ assert_instances_of($list, 'ArcanistConfigurationSourceValue');
+ return mpull($list, 'getValue');
+ }
+
+ public function getDisplayValueFromValue($value) {
+ return pht('Use the "alias" workflow to review aliases.');
+ }
+
+ public function getStorageValueFromValue($value) {
+ return mpull($value, 'getStorageDictionary');
+ }
+
+}
diff --git a/src/config/option/ArcanistConfigOption.php b/src/config/option/ArcanistConfigOption.php
--- a/src/config/option/ArcanistConfigOption.php
+++ b/src/config/option/ArcanistConfigOption.php
@@ -67,9 +67,17 @@
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);
+ abstract public function getStorageValueFromValue($value);
+
+ public function getStorageValueFromStringValue($value) {
+ throw new Exception(
+ pht(
+ 'This configuration option ("%s") does not support runtime definition '.
+ 'with "--config".',
+ $this->getKey()));
+ }
protected function getStorageValueFromSourceValue(
ArcanistConfigurationSourceValue $source_value) {
@@ -84,5 +92,9 @@
return $value;
}
+ public function writeValue(ArcanistConfigurationSource $source, $value) {
+ $value = $this->getStorageValueFromValue($value);
+ $source->setStorageValueForKey($this->getKey(), $value);
+ }
}
diff --git a/src/config/option/ArcanistListConfigOption.php b/src/config/option/ArcanistListConfigOption.php
new file mode 100644
--- /dev/null
+++ b/src/config/option/ArcanistListConfigOption.php
@@ -0,0 +1,32 @@
+<?php
+
+abstract class ArcanistListConfigOption
+ extends ArcanistConfigOption {
+
+ public function getValueFromStorageValueList(array $list) {
+ assert_instances_of($list, 'ArcanistConfigurationSourceValue');
+
+ $result_list = array();
+ foreach ($list as $source_value) {
+ $source = $source_value->getConfigurationSource();
+ $storage_value = $this->getStorageValueFromSourceValue($source_value);
+
+ $items = $this->getValueFromStorageValue($storage_value);
+ foreach ($items as $item) {
+ $result_list[] = new ArcanistConfigurationSourceValue(
+ $source,
+ $item);
+ }
+ }
+
+ $result_list = $this->didReadStorageValueList($result_list);
+
+ return $result_list;
+ }
+
+ protected function didReadStorageValueList(array $list) {
+ assert_instances_of($list, 'ArcanistConfigurationSourceValue');
+ return mpull($list, 'getValue');
+ }
+
+}
diff --git a/src/config/option/ArcanistStringConfigOption.php b/src/config/option/ArcanistStringConfigOption.php
--- a/src/config/option/ArcanistStringConfigOption.php
+++ b/src/config/option/ArcanistStringConfigOption.php
@@ -15,4 +15,8 @@
return $value;
}
+ public function getStorageValueFromValue($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,15 +3,25 @@
abstract class ArcanistConfigurationSource
extends Phobject {
+ const SCOPE_USER = 'user';
+
abstract public function getSourceDisplayName();
abstract public function getAllKeys();
abstract public function hasValueForKey($key);
abstract public function getValueForKey($key);
+ public function getConfigurationSourceScope() {
+ return null;
+ }
+
public function isStringSource() {
return false;
}
+ public function isWritableConfigurationSource() {
+ return false;
+ }
+
public function didReadUnknownOption($key) {
// TOOLSETS: Standardize this kind of messaging? On ArcanistRuntime?
diff --git a/src/config/source/ArcanistDictionaryConfigurationSource.php b/src/config/source/ArcanistDictionaryConfigurationSource.php
--- a/src/config/source/ArcanistDictionaryConfigurationSource.php
+++ b/src/config/source/ArcanistDictionaryConfigurationSource.php
@@ -29,4 +29,16 @@
return $this->values[$key];
}
+ public function setStorageValueForKey($key, $value) {
+ $this->values[$key] = $value;
+
+ $this->writeToStorage($this->values);
+
+ return $this;
+ }
+
+ protected function writeToStorage($values) {
+ throw new PhutilMethodNotImplementedException();
+ }
+
}
\ 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
@@ -35,4 +35,11 @@
return $values;
}
+ protected function writeToStorage($values) {
+ $content = id(new PhutilJSON())
+ ->encodeFormatted($values);
+
+ Filesystem::writeFile($this->path, $content);
+ }
+
}
\ 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
@@ -7,6 +7,14 @@
return pht('User Config File');
}
+ public function isWritableConfigurationSource() {
+ return true;
+ }
+
+ public function getConfigurationSourceScope() {
+ return ArcanistConfigurationSource::SCOPE_USER;
+ }
+
public function didReadFilesystemValues(array $values) {
// Before toolsets, the "~/.arcrc" file had separate top-level keys for
// "config", "hosts", and "aliases". Transform this older file format into
diff --git a/src/toolset/ArcanistAlias.php b/src/toolset/ArcanistAlias.php
new file mode 100644
--- /dev/null
+++ b/src/toolset/ArcanistAlias.php
@@ -0,0 +1,145 @@
+<?php
+
+final class ArcanistAlias extends Phobject {
+
+ private $toolset;
+ private $trigger;
+ private $command;
+ private $exception;
+ private $configurationSource;
+
+ public static function newFromConfig($key, $value) {
+ $alias = new self();
+
+ // Parse older style aliases which were always for the "arc" toolset.
+ // When we next write these back into the config file, we'll update them
+ // to the modern format.
+
+ // The old format looked like this:
+ //
+ // {
+ // "draft": ["diff", "--draft"]
+ // }
+ //
+ // The new format looks like this:
+ //
+ // {
+ // [
+ // "toolset": "arc",
+ // "trigger": "draft",
+ // "command": ["diff", "--draft"]
+ // ]
+ // }
+ //
+ // For now, we parse the older format and fill in the toolset as "arc".
+
+ $is_list = false;
+ $is_dict = false;
+ if ($value && is_array($value)) {
+ if (array_keys($value) === range(0, count($value) - 1)) {
+ $is_list = true;
+ } else {
+ $is_dict = true;
+ }
+ }
+
+ if ($is_list) {
+ $alias->trigger = $key;
+ $alias->toolset = 'arc';
+ $alias->command = $value;
+ } else if ($is_dict) {
+ try {
+ PhutilTypeSpec::checkMap(
+ $value,
+ array(
+ 'trigger' => 'string',
+ 'toolset' => 'string',
+ 'command' => 'list<string>',
+ ));
+
+ $alias->trigger = idx($value, 'trigger');
+ $alias->toolset = idx($value, 'toolset');
+ $alias->command = idx($value, 'command');
+ } catch (PhutilTypeCheckException $ex) {
+ $alias->exception = new PhutilProxyException(
+ pht(
+ 'Found invalid alias definition (with key "%s").',
+ $key),
+ $ex);
+ }
+ } else {
+ $alias->exception = new Exception(
+ pht(
+ 'Expected alias definition (with key "%s") to be a dictionary.',
+ $key));
+ }
+
+ return $alias;
+ }
+
+ public function setToolset($toolset) {
+ $this->toolset = $toolset;
+ return $this;
+ }
+
+ public function getToolset() {
+ return $this->toolset;
+ }
+
+ public function setTrigger($trigger) {
+ $this->trigger = $trigger;
+ return $this;
+ }
+
+ public function getTrigger() {
+ return $this->trigger;
+ }
+
+ public function setCommand(array $command) {
+ $this->command = $command;
+ return $this;
+ }
+
+ public function getCommand() {
+ return $this->command;
+ }
+
+ public function setException(Exception $exception) {
+ $this->exception = $exception;
+ return $this;
+ }
+
+ public function getException() {
+ return $this->exception;
+ }
+
+ public function isShellCommandAlias() {
+ $command = $this->getCommand();
+ if (!$command) {
+ return false;
+ }
+
+ $head = head($command);
+ return preg_match('/^!/', $head);
+ }
+
+ public function getStorageDictionary() {
+ return array(
+ 'trigger' => $this->getTrigger(),
+ 'toolset' => $this->getToolset(),
+ 'command' => $this->getCommand(),
+ );
+ }
+
+ public function setConfigurationSource(
+ ArcanistConfigurationSource $configuration_source) {
+ $this->configurationSource = $configuration_source;
+ return $this;
+ }
+
+ public function getConfigurationSource() {
+ return $this->configurationSource;
+ }
+
+}
+
diff --git a/src/toolset/ArcanistAliasEffect.php b/src/toolset/ArcanistAliasEffect.php
new file mode 100644
--- /dev/null
+++ b/src/toolset/ArcanistAliasEffect.php
@@ -0,0 +1,57 @@
+<?php
+
+final class ArcanistAliasEffect
+ extends Phobject {
+
+ private $type;
+ private $command;
+ private $arguments;
+ private $message;
+
+ const EFFECT_MISCONFIGURATION = 'misconfiguration';
+ const EFFECT_SHELL = 'shell';
+ const EFFECT_RESOLUTION = 'resolution';
+ const EFFECT_SUGGEST = 'suggest';
+ const EFFECT_OVERRIDDE = 'override';
+ const EFFECT_ALIAS = 'alias';
+ const EFFECT_NOTFOUND = 'not-found';
+ const EFFECT_CYCLE = 'cycle';
+ const EFFECT_STACK = 'stack';
+
+ public function setType($type) {
+ $this->type = $type;
+ return $this;
+ }
+
+ public function getType() {
+ return $this->type;
+ }
+
+ public function setCommand($command) {
+ $this->command = $command;
+ return $this;
+ }
+
+ public function getCommand() {
+ return $this->command;
+ }
+
+ public function setArguments(array $arguments) {
+ $this->arguments = $arguments;
+ return $this;
+ }
+
+ public function getArguments() {
+ return $this->arguments;
+ }
+
+ public function setMessage($message) {
+ $this->message = $message;
+ return $this;
+ }
+
+ public function getMessage() {
+ return $this->message;
+ }
+
+}
\ No newline at end of file
diff --git a/src/toolset/ArcanistAliasEngine.php b/src/toolset/ArcanistAliasEngine.php
new file mode 100644
--- /dev/null
+++ b/src/toolset/ArcanistAliasEngine.php
@@ -0,0 +1,255 @@
+<?php
+
+final class ArcanistAliasEngine
+ extends Phobject {
+
+ private $runtime;
+ private $toolset;
+ private $workflows;
+ private $configurationSourceList;
+
+ public function setRuntime(ArcanistRuntime $runtime) {
+ $this->runtime = $runtime;
+ return $this;
+ }
+
+ public function getRuntime() {
+ return $this->runtime;
+ }
+
+ public function setToolset(ArcanistToolset $toolset) {
+ $this->toolset = $toolset;
+ return $this;
+ }
+
+ public function getToolset() {
+ return $this->toolset;
+ }
+
+ public function setWorkflows(array $workflows) {
+ assert_instances_of($workflows, 'ArcanistWorkflow');
+ $this->workflows = $workflows;
+ return $this;
+ }
+
+ public function getWorkflows() {
+ return $this->workflows;
+ }
+
+ public function setConfigurationSourceList(
+ ArcanistConfigurationSourceList $config) {
+ $this->configurationSourceList = $config;
+ return $this;
+ }
+
+ public function getConfigurationSourceList() {
+ return $this->configurationSourceList;
+ }
+
+ public function resolveAliases(array $argv) {
+ $aliases_key = ArcanistArcConfigurationEngineExtension::KEY_ALIASES;
+ $source_list = $this->getConfigurationSourceList();
+ $aliases = $source_list->getConfig($aliases_key);
+
+ $results = array();
+
+ // Identify aliases which had some kind of format or specification issue
+ // when loading config. We could possibly do this earlier, but it's nice
+ // to handle all the alias stuff in one place.
+
+ foreach ($aliases as $key => $alias) {
+ $exception = $alias->getException();
+
+ if (!$exception) {
+ continue;
+ }
+
+ // This alias is not defined properly, so we're going to ignore it.
+ unset($aliases[$key]);
+
+ $results[] = $this->newEffect(ArcanistAliasEffect::EFFECT_CONFIGURATION)
+ ->setMessage(
+ pht(
+ 'Configuration source ("%s") defines an invalid alias, which '.
+ 'will be ignored: %s',
+ $alias->getConfigurationSource()->getSourceDisplayName()),
+ $exception->getMessage());
+ }
+
+ $command = array_shift($argv);
+
+ $stack = array();
+ return $this->resolveAliasesForCommand(
+ $aliases,
+ $command,
+ $argv,
+ $results,
+ $stack);
+ }
+
+ private function resolveAliasesForCommand(
+ array $aliases,
+ $command,
+ array $argv,
+ array $results,
+ array $stack) {
+
+ $toolset = $this->getToolset();
+ $toolset_key = $toolset->getToolsetKey();
+
+ // If we have a command which resolves to a real workflow, match it and
+ // finish resolution. You can not overwrite a real workflow with an alias.
+
+ $workflows = $this->getWorkflows();
+ if (isset($workflows[$command])) {
+ $results[] = $this->newEffect(ArcanistAliasEffect::EFFECT_RESOLUTION)
+ ->setCommand($command)
+ ->setArguments($argv);
+ return $results;
+ }
+
+ // Find all the aliases which match whatever the user typed, like "draft".
+ // We look for aliases in other toolsets, too, so we can provide the user
+ // a hint when they type "phage draft" and mean "arc draft".
+
+ $matches = array();
+ $toolset_matches = array();
+ foreach ($aliases as $alias) {
+ if ($alias->getTrigger() === $command) {
+ $matches[] = $alias;
+ if ($alias->getToolset() == $toolset_key) {
+ $toolset_matches[] = $alias;
+ }
+ }
+ }
+
+ if (!$toolset_matches) {
+
+ // If the user typed "phage draft" and meant "arc draft", give them a
+ // hint that the alias exists somewhere else and they may have specified
+ // the wrong toolset.
+
+ foreach ($matches as $match) {
+ $results[] = $this->newEffect(ArcanistAliasEffect::EFFECT_SUGGEST)
+ ->setMessage(
+ pht(
+ 'No "%s %s" alias is defined, did you mean "%s %s"?',
+ $toolset_key,
+ $command,
+ $match->getToolset(),
+ $command));
+ }
+
+ // If the user misspells a command (like "arc hlep") and it doesn't match
+ // anything (no alias or workflow), we want to pass it through unmodified
+ // and let the parser try to correct the spelling into a real workflow
+ // later on.
+
+ // However, if the user correctly types a command (like "arc draft") that
+ // resolves at least once (so it hits a valid alias) but does not
+ // ultimately resolve into a valid workflow, we want to treat this as a
+ // hard failure.
+
+ // This could happen if you manually defined a bad alias, or a workflow
+ // you'd previously aliased to was removed, or you stacked aliases and
+ // then deleted one.
+
+ if ($stack) {
+ $results[] = $this->newEffect(ArcanistAliasEffect::EFFECT_NOTFOUND)
+ ->setMessage(
+ pht(
+ 'Alias resolved to "%s", but this is not a valid workflow or '.
+ 'alias name. This alias or workflow might have previously '.
+ 'existed and been removed.',
+ $command));
+ } else {
+ $results[] = $this->newEffect(ArcanistAliasEffect::EFFECT_RESOLUTION)
+ ->setCommand($command)
+ ->setArguments($argv);
+ }
+
+ return $results;
+ }
+
+ $alias = array_pop($toolset_matches);
+ foreach ($toolset_matches as $ignored_match) {
+ $results[] = $this->newEffect(ArcanistAliasEffect::EFFECT_IGNORED)
+ ->setMessage(
+ pht(
+ 'Multiple configuration sources define an alias for "%s %s". '.
+ 'The definition in "%s" will be ignored.',
+ $toolset_key,
+ $command,
+ $ignored_match->getConfigurationSource()->getSourceDisplayName()));
+ }
+
+ if ($alias->isShellCommandAlias()) {
+ $results[] = $this->newEffect(ArcanistAliasEffect::EFFECT_SHELL)
+ ->setMessage(
+ pht(
+ '%s %s -> $ %s',
+ $toolset_key,
+ $command,
+ $alias->getShellCommand()))
+ ->setCommand($command)
+ ->setArgv($argv);
+ return $results;
+ }
+
+ $alias_argv = $alias->getCommand();
+ $alias_command = array_shift($alias_argv);
+
+ if (isset($stack[$alias_command])) {
+
+ $cycle = array_keys($stack);
+ $cycle[] = $alias_command;
+ $cycle = implode(' -> ', $cycle);
+
+ $results[] = $this->newEffect(ArcanistAliasEffect::EFFECT_CYCLE)
+ ->setMessage(
+ pht(
+ 'Alias definitions form a cycle which can not be resolved: %s.',
+ $cycle));
+
+ return $results;
+ }
+
+ $stack[$alias_command] = true;
+
+ $stack_limit = 16;
+ if (count($stack) >= $stack_limit) {
+ $results[] = $this->newEffect(ArcanistAliasEffect::EFFECT_STACK)
+ ->setMessage(
+ pht(
+ 'Alias definitions form an unreasonably deep stack. A chain of '.
+ 'aliases may not resolve more than %s times.',
+ new PhutilNumber($stack_limit)));
+ return $results;
+ }
+
+ $results[] = $this->newEffect(ArcanistAliasEffect::EFFECT_ALIAS)
+ ->setMessage(
+ pht(
+ '%s %s -> %s %s',
+ $toolset_key,
+ $command,
+ $toolset_key,
+ $alias_command));
+
+ $argv = array_merge($alias_argv, $argv);
+
+ return $this->resolveAliasesForCommand(
+ $aliases,
+ $alias_command,
+ $argv,
+ $results,
+ $stack);
+ }
+
+ protected function newEffect($effect_type) {
+ return id(new ArcanistAliasEffect())
+ ->setType($effect_type);
+ }
+
+}
+
diff --git a/src/toolset/ArcanistWorkflow.php b/src/toolset/ArcanistWorkflow.php
--- a/src/toolset/ArcanistWorkflow.php
+++ b/src/toolset/ArcanistWorkflow.php
@@ -2,6 +2,7 @@
abstract class ArcanistWorkflow extends Phobject {
+ private $runtime;
private $toolset;
private $arguments;
private $configurationEngine;
@@ -37,10 +38,48 @@
return true;
}
+ protected function getWorkflowArguments() {
+ // TOOLSETS: Temporary!
+ return array();
+ }
+
+ protected function getWorkflowInformation() {
+ // TOOLSETS: Temporary!
+ return null;
+ }
+
+
public function newPhutilWorkflow() {
- return id(new ArcanistPhutilWorkflow())
+ $arguments = $this->getWorkflowArguments();
+ assert_instances_of($arguments, 'ArcanistWorkflowArgument');
+
+ $specs = mpull($arguments, 'getPhutilSpecification');
+
+ $phutil_workflow = id(new ArcanistPhutilWorkflow())
->setName($this->getWorkflowName())
- ->setWorkflow($this);
+ ->setWorkflow($this)
+ ->setArguments($specs);
+
+ $information = $this->getWorkflowInformation();
+ if ($information) {
+
+ $examples = $information->getExamples();
+ if ($examples) {
+ $examples = implode("\n", $examples);
+ $phutil_workflow->setExamples($examples);
+ }
+
+ $help = $information->getHelp();
+ if (strlen($help)) {
+ // Unwrap linebreaks in the help text so we don't get weird formatting.
+ $help = preg_replace("/(?<=\S)\n(?=\S)/", " ", $help);
+
+ $phutil_workflow->setHelp($help);
+ }
+
+ }
+
+ return $phutil_workflow;
}
final public function getToolset() {
@@ -52,6 +91,19 @@
return $this;
}
+ final public function setRuntime(ArcanistRuntime $runtime) {
+ $this->runtime = $runtime;
+ return $this;
+ }
+
+ final public function getRuntime() {
+ return $this->runtime;
+ }
+
+ final public function getConfig($key) {
+ return $this->getConfigurationSourceList()->getConfig($key);
+ }
+
final public function setConfigurationSourceList(
ArcanistConfigurationSourceList $config) {
$this->configurationSourceList = $config;
@@ -99,11 +151,17 @@
return $err;
}
- final public function getArgument($key, $default = null) {
- // TOOLSETS: This is a stub for now.
- return $default;
+ final public function getArgument($key) {
+ return $this->arguments->getArg($key);
+ }
+
+ final protected function newWorkflowArgument($key) {
+ return id(new ArcanistWorkflowArgument())
+ ->setKey($key);
+ }
- return $this->arguments->getArg($key, $default);
+ final protected function newWorkflowInformation() {
+ return new ArcanistWorkflowInformation();
}
}
diff --git a/src/toolset/ArcanistWorkflowArgument.php b/src/toolset/ArcanistWorkflowArgument.php
new file mode 100644
--- /dev/null
+++ b/src/toolset/ArcanistWorkflowArgument.php
@@ -0,0 +1,50 @@
+<?php
+
+final class ArcanistWorkflowArgument
+ extends Phobject {
+
+ private $key;
+ private $help;
+ private $wildcard;
+
+ public function setKey($key) {
+ $this->key = $key;
+ return $this;
+ }
+
+ public function getKey() {
+ return $this->key;
+ }
+
+ public function setWildcard($wildcard) {
+ $this->wildcard = $wildcard;
+ return $this;
+ }
+
+ public function getWildcard() {
+ return $this->wildcard;
+ }
+
+ public function getPhutilSpecification() {
+ $spec = array(
+ 'name' => $this->getKey(),
+ );
+
+ if ($this->getWildcard()) {
+ $spec['wildcard'] = true;
+ }
+
+ return $spec;
+ }
+
+ public function setHelp($help) {
+ $this->help = $help;
+ return $this;
+ }
+
+ public function getHelp() {
+ return $this->help;
+ }
+
+}
+
diff --git a/src/toolset/ArcanistWorkflowInformation.php b/src/toolset/ArcanistWorkflowInformation.php
new file mode 100644
--- /dev/null
+++ b/src/toolset/ArcanistWorkflowInformation.php
@@ -0,0 +1,28 @@
+<?php
+
+final class ArcanistWorkflowInformation
+ extends Phobject {
+
+ private $help;
+ private $examples = array();
+
+ public function setHelp($help) {
+ $this->help = $help;
+ return $this;
+ }
+
+ public function getHelp() {
+ return $this->help;
+ }
+
+ public function addExample($example) {
+ $this->examples[] = $example;
+ return $this;
+ }
+
+ public function getExamples() {
+ return $this->examples;
+ }
+
+}
+
diff --git a/src/toolset/workflow/ArcanistAliasWorkflow.php b/src/toolset/workflow/ArcanistAliasWorkflow.php
--- a/src/toolset/workflow/ArcanistAliasWorkflow.php
+++ b/src/toolset/workflow/ArcanistAliasWorkflow.php
@@ -13,232 +13,186 @@
return true;
}
- public function getWorkflowSynopses() {
- return array(
- pht('**alias**'),
- pht('**alias** __command__'),
- pht('**alias** __command__ __target__ -- [__options__]'),
- );
- }
-
- public function getWorkflowHelp() {
- return pht(<<<EOTEXT
-Supports: cli
+ public function getWorkflowInformation() {
+ $help = pht(<<<EOTEXT
Create an alias from __command__ to __target__ (optionally, with __options__).
-For example:
- %s alias fpatch patch -- --force
+Aliases allow you to create shorthands for commands and sets of flags you
+commonly use, like defining "arc draft" as a shorthand for "arc diff --draft".
+
+**Creating Aliases**
+
+You can define "arc draft" as a shorthand for "arc diff --draft" like this:
+
+ $ arc alias draft diff -- --draft
-...will create a new 'arc' command, 'arc fpatch', which invokes
-'arc patch --force ...' when run. NOTE: use "--" before specifying
-options!
+Now, when you run "arc draft", the command will function like
+"arc diff --draft".
-If you start an alias with "!", the remainder of the alias will be
-invoked as a shell command. For example, if you want to implement
-'arc ls', you can do so like this:
+<bg:yellow> NOTE: </bg> Make sure you use "--" before specifying any flags you
+want to pass to the command! Otherwise, the flags will be interpreted as flags
+to "arc alias".
- %s alias ls '!ls'
+**Listing Aliases**
-You can now run "arc ls" and it will behave like "ls". Of course, this
-example is silly and would make your life worse.
+Without any arguments, "arc alias" will list aliases.
-You can not overwrite builtins, including 'alias' itself. The builtin
-will always execute, even if it was added after your alias.
+**Removing Aliases**
To remove an alias, run:
- arc alias fpatch
+ $ arc alias <alias-name>
+
+You will be prompted to remove the alias.
+
+**Shell Commands**
+
+If you begin an alias with "!", the remainder of the alias will be invoked as
+a shell command. For example, if you want to implement "arc ls", you can do so
+like this:
+
+ $ arc alias ls '!ls'
+
+When run, "arc ls" will now behave like "ls".
+
+**Multiple Toolsets**
-Without any arguments, 'arc alias' will list aliases.
+This workflow supports any toolset, even though the examples in this help text
+use "arc". If you are working with another toolset, use the binary for that
+toolset define aliases for it:
+
+ $ phage alias ...
+
+Aliases are bound to the toolset which was used to define them. If you define
+an "arc draft" alias, that does not also define a "phage draft" alias.
+
+**Builtins**
+
+You can not overwrite the behavior of builtin workflows, including "alias"
+itself, and if you install a new workflow it will take precedence over any
+existing aliases with the same name.
EOTEXT
- ,
- $this->getToolsetName());
+);
+
+ return $this->newWorkflowInformation()
+ ->addExample(pht('**alias**'))
+ ->addExample(pht('**alias** __command__'))
+ ->addExample(pht('**alias** __command__ __target__ -- [__options__]'))
+ ->setHelp($help);
}
- public function getArguments() {
+ public function getWorkflowArguments() {
return array(
- '*' => 'argv',
+ $this->newWorkflowArgument('json')
+ ->setHelp(pht('Output aliases in JSON format.')),
+ $this->newWorkflowArgument('argv')
+ ->setWildcard(true),
);
}
- public static function getAliases(
- ArcanistConfigurationManager $configuration_manager) {
- $sources = $configuration_manager->getConfigFromAllSources('aliases');
+ public function runWorkflow() {
+ $argv = $this->getArgument('argv');
- $aliases = array();
- foreach ($sources as $source) {
- $aliases += $source;
- }
+ $is_list = false;
+ $is_delete = false;
- return $aliases;
- }
+ if (!$argv) {
+ $is_list = true;
+ } else if (count($argv) === 1) {
+ $is_delete = true;
+ }
- private function writeAliases(array $aliases) {
- $config = $this->getConfigurationManager()->readUserConfigurationFile();
- $config['aliases'] = $aliases;
- $this->getConfigurationManager()->writeUserConfigurationFile($config);
- }
+ $is_json = $this->getArgument('json');
+ if ($is_json && !$is_list) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'The "--json" argument may only be used when listing aliases.'));
+ }
- public function runWorkflow() {
- $aliases = self::getAliases($this->getConfigurationManager());
+ if ($is_list) {
+ return $this->runListAliases();
+ }
- $argv = $this->getArgument('argv');
- if (count($argv) == 0) {
- $this->printAliases($aliases);
- } else if (count($argv) == 1) {
- $this->removeAlias($aliases, $argv[0]);
- } else {
- $arc_config = $this->getArcanistConfiguration();
- $alias = $argv[0];
-
- if ($arc_config->buildWorkflow($alias)) {
- throw new ArcanistUsageException(
- pht(
- 'You can not create an alias for "%s" because it is a '.
- 'builtin command. "%s" can only create new commands.',
- "arc {$alias}",
- 'arc alias'));
- }
-
- $new_alias = array_slice($argv, 1);
-
- $command = implode(' ', $new_alias);
- if (self::isShellCommandAlias($command)) {
- echo tsprintf(
- "%s\n",
- pht(
- 'Aliased "%s" to shell command "%s".',
- "arc {$alias}",
- substr($command, 1)));
- } else {
- echo tsprintf(
- "%s\n",
- pht(
- 'Aliased "%s" to "%s".',
- "arc {$alias}",
- "arc {$command}"));
- }
-
- $aliases[$alias] = $new_alias;
- $this->writeAliases($aliases);
+ if ($is_delete) {
+ return $this->runDeleteAlias($argv[1]);
}
- return 0;
+ return $this->runCreateAlias($argv);
}
- public static function isShellCommandAlias($command) {
- return preg_match('/^!/', $command);
+ private function runListAliases() {
+ // TOOLSETS: Actually list aliases.
+ return 1;
}
- public static function resolveAliases(
- $command,
- ArcanistRuntime $config,
- array $argv,
- ArcanistConfigurationManager $configuration_manager) {
+ private function runDeleteAlias($alias) {
+ // TOOLSETS: Actually delete aliases.
+ return 1;
+ }
- $aliases = self::getAliases($configuration_manager);
- if (!isset($aliases[$command])) {
- return array(null, $argv);
- }
+ private function runCreateAlias(array $argv) {
+ $trigger = array_shift($argv);
+ $this->validateAliasTrigger($trigger);
- $new_command = head($aliases[$command]);
+ $alias = id(new ArcanistAlias())
+ ->setToolset($this->getToolsetKey())
+ ->setTrigger($trigger)
+ ->setCommand($argv);
- if (self::isShellCommandAlias($new_command)) {
- return array($new_command, $argv);
- }
+ $aliases = $this->readAliasesForWrite();
- $workflow = $config->buildWorkflow($new_command);
- if (!$workflow) {
- return array(null, $argv);
- }
+ // TOOLSETS: Check if the user already has an alias for this trigger, and
+ // prompt them to overwrite it. Needs prompting to work.
- $alias_argv = array_slice($aliases[$command], 1);
- foreach (array_reverse($alias_argv) as $alias_arg) {
- if (!in_array($alias_arg, $argv)) {
- array_unshift($argv, $alias_arg);
- }
- }
+ $aliases[] = $alias;
- return array($new_command, $argv);
+ $this->writeAliases($aliases);
+
+ return 0;
}
- private function printAliases(array $aliases) {
- if (!$aliases) {
- echo tsprintf(
- "%s\n",
- pht('You have not defined any aliases yet.'));
- return;
- }
+ private function validateAliasTrigger($trigger) {
+ $workflows = $this->getRuntime()->getWorkflows();
- $table = id(new PhutilConsoleTable())
- ->addColumn('input', array('title' => pht('Alias')))
- ->addColumn('command', array('title' => pht('Command')))
- ->addColumn('type', array('title' => pht('Type')));
-
- ksort($aliases);
-
- foreach ($aliases as $alias => $binding) {
- $command = implode(' ', $binding);
- if (self::isShellCommandAlias($command)) {
- $command = substr($command, 1);
- $type = pht('Shell Command');
- } else {
- $command = "arc {$command}";
- $type = pht('Arcanist Command');
- }
-
- $row = array(
- 'input' => "arc {$alias}",
- 'type' => $type,
- 'command' => $command,
- );
-
- $table->addRow($row);
+ if (isset($workflows[$trigger])) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'You can not define an alias for "%s" because it is a builtin '.
+ 'workflow for the current toolset ("%s"). The "alias" workflow '.
+ 'can only define new commands as aliases; it can not redefine '.
+ 'existing commands to mean something else.',
+ $trigger,
+ $this->getToolsetKey()));
}
+ }
- $table->draw();
+ private function getEditScope() {
+ return ArcanistConfigurationSource::SCOPE_USER;
}
- private function removeAlias(array $aliases, $alias) {
- if (empty($aliases[$alias])) {
- echo tsprintf(
- "%s\n",
- pht('No alias "%s" to remove.', $alias));
- return;
- }
+ private function getAliasesConfigKey() {
+ return ArcanistArcConfigurationEngineExtension::KEY_ALIASES;
+ }
- $command = implode(' ', $aliases[$alias]);
+ private function readAliasesForWrite() {
+ $key = $this->getAliasesConfigKey();
+ $scope = $this->getEditScope();
+ $source_list = $this->getConfigurationSourceList();
- if (self::isShellCommandAlias($command)) {
- echo tsprintf(
- "%s\n",
- pht(
- '"%s" is currently aliased to shell command "%s".',
- "arc {$alias}",
- substr($command, 1)));
- } else {
- echo tsprintf(
- "%s\n",
- pht(
- '"%s" is currently aliased to "%s".',
- "arc {$alias}",
- "arc {$command}"));
- }
+ return $source_list->getConfigFromScopes($key, array($scope));
+ }
+ private function writeAliases(array $aliases) {
+ assert_instances_of($aliases, 'ArcanistAlias');
- $ok = phutil_console_confirm(pht('Delete this alias?'));
- if (!$ok) {
- throw new ArcanistUserAbortException();
- }
+ $key = $this->getAliasesConfigKey();
+ $scope = $this->getEditScope();
- unset($aliases[$alias]);
- $this->writeAliases($aliases);
+ $source_list = $this->getConfigurationSourceList();
+ $source = $source_list->getWritableSourceFromScope($scope);
+ $option = $source_list->getConfigOption($key);
- echo tsprintf(
- "%s\n",
- pht(
- 'Removed alias "%s".',
- "arc {$alias}"));
+ $option->writeValue($source, $aliases);
}
}
diff --git a/src/workflow/ArcanistLiberateWorkflow.php b/src/workflow/ArcanistLiberateWorkflow.php
--- a/src/workflow/ArcanistLiberateWorkflow.php
+++ b/src/workflow/ArcanistLiberateWorkflow.php
@@ -1,70 +1,32 @@
<?php
-/**
- * Create and update libphutil libraries.
- *
- * This workflow is unusual and involves re-executing 'arc liberate' as a
- * subprocess with `--remap` and `--verify`. This is because there is no way
- * to unload or reload a library, so every process is stuck with the library
- * definition it had when it first loaded. This is normally fine, but
- * problematic in this case because `arc liberate` modifies library definitions.
- */
final class ArcanistLiberateWorkflow extends ArcanistWorkflow {
public function getWorkflowName() {
return 'liberate';
}
- public function getCommandSynopses() {
- return phutil_console_format(<<<EOTEXT
- **liberate** [__path__]
-EOTEXT
- );
- }
+ public function getWorkflowInformation() {
+ // TOOLSETS: Expand this help.
- public function getCommandHelp() {
- return phutil_console_format(<<<EOTEXT
- Supports: libphutil
- Create or update a libphutil library, generating required metadata
- files like \__init__.php.
+ $help = pht(<<<EOTEXT
+Create or update an Arcanist library.
EOTEXT
- );
+);
+
+ return $this->newWorkflowInformation()
+ ->addExample(pht('**liberate**'))
+ ->addExample(pht('**liberate** [__path__]'))
+ ->setHelp($help);
}
- public function getArguments() {
+ public function getWorkflowArguments() {
return array(
- 'all' => array(
- 'help' => pht(
- 'Drop the module cache before liberating. This will completely '.
- 'reanalyze the entire library. Thorough, but slow!'),
- ),
- 'force-update' => array(
- 'help' => pht(
- 'Force the library map to be updated, even in the presence of '.
- 'lint errors.'),
- ),
- 'library-name' => array(
- 'param' => 'name',
- 'help' =>
- pht('Use a flag for library name rather than awaiting user input.'),
- ),
- 'remap' => array(
- 'hide' => true,
- 'help' => pht(
- 'Internal. Run the remap step of liberation. You do not need to '.
- 'run this unless you are debugging the workflow.'),
- ),
- 'verify' => array(
- 'hide' => true,
- 'help' => pht(
- 'Internal. Run the verify step of liberation. You do not need to '.
- 'run this unless you are debugging the workflow.'),
- ),
- 'upgrade' => array(
- 'hide' => true,
- 'help' => pht('Experimental. Upgrade library to v2.'),
- ),
- '*' => 'argv',
+ $this->newWorkflowArgument('clean')
+ ->setHelp(
+ pht('Perform a clean rebuild, ignoring caches. Thorough, but slow.')),
+ $this->newWorkflowArgument('argv')
+ ->setWildcard(true),
);
}
@@ -97,9 +59,6 @@
);
}
- $is_remap = $this->getArgument('remap');
- $is_verify = $this->getArgument('verify');
-
foreach ($paths as $path) {
$this->liberatePath($path);
}
@@ -122,19 +81,10 @@
$version = $this->getLibraryFormatVersion($path);
switch ($version) {
case 1:
- if ($this->getArgument('upgrade')) {
- return $this->upgradeLibrary($path);
- }
throw new ArcanistUsageException(
pht(
- "This library is using libphutil v1, which is no ".
- "longer supported. Run '%s' to upgrade to v2.",
- 'arc liberate --upgrade'));
+ 'This very old library is no longer supported.'));
case 2:
- if ($this->getArgument('upgrade')) {
- throw new ArcanistUsageException(
- pht("Can't upgrade a v2 library!"));
- }
return $this->liberateVersion2($path);
default:
throw new ArcanistUsageException(
@@ -165,25 +115,10 @@
return phutil_passthru(
'php %s %C %s',
$bin,
- $this->getArgument('all') ? '--drop-cache' : '',
+ $this->getArgument('clean') ? '--drop-cache' : '',
$path);
}
- private function upgradeLibrary($path) {
- $inits = id(new FileFinder($path))
- ->withPath('*/__init__.php')
- ->withType('f')
- ->find();
-
- echo pht('Removing %s files...', '__init__.php')."\n";
- foreach ($inits as $init) {
- Filesystem::remove($path.'/'.$init);
- }
-
- echo pht('Upgrading library to v2...')."\n";
- $this->liberateVersion2($path);
- }
-
private function liberateCreateDirectory($path) {
if (Filesystem::pathExists($path)) {
if (!is_dir($path)) {
diff --git a/support/ArcanistRuntime.php b/support/ArcanistRuntime.php
--- a/support/ArcanistRuntime.php
+++ b/support/ArcanistRuntime.php
@@ -2,6 +2,8 @@
final class ArcanistRuntime {
+ private $workflows;
+
public function execute(array $argv) {
try {
@@ -81,12 +83,14 @@
$args->parsePartial($toolset->getToolsetArguments());
$workflows = $this->newWorkflows($toolset);
+ $this->workflows = $workflows;
$phutil_workflows = array();
foreach ($workflows as $key => $workflow) {
$phutil_workflows[$key] = $workflow->newPhutilWorkflow();
$workflow
+ ->setRuntime($this)
->setConfigurationEngine($config_engine)
->setConfigurationSourceList($config);
}
@@ -104,12 +108,16 @@
throw new PhutilArgumentUsageException(pht('Choose a workflow!'));
}
- $result = $this->resolveAliases($workflows, $unconsumed_argv, $config);
- if (is_int($result)) {
- return $result;
- }
+ $alias_effects = id(new ArcanistAliasEngine())
+ ->setRuntime($this)
+ ->setToolset($toolset)
+ ->setWorkflows($workflows)
+ ->setConfigurationSourceList($config)
+ ->resolveAliases($unconsumed_argv);
- $args->setUnconsumedArgumentVector($result);
+ $result_argv = $this->applyAliasEffects($alias_effects, $unconsumed_argv);
+
+ $args->setUnconsumedArgumentVector($result_argv);
return $args->parseWorkflows($phutil_workflows);
}
@@ -468,70 +476,45 @@
return $map;
}
- private function resolveAliases(
- array $workflows,
- array $argv,
- ArcanistConfigurationSourceList $config) {
-
- return $argv;
+ private function logTrace($label, $message) {
+ echo tsprintf(
+ "**<bg:magenta> %s </bg>** %s\n",
+ $label,
+ $message);
+ }
- $command = head($argv);
+ public function getWorkflows() {
+ return $this->workflows;
+ }
- // If this is a match for a recognized workflow, just return the arguments
- // unmodified. You aren't allowed to alias over real workflows.
- if (isset($workflows[$command])) {
- return $argv;
- }
+ private function applyAliasEffects(array $effects, array $argv) {
+ assert_instances_of($effects, 'ArcanistAliasEffect');
+
+ $command = null;
+ $arguments = null;
+ foreach ($effects as $effect) {
+ $message = $effect->getMessage();
+
+ if ($message !== null) {
+ fprintf(
+ STDERR,
+ tsprintf(
+ "**<bg:yellow> %s </bg>** %s\n",
+ pht('ALIAS'),
+ $message));
+ }
- $aliases = ArcanistAliasWorkflow::getAliases($config);
- list($new_command, $new_args) = ArcanistAliasWorkflow::resolveAliases(
- $command,
- $this,
- array_slice($argv, 1),
- $config);
-
- // You can't alias something to itself, so if the new command isn't new,
- // we're all done resolving aliases.
- if ($new_command === $command) {
- return $argv;
+ if ($effect->getCommand()) {
+ $command = $effect->getCommand();
+ $arguments = $effect->getArguments();
+ }
}
- $full_alias = idx($aliases, $command, array());
- $full_alias = implode(' ', $full_alias);
-
- // Run shell command aliases.
- if (ArcanistAliasWorkflow::isShellCommandAlias($new_command)) {
- fwrite(
- STDERR,
- tsprintf(
- '**<bg:green> %s </bg>** arc %s -> $ %s',
- pht('ALIAS'),
- $command,
- $shell_cmd));
-
- $shell_cmd = substr($full_alias, 1);
-
- return phutil_passthru('%C %Ls', $shell_cmd, $args);
+ if ($command !== null) {
+ $argv = array_merge(array($command), $arguments);
}
- fwrite(
- STDERR,
- tsprintf(
- '**<bg:green> %s </bg>** arc %s -> arc %s',
- pht('ALIAS'),
- $command,
- $new_command));
-
- $new_argv = array_merge(array($new_command), $new_args);
-
- return $this->resolveAliases($workflows, $new_argv, $config);
- }
-
- private function logTrace($label, $message) {
- echo tsprintf(
- "**<bg:magenta> %s </bg>** %s\n",
- $label,
- $message);
+ return $argv;
}
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Mar 8, 3:33 PM (2 w, 1 d ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/6i/a3/giydbznmlop6qzad
Default Alt Text
D19697.diff (51 KB)
Attached To
Mode
D19697: [Wilds] Continue toward a generalized "arc alias" workflow
Attached
Detach File
Event Timeline
Log In to Comment