From b058efbb51769e1d7ba9fea13a2f736dfdf0ee3c Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 21 Feb 2012 13:16:52 -0800 Subject: [PATCH] Add an "arc alias" command Summary: Allow users to easily define aliased arc commands. This is easier than making them muck around with shell stuff, and lets me do stuff like "arc alias adiff diff -- --auto" so I can test --auto more easily. There are some limitations here (for example, you can't put --trace in an alias because it gets parsed too early) but I think it's a reasonable starting point. Test Plan: Set, listed, removed and used aliases. Reviewers: btrahan Reviewed By: btrahan CC: aran, epriestley Maniphest Tasks: T896 Differential Revision: https://secure.phabricator.com/D1653 --- scripts/arcanist.php | 31 +++- src/__phutil_library_map__.php | 2 + src/configuration/ArcanistConfiguration.php | 4 + src/workflow/alias/ArcanistAliasWorkflow.php | 153 ++++++++++++++++++ src/workflow/alias/__init__.php | 17 ++ src/workflow/base/ArcanistBaseWorkflow.php | 11 ++ src/workflow/base/__init__.php | 1 + .../ArcanistInstallCertificateWorkflow.php | 8 +- src/workflow/install-certificate/__init__.php | 3 - .../ArcanistShellCompleteWorkflow.php | 22 ++- src/workflow/shell-complete/__init__.php | 1 + 11 files changed, 239 insertions(+), 14 deletions(-) create mode 100644 src/workflow/alias/ArcanistAliasWorkflow.php create mode 100644 src/workflow/alias/__init__.php diff --git a/scripts/arcanist.php b/scripts/arcanist.php index 4f772a63..1e84eb3c 100755 --- a/scripts/arcanist.php +++ b/scripts/arcanist.php @@ -164,15 +164,40 @@ try { } $command = strtolower($args[0]); + $args = array_slice($args, 1); $workflow = $config->buildWorkflow($command); if (!$workflow) { - throw new ArcanistUsageException( - "Unknown command '{$command}'. Try 'arc help'."); + + // If the user has an alias, like 'arc alias dhelp diff help', look it up + // and substitute it. We do this only after trying to resolve the workflow + // normally to prevent you from doing silly things like aliasing 'alias' + // to something else. + + list($new_command, $args) = ArcanistAliasWorkflow::resolveAliases( + $command, + $config, + $args); + + if ($new_command) { + $workflow = $config->buildWorkflow($new_command); + } + + if (!$workflow) { + throw new ArcanistUsageException( + "Unknown command '{$command}'. Try 'arc help'."); + } else { + if ($config_trace_mode) { + $aliases = ArcanistAliasWorkflow::getAliases(); + $target = implode(' ', idx($aliases, $command, array())); + echo "[alias: 'arc {$command}' -> 'arc {$target}']\n"; + } + $command = $new_command; + } } $workflow->setArcanistConfiguration($config); $workflow->setCommand($command); $workflow->setWorkingDirectory($working_directory); - $workflow->parseArguments(array_slice($args, 1)); + $workflow->parseArguments($args); $need_working_copy = $workflow->requiresWorkingCopy(); $need_conduit = $workflow->requiresConduit(); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index d94b89b1..c6f3b428 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -8,6 +8,7 @@ phutil_register_library_map(array( 'class' => array( + 'ArcanistAliasWorkflow' => 'workflow/alias', 'ArcanistAmendWorkflow' => 'workflow/amend', 'ArcanistApacheLicenseLinter' => 'lint/linter/apachelicense', 'ArcanistApacheLicenseLinterTestCase' => 'lint/linter/apachelicense/__tests__', @@ -116,6 +117,7 @@ phutil_register_library_map(array( ), 'requires_class' => array( + 'ArcanistAliasWorkflow' => 'ArcanistBaseWorkflow', 'ArcanistAmendWorkflow' => 'ArcanistBaseWorkflow', 'ArcanistApacheLicenseLinter' => 'ArcanistLicenseLinter', 'ArcanistApacheLicenseLinterTestCase' => 'ArcanistLinterTestCase', diff --git a/src/configuration/ArcanistConfiguration.php b/src/configuration/ArcanistConfiguration.php index 1a6eadda..e89e7f0c 100644 --- a/src/configuration/ArcanistConfiguration.php +++ b/src/configuration/ArcanistConfiguration.php @@ -100,6 +100,10 @@ class ArcanistConfiguration { return $workflows; } + final public function isValidWorkflow($workflow) { + return (bool)$this->buildWorkflow($workflow); + } + public function willRunWorkflow($command, ArcanistBaseWorkflow $workflow) { // This is a hook. } diff --git a/src/workflow/alias/ArcanistAliasWorkflow.php b/src/workflow/alias/ArcanistAliasWorkflow.php new file mode 100644 index 00000000..2afa45ba --- /dev/null +++ b/src/workflow/alias/ArcanistAliasWorkflow.php @@ -0,0 +1,153 @@ + 'argv', + ); + } + + public static function getAliases() { + $config = self::readUserConfigurationFile(); + return idx($config, 'aliases'); + } + + private function writeAliases(array $aliases) { + $config = self::readUserConfigurationFile(); + $config['aliases'] = $aliases; + self::writeUserConfigurationFile($config); + } + + public function run() { + + $aliases = self::getAliases(); + + $argv = $this->getArgument('argv'); + if (count($argv) == 0) { + if ($aliases) { + foreach ($aliases as $alias => $binding) { + echo phutil_console_format( + "**%s** %s\n", + $alias, + implode(' ' , $binding)); + } + } else { + echo "You haven't defined any aliases yet.\n"; + } + } else if (count($argv) == 1) { + if (empty($aliases[$argv[0]])) { + echo "No alias '{$argv[0]}' to remove.\n"; + } else { + echo phutil_console_format( + "'**arc %s**' is currently aliased to '**arc %s**'.", + $argv[0], + implode(' ', $aliases[$argv[0]])); + $ok = phutil_console_confirm('Delete this alias?'); + if ($ok) { + $was = implode(' ', $aliases[$argv[0]]); + unset($aliases[$argv[0]]); + $this->writeAliases($aliases); + echo "Unaliased '{$argv[0]}' (was '{$was}').\n"; + } else { + throw new ArcanistUserAbortException(); + } + } + } else { + $arc_config = $this->getArcanistConfiguration(); + + if ($arc_config->buildWorkflow($argv[0])) { + throw new ArcanistUsageException( + "You can not create an alias for '{$argv[0]}' because it is a ". + "builtin command. 'arc alias' can only create new commands."); + } + + $aliases[$argv[0]] = array_slice($argv, 1); + + echo phutil_console_format( + "Aliased '**arc %s**' to '**arc %s**'.\n", + $argv[0], + implode(' ', $aliases[$argv[0]])); + + $this->writeAliases($aliases); + } + + return 0; + } + + public static function resolveAliases( + $command, + ArcanistConfiguration $config, + array $argv) { + + $aliases = ArcanistAliasWorkflow::getAliases(); + if (!isset($aliases[$command])) { + return array(null, $argv); + } + + $new_command = head($aliases[$command]); + $workflow = $config->buildWorkflow($new_command); + if (!$workflow) { + return array(null, $argv); + } + + $alias_argv = array_slice($aliases[$command], 1); + foreach ($alias_argv as $alias_arg) { + if (!in_array($alias_arg, $argv)) { + array_unshift($argv, $alias_arg); + } + } + + return array($new_command, $argv); + } + +} diff --git a/src/workflow/alias/__init__.php b/src/workflow/alias/__init__.php new file mode 100644 index 00000000..e5111ce4 --- /dev/null +++ b/src/workflow/alias/__init__.php @@ -0,0 +1,17 @@ +encodeFormatted($config); + + $path = self::getUserConfigurationFileLocation(); + Filesystem::writeFile($path, $json); + execx('chmod 600 %s', $path); + } + + /** * Write a message to stderr so that '--json' flags or stdout which is meant * to be piped somewhere aren't disrupted. diff --git a/src/workflow/base/__init__.php b/src/workflow/base/__init__.php index f23596cf..49420f4d 100644 --- a/src/workflow/base/__init__.php +++ b/src/workflow/base/__init__.php @@ -21,6 +21,7 @@ phutil_require_module('phutil', 'console'); phutil_require_module('phutil', 'filesystem'); phutil_require_module('phutil', 'future/exec'); phutil_require_module('phutil', 'moduleutils'); +phutil_require_module('phutil', 'parser/json'); phutil_require_module('phutil', 'utils'); diff --git a/src/workflow/install-certificate/ArcanistInstallCertificateWorkflow.php b/src/workflow/install-certificate/ArcanistInstallCertificateWorkflow.php index 36aadc93..38160630 100644 --- a/src/workflow/install-certificate/ArcanistInstallCertificateWorkflow.php +++ b/src/workflow/install-certificate/ArcanistInstallCertificateWorkflow.php @@ -110,14 +110,8 @@ EOTEXT 'cert' => $info['certificate'], ); - $json_encoder = new PhutilJSON(); - $json = $json_encoder->encodeFormatted($config); - echo "Writing ~/.arcrc...\n"; - - $path = self::getUserConfigurationFileLocation(); - Filesystem::writeFile($path, $json); - execx('chmod 600 %s', $path); + self::writeUserConfigurationFile($config); echo phutil_console_format( "** SUCCESS! ** Certificate installed.\n"); diff --git a/src/workflow/install-certificate/__init__.php b/src/workflow/install-certificate/__init__.php index ad6ca17e..d5f910fe 100644 --- a/src/workflow/install-certificate/__init__.php +++ b/src/workflow/install-certificate/__init__.php @@ -11,9 +11,6 @@ phutil_require_module('arcanist', 'workflow/base'); phutil_require_module('phutil', 'conduit/client'); phutil_require_module('phutil', 'console'); -phutil_require_module('phutil', 'filesystem'); -phutil_require_module('phutil', 'future/exec'); -phutil_require_module('phutil', 'parser/json'); phutil_require_module('phutil', 'parser/uri'); diff --git a/src/workflow/shell-complete/ArcanistShellCompleteWorkflow.php b/src/workflow/shell-complete/ArcanistShellCompleteWorkflow.php index c297ac0b..17358089 100644 --- a/src/workflow/shell-complete/ArcanistShellCompleteWorkflow.php +++ b/src/workflow/shell-complete/ArcanistShellCompleteWorkflow.php @@ -93,13 +93,33 @@ EOTEXT $complete[] = $name; } + // Also permit autocompletion of "arc alias" commands. + foreach (ArcanistAliasWorkflow::getAliases() as $key => $value) { + $complete[] = $key; + } + echo implode(' ', $complete)."\n"; return 0; } else { $workflow = $arc_config->buildWorkflow($argv[1]); if (!$workflow) { - return 1; + list($new_command, $new_args) = ArcanistAliasWorkflow::resolveAliases( + $argv[1], + $arc_config, + array_slice($argv, 2)); + if ($new_command) { + $workflow = $arc_config->buildWorkflow($new_command); + } + if (!$workflow) { + return 1; + } else { + $argv = array_merge( + array($argv[0]), + array($new_command), + $new_args); + } } + $arguments = $workflow->getArguments(); $prev = idx($argv, $pos - 1, null); diff --git a/src/workflow/shell-complete/__init__.php b/src/workflow/shell-complete/__init__.php index dbb4ac02..405ce60e 100644 --- a/src/workflow/shell-complete/__init__.php +++ b/src/workflow/shell-complete/__init__.php @@ -7,6 +7,7 @@ phutil_require_module('arcanist', 'repository/api/base'); +phutil_require_module('arcanist', 'workflow/alias'); phutil_require_module('arcanist', 'workflow/base'); phutil_require_module('arcanist', 'workingcopyidentity');