mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-11-25 16:22:42 +01:00
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
This commit is contained in:
parent
964bdb63ba
commit
b058efbb51
11 changed files with 239 additions and 14 deletions
|
@ -164,15 +164,40 @@ try {
|
||||||
}
|
}
|
||||||
|
|
||||||
$command = strtolower($args[0]);
|
$command = strtolower($args[0]);
|
||||||
|
$args = array_slice($args, 1);
|
||||||
$workflow = $config->buildWorkflow($command);
|
$workflow = $config->buildWorkflow($command);
|
||||||
if (!$workflow) {
|
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->setArcanistConfiguration($config);
|
||||||
$workflow->setCommand($command);
|
$workflow->setCommand($command);
|
||||||
$workflow->setWorkingDirectory($working_directory);
|
$workflow->setWorkingDirectory($working_directory);
|
||||||
$workflow->parseArguments(array_slice($args, 1));
|
$workflow->parseArguments($args);
|
||||||
|
|
||||||
$need_working_copy = $workflow->requiresWorkingCopy();
|
$need_working_copy = $workflow->requiresWorkingCopy();
|
||||||
$need_conduit = $workflow->requiresConduit();
|
$need_conduit = $workflow->requiresConduit();
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
phutil_register_library_map(array(
|
phutil_register_library_map(array(
|
||||||
'class' =>
|
'class' =>
|
||||||
array(
|
array(
|
||||||
|
'ArcanistAliasWorkflow' => 'workflow/alias',
|
||||||
'ArcanistAmendWorkflow' => 'workflow/amend',
|
'ArcanistAmendWorkflow' => 'workflow/amend',
|
||||||
'ArcanistApacheLicenseLinter' => 'lint/linter/apachelicense',
|
'ArcanistApacheLicenseLinter' => 'lint/linter/apachelicense',
|
||||||
'ArcanistApacheLicenseLinterTestCase' => 'lint/linter/apachelicense/__tests__',
|
'ArcanistApacheLicenseLinterTestCase' => 'lint/linter/apachelicense/__tests__',
|
||||||
|
@ -116,6 +117,7 @@ phutil_register_library_map(array(
|
||||||
),
|
),
|
||||||
'requires_class' =>
|
'requires_class' =>
|
||||||
array(
|
array(
|
||||||
|
'ArcanistAliasWorkflow' => 'ArcanistBaseWorkflow',
|
||||||
'ArcanistAmendWorkflow' => 'ArcanistBaseWorkflow',
|
'ArcanistAmendWorkflow' => 'ArcanistBaseWorkflow',
|
||||||
'ArcanistApacheLicenseLinter' => 'ArcanistLicenseLinter',
|
'ArcanistApacheLicenseLinter' => 'ArcanistLicenseLinter',
|
||||||
'ArcanistApacheLicenseLinterTestCase' => 'ArcanistLinterTestCase',
|
'ArcanistApacheLicenseLinterTestCase' => 'ArcanistLinterTestCase',
|
||||||
|
|
|
@ -100,6 +100,10 @@ class ArcanistConfiguration {
|
||||||
return $workflows;
|
return $workflows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final public function isValidWorkflow($workflow) {
|
||||||
|
return (bool)$this->buildWorkflow($workflow);
|
||||||
|
}
|
||||||
|
|
||||||
public function willRunWorkflow($command, ArcanistBaseWorkflow $workflow) {
|
public function willRunWorkflow($command, ArcanistBaseWorkflow $workflow) {
|
||||||
// This is a hook.
|
// This is a hook.
|
||||||
}
|
}
|
||||||
|
|
153
src/workflow/alias/ArcanistAliasWorkflow.php
Normal file
153
src/workflow/alias/ArcanistAliasWorkflow.php
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show which revision or revisions are in the working copy.
|
||||||
|
*
|
||||||
|
* @group workflow
|
||||||
|
*/
|
||||||
|
final class ArcanistAliasWorkflow extends ArcanistBaseWorkflow {
|
||||||
|
|
||||||
|
public function getCommandHelp() {
|
||||||
|
return phutil_console_format(<<<EOTEXT
|
||||||
|
**alias**
|
||||||
|
**alias** __command__
|
||||||
|
**alias** __command__ __target__ -- [__options__]
|
||||||
|
Supports: cli
|
||||||
|
Create an alias from __command__ to __target__ (optionally, with
|
||||||
|
__options__). For example:
|
||||||
|
|
||||||
|
arc alias fpatch patch -- --force
|
||||||
|
|
||||||
|
...will create a new 'arc' command, 'arc fpatch', which invokes
|
||||||
|
'arc patch --force ...' when run. NOTE: use "--" before specifying
|
||||||
|
options!
|
||||||
|
|
||||||
|
You can not overwrite builtins, including 'alias' itself. The builtin
|
||||||
|
will always execute, even if it was added after your alias.
|
||||||
|
|
||||||
|
To remove an alias, run:
|
||||||
|
|
||||||
|
arc alias fpatch
|
||||||
|
|
||||||
|
Without any arguments, 'arc alias' will list aliases.
|
||||||
|
|
||||||
|
EOTEXT
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getArguments() {
|
||||||
|
return array(
|
||||||
|
'*' => '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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
17
src/workflow/alias/__init__.php
Normal file
17
src/workflow/alias/__init__.php
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('arcanist', 'exception/usage');
|
||||||
|
phutil_require_module('arcanist', 'exception/usage/userabort');
|
||||||
|
phutil_require_module('arcanist', 'workflow/base');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'console');
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('ArcanistAliasWorkflow.php');
|
|
@ -952,6 +952,17 @@ abstract class ArcanistBaseWorkflow {
|
||||||
return $user_config;
|
return $user_config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static function writeUserConfigurationFile($config) {
|
||||||
|
$json_encoder = new PhutilJSON();
|
||||||
|
$json = $json_encoder->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
|
* Write a message to stderr so that '--json' flags or stdout which is meant
|
||||||
* to be piped somewhere aren't disrupted.
|
* to be piped somewhere aren't disrupted.
|
||||||
|
|
|
@ -21,6 +21,7 @@ phutil_require_module('phutil', 'console');
|
||||||
phutil_require_module('phutil', 'filesystem');
|
phutil_require_module('phutil', 'filesystem');
|
||||||
phutil_require_module('phutil', 'future/exec');
|
phutil_require_module('phutil', 'future/exec');
|
||||||
phutil_require_module('phutil', 'moduleutils');
|
phutil_require_module('phutil', 'moduleutils');
|
||||||
|
phutil_require_module('phutil', 'parser/json');
|
||||||
phutil_require_module('phutil', 'utils');
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -110,14 +110,8 @@ EOTEXT
|
||||||
'cert' => $info['certificate'],
|
'cert' => $info['certificate'],
|
||||||
);
|
);
|
||||||
|
|
||||||
$json_encoder = new PhutilJSON();
|
|
||||||
$json = $json_encoder->encodeFormatted($config);
|
|
||||||
|
|
||||||
echo "Writing ~/.arcrc...\n";
|
echo "Writing ~/.arcrc...\n";
|
||||||
|
self::writeUserConfigurationFile($config);
|
||||||
$path = self::getUserConfigurationFileLocation();
|
|
||||||
Filesystem::writeFile($path, $json);
|
|
||||||
execx('chmod 600 %s', $path);
|
|
||||||
|
|
||||||
echo phutil_console_format(
|
echo phutil_console_format(
|
||||||
"<bg:green>** SUCCESS! **</bg> Certificate installed.\n");
|
"<bg:green>** SUCCESS! **</bg> Certificate installed.\n");
|
||||||
|
|
|
@ -11,9 +11,6 @@ phutil_require_module('arcanist', 'workflow/base');
|
||||||
|
|
||||||
phutil_require_module('phutil', 'conduit/client');
|
phutil_require_module('phutil', 'conduit/client');
|
||||||
phutil_require_module('phutil', 'console');
|
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');
|
phutil_require_module('phutil', 'parser/uri');
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -93,13 +93,33 @@ EOTEXT
|
||||||
$complete[] = $name;
|
$complete[] = $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also permit autocompletion of "arc alias" commands.
|
||||||
|
foreach (ArcanistAliasWorkflow::getAliases() as $key => $value) {
|
||||||
|
$complete[] = $key;
|
||||||
|
}
|
||||||
|
|
||||||
echo implode(' ', $complete)."\n";
|
echo implode(' ', $complete)."\n";
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
$workflow = $arc_config->buildWorkflow($argv[1]);
|
$workflow = $arc_config->buildWorkflow($argv[1]);
|
||||||
if (!$workflow) {
|
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();
|
$arguments = $workflow->getArguments();
|
||||||
|
|
||||||
$prev = idx($argv, $pos - 1, null);
|
$prev = idx($argv, $pos - 1, null);
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
|
|
||||||
phutil_require_module('arcanist', 'repository/api/base');
|
phutil_require_module('arcanist', 'repository/api/base');
|
||||||
|
phutil_require_module('arcanist', 'workflow/alias');
|
||||||
phutil_require_module('arcanist', 'workflow/base');
|
phutil_require_module('arcanist', 'workflow/base');
|
||||||
phutil_require_module('arcanist', 'workingcopyidentity');
|
phutil_require_module('arcanist', 'workingcopyidentity');
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue