1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-10 00:42:40 +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:
epriestley 2012-02-21 13:16:52 -08:00
parent 964bdb63ba
commit b058efbb51
11 changed files with 239 additions and 14 deletions

View file

@ -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 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) { if (!$workflow) {
throw new ArcanistUsageException( throw new ArcanistUsageException(
"Unknown command '{$command}'. Try 'arc help'."); "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();

View file

@ -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',

View file

@ -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.
} }

View 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);
}
}

View 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');

View file

@ -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.

View file

@ -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');

View file

@ -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");

View file

@ -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');

View file

@ -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);

View file

@ -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');