mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-11-25 16:22:42 +01:00
[Wilds] Rewrite "arc" entrypoints for toolsets
Summary: Ref T13098. Depends on D19675. This change is headed to the `untamed-wilds` branch. This change prevents `arc` from running `arc diff`, so I'm copy/pasting it. This change also completely breaks `arc`, but I'm just generally trying to do this rewrite step-by-step so we have at least a bit of context to refer to in the future. The major change here is to turn both `arc` and `phage` into scripts which start an `ArcanistRuntime`. This runtime then decides which workflows (like "diff", "patch", or "remote") are available based on `$argv[0]`. This turns `arc` into more of a CLI tools platform: we can build `phage` on it, third parties can build `my-companion-tool-thing`, etc. But all the different entry points can share a lot of infrastructure like: `help`, `alias`, shell-complete, configuration, Conduit, prompting, `--trace`, extension infrastructure, and so on. `ArcanistRuntime` is roughly a slightly more modern version of `scripts/arcanist.php`. That will be removed eventually, but not everything has ported yet. Test Plan: This code basically doesn't run yet, although the next patch runs at least a little bit. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13098 Differential Revision: https://secure.phabricator.com/D19677
This commit is contained in:
parent
9d59e9e590
commit
7d05dbec15
6 changed files with 612 additions and 457 deletions
29
bin/arc
29
bin/arc
|
@ -1,21 +1,14 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
# NOTE: This file is a wrapper script instead of a symlink so it will work in
|
if (function_exists('pcntl_async_signals')) {
|
||||||
# the Git Bash environment in Windows.
|
pcntl_async_signals(true);
|
||||||
|
} else {
|
||||||
|
declare(ticks = 1);
|
||||||
|
}
|
||||||
|
|
||||||
# Do bash magic to resolve the real location of this script through aliases,
|
require_once dirname(dirname(__FILE__)).'/scripts/init/init-arcanist.php';
|
||||||
# symlinks, etc.
|
|
||||||
SOURCE="${BASH_SOURCE[0]}";
|
$runtime = new ArcanistRuntime();
|
||||||
while [ -h "$SOURCE" ]; do
|
return $runtime->execute($argv);
|
||||||
LINK="$(readlink "$SOURCE")";
|
|
||||||
if [ "${LINK:0:1}" == "/" ]; then
|
|
||||||
# absolute symlink
|
|
||||||
SOURCE="$LINK"
|
|
||||||
else
|
|
||||||
# relative symlink
|
|
||||||
SOURCE="$(cd -P "$(dirname "$SOURCE")" && pwd)/$LINK"
|
|
||||||
fi
|
|
||||||
done;
|
|
||||||
DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
|
|
||||||
|
|
||||||
exec "$DIR/../scripts/arcanist.php" "$@"
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
@echo off
|
@echo off
|
||||||
php -f "%~dp0..\scripts\arcanist.php" -- %*
|
php -f "%~dp0..\bin\arc" -- %*
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
../scripts/phage.php
|
|
14
bin/phage
Executable file
14
bin/phage
Executable file
|
@ -0,0 +1,14 @@
|
||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
if (function_exists('pcntl_async_signals')) {
|
||||||
|
pcntl_async_signals(true);
|
||||||
|
} else {
|
||||||
|
declare(ticks = 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once dirname(dirname(__FILE__)).'/scripts/init/init-arcanist.php';
|
||||||
|
|
||||||
|
$runtime = new ArcanistRuntime();
|
||||||
|
return $runtime->execute($argv);
|
||||||
|
|
|
@ -1,197 +1,32 @@
|
||||||
#!/usr/bin/env php
|
#!/usr/bin/env php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
sanity_check_environment();
|
if (function_exists('pcntl_async_signals')) {
|
||||||
|
pcntl_async_signals(true);
|
||||||
|
} else {
|
||||||
|
declare(ticks = 1);
|
||||||
|
}
|
||||||
|
|
||||||
require_once dirname(__FILE__).'/__init_script__.php';
|
require_once dirname(dirname(__FILE__)).'/scripts/init/init-arcanist.php';
|
||||||
|
|
||||||
ini_set('memory_limit', -1);
|
$runtime = new ArcanistRuntime();
|
||||||
|
return $runtime->execute($argv);
|
||||||
|
|
||||||
$original_argv = $argv;
|
|
||||||
$base_args = new PhutilArgumentParser($argv);
|
|
||||||
$base_args->parseStandardArguments();
|
|
||||||
$base_args->parsePartial(
|
|
||||||
array(
|
|
||||||
array(
|
|
||||||
'name' => 'load-phutil-library',
|
|
||||||
'param' => 'path',
|
|
||||||
'help' => pht('Load a libphutil library.'),
|
|
||||||
'repeat' => true,
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'name' => 'skip-arcconfig',
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'name' => 'arcrc-file',
|
|
||||||
'param' => 'filename',
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'name' => 'conduit-uri',
|
|
||||||
'param' => 'uri',
|
|
||||||
'help' => pht('Connect to Phabricator install specified by __uri__.'),
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'name' => 'conduit-token',
|
|
||||||
'param' => 'token',
|
|
||||||
'help' => pht('Use a specific authentication token.'),
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'name' => 'anonymous',
|
|
||||||
'help' => pht('Run workflow as a public user, without authenticating.'),
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'name' => 'conduit-version',
|
|
||||||
'param' => 'version',
|
|
||||||
'help' => pht(
|
|
||||||
'(Developers) Mock client version in protocol handshake.'),
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'name' => 'conduit-timeout',
|
|
||||||
'param' => 'timeout',
|
|
||||||
'help' => pht('Set Conduit timeout (in seconds).'),
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'name' => 'config',
|
|
||||||
'param' => 'key=value',
|
|
||||||
'repeat' => true,
|
|
||||||
'help' => pht(
|
|
||||||
'Specify a runtime configuration value. This will take precedence '.
|
|
||||||
'over static values, and only affect the current arcanist invocation.'),
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
$config_trace_mode = $base_args->getArg('trace');
|
$config_trace_mode = $base_args->getArg('trace');
|
||||||
|
|
||||||
$force_conduit = $base_args->getArg('conduit-uri');
|
$force_conduit = $base_args->getArg('conduit-uri');
|
||||||
$force_token = $base_args->getArg('conduit-token');
|
$force_token = $base_args->getArg('conduit-token');
|
||||||
$force_conduit_version = $base_args->getArg('conduit-version');
|
|
||||||
$conduit_timeout = $base_args->getArg('conduit-timeout');
|
|
||||||
$skip_arcconfig = $base_args->getArg('skip-arcconfig');
|
|
||||||
$custom_arcrc = $base_args->getArg('arcrc-file');
|
|
||||||
$is_anonymous = $base_args->getArg('anonymous');
|
$is_anonymous = $base_args->getArg('anonymous');
|
||||||
$load = $base_args->getArg('load-phutil-library');
|
$load = $base_args->getArg('load-phutil-library');
|
||||||
$help = $base_args->getArg('help');
|
$help = $base_args->getArg('help');
|
||||||
$args = array_values($base_args->getUnconsumedArgumentVector());
|
$args = array_values($base_args->getUnconsumedArgumentVector());
|
||||||
|
|
||||||
$working_directory = getcwd();
|
|
||||||
$console = PhutilConsole::getConsole();
|
$console = PhutilConsole::getConsole();
|
||||||
$config = null;
|
$config = null;
|
||||||
$workflow = null;
|
$workflow = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if ($config_trace_mode) {
|
|
||||||
echo tsprintf(
|
|
||||||
"**<bg:magenta> %s </bg>** %s\n",
|
|
||||||
pht('ARGV'),
|
|
||||||
csprintf('%Ls', $original_argv));
|
|
||||||
|
|
||||||
$libraries = array(
|
|
||||||
'phutil',
|
|
||||||
'arcanist',
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($libraries as $library_name) {
|
|
||||||
echo tsprintf(
|
|
||||||
"**<bg:magenta> %s </bg>** %s\n",
|
|
||||||
pht('LOAD'),
|
|
||||||
pht(
|
|
||||||
'Loaded "%s" from "%s".',
|
|
||||||
$library_name,
|
|
||||||
phutil_get_library_root($library_name)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$args) {
|
|
||||||
if ($help) {
|
|
||||||
$args = array('help');
|
|
||||||
} else {
|
|
||||||
throw new ArcanistUsageException(
|
|
||||||
pht('No command provided. Try `%s`.', 'arc help'));
|
|
||||||
}
|
|
||||||
} else if ($help) {
|
|
||||||
array_unshift($args, 'help');
|
|
||||||
}
|
|
||||||
|
|
||||||
$configuration_manager = new ArcanistConfigurationManager();
|
|
||||||
if ($custom_arcrc) {
|
|
||||||
$configuration_manager->setUserConfigurationFileLocation($custom_arcrc);
|
|
||||||
}
|
|
||||||
|
|
||||||
$global_config = $configuration_manager->readUserArcConfig();
|
|
||||||
$system_config = $configuration_manager->readSystemArcConfig();
|
|
||||||
$runtime_config = $configuration_manager->applyRuntimeArcConfig($base_args);
|
|
||||||
|
|
||||||
if ($skip_arcconfig) {
|
|
||||||
$working_copy = ArcanistWorkingCopyIdentity::newDummyWorkingCopy();
|
|
||||||
} else {
|
|
||||||
$working_copy =
|
|
||||||
ArcanistWorkingCopyIdentity::newFromPath($working_directory);
|
|
||||||
}
|
|
||||||
$configuration_manager->setWorkingCopyIdentity($working_copy);
|
|
||||||
|
|
||||||
// Load additional libraries, which can provide new classes like configuration
|
|
||||||
// overrides, linters and lint engines, unit test engines, etc.
|
|
||||||
|
|
||||||
// If the user specified "--load-phutil-library" one or more times from
|
|
||||||
// the command line, we load those libraries **instead** of whatever else
|
|
||||||
// is configured. This is basically a debugging feature to let you force
|
|
||||||
// specific libraries to load regardless of the state of the world.
|
|
||||||
if ($load) {
|
|
||||||
$console->writeLog(
|
|
||||||
"%s\n",
|
|
||||||
pht(
|
|
||||||
'Using `%s` flag, configuration will be ignored and configured '.
|
|
||||||
'libraries will not be loaded.',
|
|
||||||
'--load-phutil-library'));
|
|
||||||
// Load the flag libraries. These must load, since the user specified them
|
|
||||||
// explicitly.
|
|
||||||
arcanist_load_libraries(
|
|
||||||
$load,
|
|
||||||
$must_load = true,
|
|
||||||
$lib_source = pht('a "%s" flag', '--load-phutil-library'),
|
|
||||||
$working_copy);
|
|
||||||
} else {
|
|
||||||
// Load libraries in system 'load' config. In contrast to global config, we
|
|
||||||
// fail hard here because this file is edited manually, so if 'arc' breaks
|
|
||||||
// that doesn't make it any more difficult to correct.
|
|
||||||
arcanist_load_libraries(
|
|
||||||
idx($system_config, 'load', array()),
|
|
||||||
$must_load = true,
|
|
||||||
$lib_source = pht('the "%s" setting in system config', 'load'),
|
|
||||||
$working_copy);
|
|
||||||
|
|
||||||
// Load libraries in global 'load' config, as per "arc set-config load". We
|
|
||||||
// need to fail softly if these break because errors would prevent the user
|
|
||||||
// from running "arc set-config" to correct them.
|
|
||||||
arcanist_load_libraries(
|
|
||||||
idx($global_config, 'load', array()),
|
|
||||||
$must_load = false,
|
|
||||||
$lib_source = pht('the "%s" setting in global config', 'load'),
|
|
||||||
$working_copy);
|
|
||||||
|
|
||||||
// Load libraries in ".arcconfig". Libraries here must load.
|
|
||||||
arcanist_load_libraries(
|
|
||||||
$working_copy->getProjectConfig('load'),
|
|
||||||
$must_load = true,
|
|
||||||
$lib_source = pht('the "%s" setting in "%s"', 'load', '.arcconfig'),
|
|
||||||
$working_copy);
|
|
||||||
|
|
||||||
// Load libraries in ".arcconfig". Libraries here must load.
|
|
||||||
arcanist_load_libraries(
|
|
||||||
idx($runtime_config, 'load', array()),
|
|
||||||
$must_load = true,
|
|
||||||
$lib_source = pht('the %s argument', '--config "load=[...]"'),
|
|
||||||
$working_copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
$user_config = $configuration_manager->readUserConfigurationFile();
|
|
||||||
|
|
||||||
$config_class = $working_copy->getProjectConfig('arcanist_configuration');
|
|
||||||
if ($config_class) {
|
|
||||||
$config = new $config_class();
|
|
||||||
} else {
|
|
||||||
$config = new ArcanistConfiguration();
|
|
||||||
}
|
|
||||||
|
|
||||||
$command = strtolower($args[0]);
|
$command = strtolower($args[0]);
|
||||||
$args = array_slice($args, 1);
|
$args = array_slice($args, 1);
|
||||||
|
@ -211,13 +46,6 @@ try {
|
||||||
// their behaviors.
|
// their behaviors.
|
||||||
putenv('ARCANIST='.$command);
|
putenv('ARCANIST='.$command);
|
||||||
|
|
||||||
if ($force_conduit_version) {
|
|
||||||
$workflow->forceConduitVersion($force_conduit_version);
|
|
||||||
}
|
|
||||||
if ($conduit_timeout) {
|
|
||||||
$workflow->setConduitTimeout($conduit_timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
$need_working_copy = $workflow->requiresWorkingCopy();
|
$need_working_copy = $workflow->requiresWorkingCopy();
|
||||||
|
|
||||||
$supported_vcs_types = $workflow->getSupportedRevisionControlSystems();
|
$supported_vcs_types = $workflow->getSupportedRevisionControlSystems();
|
||||||
|
@ -353,10 +181,6 @@ try {
|
||||||
->setBasicAuthUser($basic_user)
|
->setBasicAuthUser($basic_user)
|
||||||
->setBasicAuthPass($basic_pass);
|
->setBasicAuthPass($basic_pass);
|
||||||
|
|
||||||
if ($conduit_timeout) {
|
|
||||||
$engine->setConduitTimeout($conduit_timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
$workflow->setConduitEngine($engine);
|
$workflow->setConduitEngine($engine);
|
||||||
|
|
||||||
if ($need_auth) {
|
if ($need_auth) {
|
||||||
|
@ -470,230 +294,6 @@ try {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform some sanity checks against the possible diversity of PHP builds in
|
|
||||||
* the wild, like very old versions and builds that were compiled with flags
|
|
||||||
* that exclude core functionality.
|
|
||||||
*/
|
|
||||||
function sanity_check_environment() {
|
|
||||||
// NOTE: We don't have phutil_is_windows() yet here.
|
|
||||||
$is_windows = (DIRECTORY_SEPARATOR != '/');
|
|
||||||
|
|
||||||
// We use stream_socket_pair() which is not available on Windows earlier.
|
|
||||||
$min_version = ($is_windows ? '5.3.0' : '5.2.3');
|
|
||||||
$cur_version = phpversion();
|
|
||||||
if (version_compare($cur_version, $min_version, '<')) {
|
|
||||||
die_with_bad_php(
|
|
||||||
"You are running PHP version '{$cur_version}', which is older than ".
|
|
||||||
"the minimum version, '{$min_version}'. Update to at least ".
|
|
||||||
"'{$min_version}'.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($is_windows) {
|
|
||||||
$need_functions = array(
|
|
||||||
'curl_init' => array('builtin-dll', 'php_curl.dll'),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$need_functions = array(
|
|
||||||
'curl_init' => array(
|
|
||||||
'text',
|
|
||||||
"You need to install the cURL PHP extension, maybe with ".
|
|
||||||
"'apt-get install php5-curl' or 'yum install php53-curl' or ".
|
|
||||||
"something similar.",
|
|
||||||
),
|
|
||||||
'json_decode' => array('flag', '--without-json'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$problems = array();
|
|
||||||
|
|
||||||
$config = null;
|
|
||||||
$show_config = false;
|
|
||||||
foreach ($need_functions as $fname => $resolution) {
|
|
||||||
if (function_exists($fname)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
static $info;
|
|
||||||
if ($info === null) {
|
|
||||||
ob_start();
|
|
||||||
phpinfo(INFO_GENERAL);
|
|
||||||
$info = ob_get_clean();
|
|
||||||
$matches = null;
|
|
||||||
if (preg_match('/^Configure Command =>\s*(.*?)$/m', $info, $matches)) {
|
|
||||||
$config = $matches[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$generic = true;
|
|
||||||
list($what, $which) = $resolution;
|
|
||||||
|
|
||||||
if ($what == 'flag' && strpos($config, $which) !== false) {
|
|
||||||
$show_config = true;
|
|
||||||
$generic = false;
|
|
||||||
$problems[] =
|
|
||||||
"This build of PHP was compiled with the configure flag '{$which}', ".
|
|
||||||
"which means it does not have the function '{$fname}()'. This ".
|
|
||||||
"function is required for arc to run. Rebuild PHP without this flag. ".
|
|
||||||
"You may also be able to build or install the relevant extension ".
|
|
||||||
"separately.";
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($what == 'builtin-dll') {
|
|
||||||
$generic = false;
|
|
||||||
$problems[] =
|
|
||||||
"Your install of PHP does not have the '{$which}' extension enabled. ".
|
|
||||||
"Edit your php.ini file and uncomment the line which reads ".
|
|
||||||
"'extension={$which}'.";
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($what == 'text') {
|
|
||||||
$generic = false;
|
|
||||||
$problems[] = $which;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($generic) {
|
|
||||||
$problems[] =
|
|
||||||
"This build of PHP is missing the required function '{$fname}()'. ".
|
|
||||||
"Rebuild PHP or install the extension which provides '{$fname}()'.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($problems) {
|
|
||||||
if ($show_config) {
|
|
||||||
$problems[] = "PHP was built with this configure command:\n\n{$config}";
|
|
||||||
}
|
|
||||||
die_with_bad_php(implode("\n\n", $problems));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function die_with_bad_php($message) {
|
|
||||||
// NOTE: We're bailing because PHP is broken. We can't call any library
|
|
||||||
// functions because they won't be loaded yet.
|
|
||||||
|
|
||||||
echo "\n";
|
|
||||||
echo 'PHP CONFIGURATION ERRORS';
|
|
||||||
echo "\n\n";
|
|
||||||
echo $message;
|
|
||||||
echo "\n\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function arcanist_load_libraries(
|
|
||||||
$load,
|
|
||||||
$must_load,
|
|
||||||
$lib_source,
|
|
||||||
ArcanistWorkingCopyIdentity $working_copy) {
|
|
||||||
|
|
||||||
if (!$load) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_array($load)) {
|
|
||||||
$error = pht(
|
|
||||||
'Libraries specified by %s are invalid; expected a list. '.
|
|
||||||
'Check your configuration.',
|
|
||||||
$lib_source);
|
|
||||||
$console = PhutilConsole::getConsole();
|
|
||||||
$console->writeErr("%s: %s\n", pht('WARNING'), $error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($load as $location) {
|
|
||||||
|
|
||||||
// Try to resolve the library location. We look in several places, in
|
|
||||||
// order:
|
|
||||||
//
|
|
||||||
// 1. Inside the working copy. This is for phutil libraries within the
|
|
||||||
// project. For instance "library/src" will resolve to
|
|
||||||
// "./library/src" if it exists.
|
|
||||||
// 2. In the same directory as the working copy. This allows you to
|
|
||||||
// check out a library alongside a working copy and reference it.
|
|
||||||
// If we haven't resolved yet, "library/src" will try to resolve to
|
|
||||||
// "../library/src" if it exists.
|
|
||||||
// 3. Using normal libphutil resolution rules. Generally, this means
|
|
||||||
// that it checks for libraries next to libphutil, then libraries
|
|
||||||
// in the PHP include_path.
|
|
||||||
//
|
|
||||||
// Note that absolute paths will just resolve absolutely through rule (1).
|
|
||||||
|
|
||||||
$resolved = false;
|
|
||||||
|
|
||||||
// Check inside the working copy. This also checks absolute paths, since
|
|
||||||
// they'll resolve absolute and just ignore the project root.
|
|
||||||
$resolved_location = Filesystem::resolvePath(
|
|
||||||
$location,
|
|
||||||
$working_copy->getProjectRoot());
|
|
||||||
if (Filesystem::pathExists($resolved_location)) {
|
|
||||||
$location = $resolved_location;
|
|
||||||
$resolved = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we didn't find anything, check alongside the working copy.
|
|
||||||
if (!$resolved) {
|
|
||||||
$resolved_location = Filesystem::resolvePath(
|
|
||||||
$location,
|
|
||||||
dirname($working_copy->getProjectRoot()));
|
|
||||||
if (Filesystem::pathExists($resolved_location)) {
|
|
||||||
$location = $resolved_location;
|
|
||||||
$resolved = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$console = PhutilConsole::getConsole();
|
|
||||||
$console->writeLog(
|
|
||||||
"%s\n",
|
|
||||||
pht("Loading phutil library from '%s'...", $location));
|
|
||||||
|
|
||||||
$error = null;
|
|
||||||
try {
|
|
||||||
phutil_load_library($location);
|
|
||||||
} catch (PhutilBootloaderException $ex) {
|
|
||||||
$error = pht(
|
|
||||||
"Failed to load phutil library at location '%s'. This library ".
|
|
||||||
"is specified by %s. Check that the setting is correct and the ".
|
|
||||||
"library is located in the right place.",
|
|
||||||
$location,
|
|
||||||
$lib_source);
|
|
||||||
if ($must_load) {
|
|
||||||
throw new ArcanistUsageException($error);
|
|
||||||
} else {
|
|
||||||
fwrite(STDERR, phutil_console_wrap(
|
|
||||||
phutil_console_format("%s: %s\n",
|
|
||||||
pht('WARNING'),
|
|
||||||
$error)));
|
|
||||||
}
|
|
||||||
} catch (PhutilLibraryConflictException $ex) {
|
|
||||||
if ($ex->getLibrary() != 'arcanist') {
|
|
||||||
throw $ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: If you are running `arc` against itself, we ignore the library
|
|
||||||
// conflict created by loading the local `arc` library (in the current
|
|
||||||
// working directory) and continue without loading it.
|
|
||||||
|
|
||||||
// This means we only execute code in the `arcanist/` directory which is
|
|
||||||
// associated with the binary you are running, whereas we would normally
|
|
||||||
// execute local code.
|
|
||||||
|
|
||||||
// This can make `arc` development slightly confusing if your setup is
|
|
||||||
// especially bizarre, but it allows `arc` to be used in automation
|
|
||||||
// workflows more easily. For some context, see PHI13.
|
|
||||||
|
|
||||||
$executing_directory = dirname(dirname(__FILE__));
|
|
||||||
$working_directory = dirname($location);
|
|
||||||
|
|
||||||
fwrite(
|
|
||||||
STDERR,
|
|
||||||
tsprintf(
|
|
||||||
"**<bg:yellow> %s </bg>** %s\n",
|
|
||||||
pht('VERY META'),
|
|
||||||
pht(
|
|
||||||
'You are running one copy of Arcanist (at path "%s") against '.
|
|
||||||
'another copy of Arcanist (at path "%s"). Code in the current '.
|
|
||||||
'working directory will not be loaded or executed.',
|
|
||||||
$executing_directory,
|
|
||||||
$working_directory)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
578
scripts/init/init-arcanist.php
Normal file
578
scripts/init/init-arcanist.php
Normal file
|
@ -0,0 +1,578 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ArcanistRuntime {
|
||||||
|
|
||||||
|
public function execute(array $argv) {
|
||||||
|
$err = $this->checkEnvironment();
|
||||||
|
if ($err) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
$err = $this->includeCoreLibraries();
|
||||||
|
if ($err) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
PhutilTranslator::getInstance()
|
||||||
|
->setLocale(PhutilLocale::loadLocale('en_US'))
|
||||||
|
->setTranslations(PhutilTranslation::getTranslationMapForLocale('en_US'));
|
||||||
|
|
||||||
|
ini_set('memory_limit', -1);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return $this->executeCore($argv);
|
||||||
|
} catch (PhutilArgumentUsageException $ex) {
|
||||||
|
fwrite(
|
||||||
|
STDERR,
|
||||||
|
tsprintf(
|
||||||
|
"**%s:** %s\n",
|
||||||
|
pht('Usage Exception'),
|
||||||
|
$ex->getMessage()));
|
||||||
|
return 77;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function executeCore(array $argv) {
|
||||||
|
$config_args = array(
|
||||||
|
array(
|
||||||
|
'name' => 'load-phutil-library',
|
||||||
|
'param' => 'path',
|
||||||
|
'help' => pht('Load a libphutil library.'),
|
||||||
|
'repeat' => true,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'config',
|
||||||
|
'param' => 'key=value',
|
||||||
|
'repeat' => true,
|
||||||
|
'help' => pht('Specify a runtime configuration value.'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$args = id(new PhutilArgumentParser($argv))
|
||||||
|
->parseStandardArguments();
|
||||||
|
|
||||||
|
$is_trace = $args->getArg('trace');
|
||||||
|
if ($is_trace) {
|
||||||
|
$this->logTrace(pht('ARGV'), csprintf('%Ls', $argv));
|
||||||
|
}
|
||||||
|
|
||||||
|
$args->parsePartial($config_args, true);
|
||||||
|
|
||||||
|
$config = $this->loadConfiguration($args);
|
||||||
|
|
||||||
|
$this->loadLibraries($args, $config);
|
||||||
|
|
||||||
|
$toolset = $this->newToolset($argv);
|
||||||
|
|
||||||
|
$args->parsePartial($toolset->getToolsetArguments());
|
||||||
|
|
||||||
|
$workflows = $this->newWorkflows($toolset);
|
||||||
|
|
||||||
|
$phutil_workflows = array();
|
||||||
|
foreach ($workflows as $key => $workflow) {
|
||||||
|
$phutil_workflows[$key] = $workflow->newPhutilWorkflow();
|
||||||
|
}
|
||||||
|
|
||||||
|
$unconsumed_argv = $args->getUnconsumedArgumentVector();
|
||||||
|
|
||||||
|
$result = $this->resolveAliases($workflows, $unconsumed_argv, $config);
|
||||||
|
if (is_int($result)) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$args->setUnconsumedArgumentVector($result);
|
||||||
|
|
||||||
|
return $args->parseWorkflows($phutil_workflows);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform some sanity checks against the possible diversity of PHP builds in
|
||||||
|
* the wild, like very old versions and builds that were compiled with flags
|
||||||
|
* that exclude core functionality.
|
||||||
|
*/
|
||||||
|
private function checkEnvironment() {
|
||||||
|
// NOTE: We don't have phutil_is_windows() yet here.
|
||||||
|
$is_windows = (DIRECTORY_SEPARATOR != '/');
|
||||||
|
|
||||||
|
// We use stream_socket_pair() which is not available on Windows earlier.
|
||||||
|
$min_version = ($is_windows ? '5.3.0' : '5.2.3');
|
||||||
|
$cur_version = phpversion();
|
||||||
|
if (version_compare($cur_version, $min_version, '<')) {
|
||||||
|
$message = sprintf(
|
||||||
|
'You are running a version of PHP ("%s"), which is older than the '.
|
||||||
|
'minimum supported version ("%s"). Update PHP to continue.',
|
||||||
|
$cur_version,
|
||||||
|
$min_version);
|
||||||
|
return $this->fatalError($message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($is_windows) {
|
||||||
|
$need_functions = array(
|
||||||
|
'curl_init' => array('builtin-dll', 'php_curl.dll'),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$need_functions = array(
|
||||||
|
'curl_init' => array(
|
||||||
|
'text',
|
||||||
|
"You need to install the cURL PHP extension, maybe with ".
|
||||||
|
"'apt-get install php5-curl' or 'yum install php53-curl' or ".
|
||||||
|
"something similar.",
|
||||||
|
),
|
||||||
|
'json_decode' => array('flag', '--without-json'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$problems = array();
|
||||||
|
|
||||||
|
$config = null;
|
||||||
|
$show_config = false;
|
||||||
|
foreach ($need_functions as $fname => $resolution) {
|
||||||
|
if (function_exists($fname)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
static $info;
|
||||||
|
if ($info === null) {
|
||||||
|
ob_start();
|
||||||
|
phpinfo(INFO_GENERAL);
|
||||||
|
$info = ob_get_clean();
|
||||||
|
$matches = null;
|
||||||
|
if (preg_match('/^Configure Command =>\s*(.*?)$/m', $info, $matches)) {
|
||||||
|
$config = $matches[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list($what, $which) = $resolution;
|
||||||
|
|
||||||
|
if ($what == 'flag' && strpos($config, $which) !== false) {
|
||||||
|
$show_config = true;
|
||||||
|
$problems[] = sprintf(
|
||||||
|
'The build of PHP you are running was compiled with the configure '.
|
||||||
|
'flag "%s", which means it does not support the function "%s()". '.
|
||||||
|
'This function is required for Arcanist to run. Install a standard '.
|
||||||
|
'build of PHP or rebuild it without this flag. You may also be '.
|
||||||
|
'able to build or install the relevant extension separately.',
|
||||||
|
$which,
|
||||||
|
$fname);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($what == 'builtin-dll') {
|
||||||
|
$problems[] = sprintf(
|
||||||
|
'The build of PHP you are running does not have the "%s" extension '.
|
||||||
|
'enabled. Edit your php.ini file and uncomment the line which '.
|
||||||
|
'reads "extension=%s".',
|
||||||
|
$which,
|
||||||
|
$which);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($what == 'text') {
|
||||||
|
$problems[] = $which;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$problems[] = sprintf(
|
||||||
|
'The build of PHP you are running is missing the required function '.
|
||||||
|
'"%s()". Rebuild PHP or install the extension which provides "%s()".',
|
||||||
|
$fname,
|
||||||
|
$fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($problems) {
|
||||||
|
if ($show_config) {
|
||||||
|
$problems[] = "PHP was built with this configure command:\n\n{$config}";
|
||||||
|
}
|
||||||
|
$problems = implode("\n\n", $problems);
|
||||||
|
return $this->fatalError($problems);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function fatalError($message) {
|
||||||
|
echo "CONFIGURATION ERROR\n\n";
|
||||||
|
echo $message;
|
||||||
|
echo "\n\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function includeCoreLibraries() {
|
||||||
|
// Adjust 'include_path' to add locations where we'll search for libphutil.
|
||||||
|
// We look in these places:
|
||||||
|
//
|
||||||
|
// - Next to 'arcanist/'.
|
||||||
|
// - Anywhere in the normal PHP 'include_path'.
|
||||||
|
// - Inside 'arcanist/externals/includes/'.
|
||||||
|
//
|
||||||
|
// When looking in these places, we expect to find a 'libphutil/' directory.
|
||||||
|
|
||||||
|
// The 'arcanist/' directory.
|
||||||
|
$arcanist_dir = dirname(dirname(dirname(__FILE__)));
|
||||||
|
|
||||||
|
// The parent directory of 'arcanist/'.
|
||||||
|
$parent_dir = dirname($arcanist_dir);
|
||||||
|
|
||||||
|
// The 'arcanist/externals/includes/' directory.
|
||||||
|
$include_dir = implode(
|
||||||
|
DIRECTORY_SEPARATOR,
|
||||||
|
array(
|
||||||
|
$arcanist_dir,
|
||||||
|
'externals',
|
||||||
|
'includes',
|
||||||
|
));
|
||||||
|
|
||||||
|
$php_include_path = ini_get('include_path');
|
||||||
|
$php_include_path = implode(
|
||||||
|
PATH_SEPARATOR,
|
||||||
|
array(
|
||||||
|
$parent_dir,
|
||||||
|
$php_include_path,
|
||||||
|
$include_dir,
|
||||||
|
));
|
||||||
|
|
||||||
|
ini_set('include_path', $php_include_path);
|
||||||
|
|
||||||
|
// Load libphutil.
|
||||||
|
@include_once 'libphutil/scripts/__init_script__.php';
|
||||||
|
|
||||||
|
if (!@constant('__LIBPHUTIL__')) {
|
||||||
|
return $this->fatalError(
|
||||||
|
'Unable to load libphutil. Put "libphutil/" next to "arcanist/"; '.
|
||||||
|
'or update your PHP "include_path" to include the parent directory '.
|
||||||
|
'of "libphutil/"; or symlink "libphutil" into '.
|
||||||
|
'"arcanist/externals/includes/".');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load Arcanist.
|
||||||
|
phutil_load_library($arcanist_dir.'/src/');
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadConfiguration(PhutilArgumentParser $args) {
|
||||||
|
$configuration_manager = new ArcanistConfigurationManager();
|
||||||
|
|
||||||
|
$cwd = getcwd();
|
||||||
|
$working_copy = ArcanistWorkingCopyIdentity::newFromPath($cwd);
|
||||||
|
$configuration_manager->setWorkingCopyIdentity($working_copy);
|
||||||
|
|
||||||
|
$configuration_manager->applyRuntimeArcConfig($args);
|
||||||
|
|
||||||
|
return $configuration_manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadLibraries(
|
||||||
|
PhutilArgumentParser $args,
|
||||||
|
ArcanistConfigurationManager $config) {
|
||||||
|
|
||||||
|
$is_trace = $args->getArg('trace');
|
||||||
|
|
||||||
|
if ($is_trace) {
|
||||||
|
$libraries = array(
|
||||||
|
'phutil',
|
||||||
|
'arcanist',
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($libraries as $library_name) {
|
||||||
|
$this->logTrace(
|
||||||
|
pht('LOAD'),
|
||||||
|
pht(
|
||||||
|
'Loaded "%s" from "%s".',
|
||||||
|
$library_name,
|
||||||
|
phutil_get_library_root($library_name)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$load = array();
|
||||||
|
$working_copy = $config->getWorkingCopyIdentity();
|
||||||
|
|
||||||
|
$cli_libraries = $args->getArg('load-phutil-library');
|
||||||
|
if ($cli_libraries) {
|
||||||
|
$load[] = array(
|
||||||
|
'--load-phutil-library',
|
||||||
|
$cli_libraries,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$system_config = $config->readSystemArcConfig();
|
||||||
|
$load[] = array(
|
||||||
|
$config->getSystemArcConfigLocation(),
|
||||||
|
idx($system_config, 'load', array()),
|
||||||
|
);
|
||||||
|
|
||||||
|
$global_config = $config->readUserArcConfig();
|
||||||
|
$load[] = array(
|
||||||
|
$config->getUserConfigurationFileLocation(),
|
||||||
|
idx($global_config, 'load', array()),
|
||||||
|
);
|
||||||
|
|
||||||
|
$load[] = array(
|
||||||
|
'.arcconfig',
|
||||||
|
$working_copy->getProjectConfig('load'),
|
||||||
|
);
|
||||||
|
|
||||||
|
$load[] = array(
|
||||||
|
// TODO: We could explain exactly where this is coming from more
|
||||||
|
// clearly.
|
||||||
|
'./.../arc/config',
|
||||||
|
$working_copy->getLocalConfig('load'),
|
||||||
|
);
|
||||||
|
|
||||||
|
$load[] = array(
|
||||||
|
'--config load=...',
|
||||||
|
$config->getRuntimeConfig('load', array()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($load as $spec) {
|
||||||
|
list($source, $libraries) = $spec;
|
||||||
|
if ($is_trace) {
|
||||||
|
$this->logTrace(
|
||||||
|
pht('LOAD'),
|
||||||
|
pht(
|
||||||
|
'Loading libraries from "%s"...',
|
||||||
|
$source));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$libraries) {
|
||||||
|
if ($is_trace) {
|
||||||
|
$this->logTrace(pht('NONE'), pht('Nothing to load.'));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($libraries)) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Libraries specified by "%s" are not formatted correctly. '.
|
||||||
|
'Expected a list of paths. Check your configuration.',
|
||||||
|
$source));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($libraries as $library) {
|
||||||
|
$this->loadLibrary($source, $library, $working_copy, $is_trace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadLibrary(
|
||||||
|
$source,
|
||||||
|
$location,
|
||||||
|
ArcanistWorkingCopyIdentity $working_copy,
|
||||||
|
$is_trace) {
|
||||||
|
|
||||||
|
// Try to resolve the library location. We look in several places, in
|
||||||
|
// order:
|
||||||
|
//
|
||||||
|
// 1. Inside the working copy. This is for phutil libraries within the
|
||||||
|
// project. For instance "library/src" will resolve to
|
||||||
|
// "./library/src" if it exists.
|
||||||
|
// 2. In the same directory as the working copy. This allows you to
|
||||||
|
// check out a library alongside a working copy and reference it.
|
||||||
|
// If we haven't resolved yet, "library/src" will try to resolve to
|
||||||
|
// "../library/src" if it exists.
|
||||||
|
// 3. Using normal libphutil resolution rules. Generally, this means
|
||||||
|
// that it checks for libraries next to libphutil, then libraries
|
||||||
|
// in the PHP include_path.
|
||||||
|
//
|
||||||
|
// Note that absolute paths will just resolve absolutely through rule (1).
|
||||||
|
|
||||||
|
$resolved = false;
|
||||||
|
|
||||||
|
// Check inside the working copy. This also checks absolute paths, since
|
||||||
|
// they'll resolve absolute and just ignore the project root.
|
||||||
|
$resolved_location = Filesystem::resolvePath(
|
||||||
|
$location,
|
||||||
|
$working_copy->getProjectRoot());
|
||||||
|
if (Filesystem::pathExists($resolved_location)) {
|
||||||
|
$location = $resolved_location;
|
||||||
|
$resolved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we didn't find anything, check alongside the working copy.
|
||||||
|
if (!$resolved) {
|
||||||
|
$resolved_location = Filesystem::resolvePath(
|
||||||
|
$location,
|
||||||
|
dirname($working_copy->getProjectRoot()));
|
||||||
|
if (Filesystem::pathExists($resolved_location)) {
|
||||||
|
$location = $resolved_location;
|
||||||
|
$resolved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($is_trace) {
|
||||||
|
$this->logTrace(
|
||||||
|
pht('LOAD'),
|
||||||
|
pht('Loading phutil library from "%s"...', $location));
|
||||||
|
}
|
||||||
|
|
||||||
|
$error = null;
|
||||||
|
try {
|
||||||
|
phutil_load_library($location);
|
||||||
|
} catch (PhutilBootloaderException $ex) {
|
||||||
|
fwrite(
|
||||||
|
STDERR,
|
||||||
|
"%s",
|
||||||
|
tsprintf(
|
||||||
|
"**<bg:red> %s </bg>** %s\n",
|
||||||
|
pht(
|
||||||
|
'Failed to load phutil library at location "%s". This library '.
|
||||||
|
'is specified by "%s". Check that the setting is correct and '.
|
||||||
|
'the library is located in the right place.',
|
||||||
|
$location,
|
||||||
|
$source)));
|
||||||
|
|
||||||
|
$prompt = pht('Continue without loading library?');
|
||||||
|
if (!phutil_console_confirm($prompt)) {
|
||||||
|
throw $ex;
|
||||||
|
}
|
||||||
|
} catch (PhutilLibraryConflictException $ex) {
|
||||||
|
if ($ex->getLibrary() != 'arcanist') {
|
||||||
|
throw $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: If you are running `arc` against itself, we ignore the library
|
||||||
|
// conflict created by loading the local `arc` library (in the current
|
||||||
|
// working directory) and continue without loading it.
|
||||||
|
|
||||||
|
// This means we only execute code in the `arcanist/` directory which is
|
||||||
|
// associated with the binary you are running, whereas we would normally
|
||||||
|
// execute local code.
|
||||||
|
|
||||||
|
// This can make `arc` development slightly confusing if your setup is
|
||||||
|
// especially bizarre, but it allows `arc` to be used in automation
|
||||||
|
// workflows more easily. For some context, see PHI13.
|
||||||
|
|
||||||
|
$executing_directory = dirname(dirname(__FILE__));
|
||||||
|
$working_directory = dirname($location);
|
||||||
|
|
||||||
|
fwrite(
|
||||||
|
STDERR,
|
||||||
|
tsprintf(
|
||||||
|
"**<bg:yellow> %s </bg>** %s\n",
|
||||||
|
pht('VERY META'),
|
||||||
|
pht(
|
||||||
|
'You are running one copy of Arcanist (at path "%s") against '.
|
||||||
|
'another copy of Arcanist (at path "%s"). Code in the current '.
|
||||||
|
'working directory will not be loaded or executed.',
|
||||||
|
$executing_directory,
|
||||||
|
$working_directory)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newToolset(array $argv) {
|
||||||
|
$binary = basename($argv[0]);
|
||||||
|
|
||||||
|
$toolsets = ArcanistToolset::newToolsetMap();
|
||||||
|
if (!isset($toolsets[$binary])) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Arcanist toolset "%s" is unknown. The Arcanist binary should '.
|
||||||
|
'be executed so that "argv[0]" identifies a supported toolset. '.
|
||||||
|
'Rename the binary or install the library that provides the '.
|
||||||
|
'desired toolset. Current available toolsets: %s.',
|
||||||
|
$binary,
|
||||||
|
implode(', ', array_keys($toolsets))));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $toolsets[$binary];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newWorkflows(ArcanistToolset $toolset) {
|
||||||
|
$workflows = id(new PhutilClassMapQuery())
|
||||||
|
->setAncestorClass('ArcanistWorkflow')
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
foreach ($workflows as $key => $workflow) {
|
||||||
|
if (!$workflow->supportsToolset($toolset)) {
|
||||||
|
unset($workflows[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$map = array();
|
||||||
|
foreach ($workflows as $workflow) {
|
||||||
|
$key = $workflow->getWorkflowName();
|
||||||
|
if (isset($map[$key])) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Two workflows ("%s" and "%s") both have the same name ("%s") '.
|
||||||
|
'and both support the current toolset ("%s", "%s"). Each '.
|
||||||
|
'workflow in a given toolset must have a unique name.',
|
||||||
|
get_class($workflow),
|
||||||
|
get_class($map[$key]),
|
||||||
|
get_class($toolset),
|
||||||
|
$toolset->getToolsetKey()));
|
||||||
|
}
|
||||||
|
$map[$key] = id(clone $workflow)
|
||||||
|
->setToolset($toolset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolveAliases(
|
||||||
|
array $workflows,
|
||||||
|
array $argv,
|
||||||
|
ArcanistConfigurationManager $config) {
|
||||||
|
|
||||||
|
$command = head($argv);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
|
||||||
|
$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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,29 +0,0 @@
|
||||||
#!/usr/bin/env php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
require_once dirname(__FILE__).'/__init_script__.php';
|
|
||||||
ini_set('memory_limit', -1);
|
|
||||||
|
|
||||||
$args = new PhutilArgumentParser($argv);
|
|
||||||
$args->parseStandardArguments();
|
|
||||||
|
|
||||||
$args->parsePartial(array());
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: This is pretty minimal and should be shared with "arc".
|
|
||||||
$working_directory = getcwd();
|
|
||||||
$working_copy = ArcanistWorkingCopyIdentity::newFromPath($working_directory);
|
|
||||||
$config = id(new ArcanistConfigurationManager())
|
|
||||||
->setWorkingCopyIdentity($working_copy);
|
|
||||||
|
|
||||||
foreach ($config->getProjectConfig('load') as $load) {
|
|
||||||
$load = Filesystem::resolvePath($working_copy->getProjectRoot().'/'.$load);
|
|
||||||
phutil_load_library($load);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$workflows = id(new PhutilClassMapQuery())
|
|
||||||
->setAncestorClass('PhageWorkflow')
|
|
||||||
->execute();
|
|
||||||
$workflows[] = new PhutilHelpArgumentWorkflow();
|
|
||||||
$args->parseWorkflows($workflows);
|
|
Loading…
Reference in a new issue