2018-09-18 19:12:14 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class ArcanistRuntime {
|
|
|
|
|
[Wilds] Continue toward a generalized "arc alias" workflow
Summary:
Ref T13098. This leaves a lot of rough edges but nothing is overtly broken so here's where we're at so far:
Config sources get "scopes", like user configuration vs system configuration. The major reason for this is so that `arc set-config x y` can know where it's supposed to write. This is generalized enough that we can implement `arc set-config --system ...` and `arc alias --local ...` and so on relatively easily later, although scopes themselves are not modular (a third-party can't add a new type of config scope). Maybe we'll modularize this some day but it felt like that's probably YAGNI/overboard since we have no current use cases. For now, a source does not need to belong to any particular scope.
Config may be writable (like user config in `~/.arcrc`) or nonwritable (like `--config` flags). Writable config can now specify how to write to disk. Config files can actually write to disk now, although the only pathway for doing this that exists is via `arc alias`.
Aliases now parse properly and can write to disk. `arc alias` now lets you define aliases, and writes them to disk. **The first time you do this, your `~/.arcrc` file will be rewritten into a format which old `arc` can not read!** It's relatively easily to unmangle/repair these files so I'm planning to just let this happen.
When a toolset is invoked, it now reads and evaluates aliases. Aliases have a lot of new guard rails like suggesting the user try `arc draft` if they type `phage draft`, allowing alias chains, detecting cycles, and limiting chain length.
Workflows can provide help and argument lists in a more structured way. I've moved this to sub-objects: help is now on `WorkflowInformation` (instead of a bunch of different `getHelp()`, `getSynopsis()` methods) and arguments now have a `WorkflowArgument` object instead of a dictionary. I think this pattern is generally better for extending: it lets us add and change stuff with less impact (and greater explicitness) down the road.
`arc alias` now has reasonable help text and argument documentation. The `arc alias` (list) and `arc alias x` (details/remove) flows don't work yet but `arc alias x y` does.
`arc liberate` now uses the new help/argument stuff, although the help needs more beef eventually. I pruned a bunch of long-obsolete or questionable flags and renamed `--all` to `--clean` since `--all` sounds like "liberate all libraries", which is now the default behavior of `arc liberate`.
Test Plan:
You can now define chains of aliases. Finally!
```
$ arc draft4
WARNING Ignoring unrecognized configuration option ("hosts") from source: User Config File (/Users/epriestley/.arcrc).
WARNING Ignoring unrecognized configuration option ("load") from source: Project Config File (/Users/epriestley/dev/core/.arcconfig).
ALIAS arc draft4 -> arc draft3
ALIAS arc draft3 -> arc draft2
ALIAS arc draft2 -> arc diff
Usage Exception: Unrecognized argument '--draft'.
```
This also works now:
```
$ phage alias deploy-secure -- deploy --hosts secure001-4 --limit 1
```
General!
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T13098
Differential Revision: https://secure.phabricator.com/D19697
2018-09-14 22:45:26 +02:00
|
|
|
private $workflows;
|
2018-09-21 00:28:19 +02:00
|
|
|
private $logEngine;
|
2018-09-24 17:14:44 +02:00
|
|
|
private $lastInterruptTime;
|
|
|
|
|
|
|
|
private $stack = array();
|
[Wilds] Continue toward a generalized "arc alias" workflow
Summary:
Ref T13098. This leaves a lot of rough edges but nothing is overtly broken so here's where we're at so far:
Config sources get "scopes", like user configuration vs system configuration. The major reason for this is so that `arc set-config x y` can know where it's supposed to write. This is generalized enough that we can implement `arc set-config --system ...` and `arc alias --local ...` and so on relatively easily later, although scopes themselves are not modular (a third-party can't add a new type of config scope). Maybe we'll modularize this some day but it felt like that's probably YAGNI/overboard since we have no current use cases. For now, a source does not need to belong to any particular scope.
Config may be writable (like user config in `~/.arcrc`) or nonwritable (like `--config` flags). Writable config can now specify how to write to disk. Config files can actually write to disk now, although the only pathway for doing this that exists is via `arc alias`.
Aliases now parse properly and can write to disk. `arc alias` now lets you define aliases, and writes them to disk. **The first time you do this, your `~/.arcrc` file will be rewritten into a format which old `arc` can not read!** It's relatively easily to unmangle/repair these files so I'm planning to just let this happen.
When a toolset is invoked, it now reads and evaluates aliases. Aliases have a lot of new guard rails like suggesting the user try `arc draft` if they type `phage draft`, allowing alias chains, detecting cycles, and limiting chain length.
Workflows can provide help and argument lists in a more structured way. I've moved this to sub-objects: help is now on `WorkflowInformation` (instead of a bunch of different `getHelp()`, `getSynopsis()` methods) and arguments now have a `WorkflowArgument` object instead of a dictionary. I think this pattern is generally better for extending: it lets us add and change stuff with less impact (and greater explicitness) down the road.
`arc alias` now has reasonable help text and argument documentation. The `arc alias` (list) and `arc alias x` (details/remove) flows don't work yet but `arc alias x y` does.
`arc liberate` now uses the new help/argument stuff, although the help needs more beef eventually. I pruned a bunch of long-obsolete or questionable flags and renamed `--all` to `--clean` since `--all` sounds like "liberate all libraries", which is now the default behavior of `arc liberate`.
Test Plan:
You can now define chains of aliases. Finally!
```
$ arc draft4
WARNING Ignoring unrecognized configuration option ("hosts") from source: User Config File (/Users/epriestley/.arcrc).
WARNING Ignoring unrecognized configuration option ("load") from source: Project Config File (/Users/epriestley/dev/core/.arcconfig).
ALIAS arc draft4 -> arc draft3
ALIAS arc draft3 -> arc draft2
ALIAS arc draft2 -> arc diff
Usage Exception: Unrecognized argument '--draft'.
```
This also works now:
```
$ phage alias deploy-secure -- deploy --hosts secure001-4 --limit 1
```
General!
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T13098
Differential Revision: https://secure.phabricator.com/D19697
2018-09-14 22:45:26 +02:00
|
|
|
|
2018-09-18 19:12:14 +02:00
|
|
|
public function execute(array $argv) {
|
2018-09-18 20:07:45 +02:00
|
|
|
|
|
|
|
try {
|
|
|
|
$this->checkEnvironment();
|
|
|
|
} catch (Exception $ex) {
|
|
|
|
echo "CONFIGURATION ERROR\n\n";
|
|
|
|
echo $ex->getMessage();
|
|
|
|
echo "\n\n";
|
|
|
|
return 1;
|
2018-09-18 19:12:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
PhutilTranslator::getInstance()
|
|
|
|
->setLocale(PhutilLocale::loadLocale('en_US'))
|
|
|
|
->setTranslations(PhutilTranslation::getTranslationMapForLocale('en_US'));
|
|
|
|
|
2018-09-21 00:28:19 +02:00
|
|
|
$log = new ArcanistLogEngine();
|
|
|
|
$this->logEngine = $log;
|
|
|
|
|
2018-09-18 19:12:14 +02:00
|
|
|
try {
|
|
|
|
return $this->executeCore($argv);
|
2018-09-20 23:35:19 +02:00
|
|
|
} catch (ArcanistConduitException $ex) {
|
2018-09-21 00:28:19 +02:00
|
|
|
$log->writeError(pht('CONDUIT'), $ex->getMessage());
|
2018-09-18 19:12:14 +02:00
|
|
|
} catch (PhutilArgumentUsageException $ex) {
|
2018-09-21 00:28:19 +02:00
|
|
|
$log->writeError(pht('USAGE EXCEPTION'), $ex->getMessage());
|
[Wilds] Provide a skeleton for prompt behaviors
Summary:
Ref T13098. Ref T13198. Ref T12996. The major ideas here are:
Workflows must define a list of all the prompts they can raise, so that these prompts can be enumerated with `arc prompts <workflow>`.
Prompts themselves should respond properly to ^C (abort immediately) and we should be able to make them nonblocking in the future (particularly, we'd like to be able to continue reading bytes from subprocess buffers while the prompt is shown on screen).
This doesn't have a lot of fancy features yet (non-confirm prompts, default yes, prompts which don't abort on "N", etc) but those should be easy to add later.
In the future, you'll be able to configure a default answer to prompts either in a config file or at runtime with `--config prompts=x.y.z=N` or similar.
This removes the history/readline mode for prompts, where you could use the up arrow to cycle through older responses. I believe this was only really useful for "excuse" prompts and intend to remove those.
Test Plan:
Forced `arc shell-complete` to always prompt, then:
- Got prompted, answered "y", "n", "N", "", "quack". Got sensible behavior in all cases.
- Ran `echo | arc shell-complete`, got a TTY error.
- Ran `arc prompts`, `arc prompts shell-complete`.
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T13098, T13198, T12996
Differential Revision: https://secure.phabricator.com/D19706
2018-09-25 18:07:16 +02:00
|
|
|
} catch (ArcanistUserAbortException $ex) {
|
|
|
|
$log->writeError(pht('---'), $ex->getMessage());
|
2018-09-18 19:12:14 +02:00
|
|
|
}
|
2018-09-21 00:28:19 +02:00
|
|
|
|
|
|
|
return 1;
|
2018-09-18 19:12:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private function executeCore(array $argv) {
|
2018-09-21 00:28:19 +02:00
|
|
|
$log = $this->getLogEngine();
|
|
|
|
|
2018-09-18 19:12:14 +02:00
|
|
|
$config_args = array(
|
|
|
|
array(
|
|
|
|
'name' => 'library',
|
|
|
|
'param' => 'path',
|
[Wilds] Remove libphutil
Summary:
Ref T13098. Historically, Phabricator was split into three parts:
- Phabricator, the server.
- Arcanist, the client.
- libphutil, libraries shared between the client and server.
One imagined use case for this was that `libphutil` might become a general-purpose library that other projects would use.
However, this didn't really happen, and it seems unlikely to at this point: Phabricator has become a relatively more sophisticated application platform; we didn't end up seeing or encouraging much custom development; what custom development there is basically embraces all of Phabricator since there are huge advantages to doing so; and a general "open source is awful" sort of factor here in the sense that open source users often don't have goals well aligned to our goals.
Turning "arc" into a client platform and building package management solidify us in this direction of being a standalone platform, not a standalone utility library.
Phabricator also depends on `arcanist/`. If it didn't, there would be a small advantage to saying "shared code + client for client, shared code + server for server", but there's no such distinction and it seems unlikely that one will ever exist. Even if it did, I think this has little value.
Nowadays, I think this separation has no advantages for us and one significant cost: it makes installing `arcanist` more difficult for end-users.
This will need some more finesssing (Phabricator will need some changes for compatibility, and a lot of stuff that still says "libphutil" or "phutil" may eventually want to say "arcanist"), and some stuff (like xhpast) is probably straight-up broken right now and needs some tweaking, but I don't anticipate any major issues here. There was never anything particularly magical about libphutil as a separate standalone library.
Test Plan: Ran `arc`, it gets about as far as it did before.
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T13098
Differential Revision: https://secure.phabricator.com/D19688
2018-09-18 19:37:45 +02:00
|
|
|
'help' => pht('Load a library.'),
|
2018-09-18 19:12:14 +02:00
|
|
|
'repeat' => true,
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
'name' => 'config',
|
|
|
|
'param' => 'key=value',
|
|
|
|
'repeat' => true,
|
|
|
|
'help' => pht('Specify a runtime configuration value.'),
|
|
|
|
),
|
2018-09-18 21:49:51 +02:00
|
|
|
array(
|
|
|
|
'name' => 'config-file',
|
|
|
|
'param' => 'path',
|
|
|
|
'repeat' => true,
|
|
|
|
'help' => pht(
|
|
|
|
'Load one or more configuration files. If this flag is provided, '.
|
|
|
|
'the system and user configuration files are ignored.'),
|
|
|
|
),
|
2018-09-18 19:12:14 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
$args = id(new PhutilArgumentParser($argv))
|
|
|
|
->parseStandardArguments();
|
|
|
|
|
|
|
|
$is_trace = $args->getArg('trace');
|
2018-09-21 00:28:19 +02:00
|
|
|
$log->setShowTraceMessages($is_trace);
|
|
|
|
|
|
|
|
$log->writeTrace(pht('ARGV'), csprintf('%Ls', $argv));
|
2018-09-18 19:12:14 +02:00
|
|
|
|
2018-09-24 17:14:44 +02:00
|
|
|
// We're installing the signal handler after parsing "--trace" so that it
|
|
|
|
// can emit debugging messages. This means there's a very small window at
|
|
|
|
// startup where signals have no special handling, but we couldn't really
|
|
|
|
// route them or do anything interesting with them anyway.
|
|
|
|
$this->installSignalHandler();
|
|
|
|
|
2018-09-18 19:12:14 +02:00
|
|
|
$args->parsePartial($config_args, true);
|
|
|
|
|
2018-09-18 22:46:12 +02:00
|
|
|
$config_engine = $this->loadConfiguration($args);
|
|
|
|
$config = $config_engine->newConfigurationSourceList();
|
2018-09-18 19:12:14 +02:00
|
|
|
|
|
|
|
$this->loadLibraries($args, $config);
|
|
|
|
|
2018-09-18 22:46:12 +02:00
|
|
|
// Now that we've loaded libraries, we can validate configuration.
|
|
|
|
// Do this before continuing since configuration can impact other
|
|
|
|
// behaviors immediately and we want to catch any issues right away.
|
|
|
|
$config->setConfigOptions($config_engine->newConfigOptionsMap());
|
2018-09-21 00:28:19 +02:00
|
|
|
$config->validateConfiguration($this);
|
2018-09-18 22:46:12 +02:00
|
|
|
|
2018-09-18 19:12:14 +02:00
|
|
|
$toolset = $this->newToolset($argv);
|
|
|
|
|
|
|
|
$args->parsePartial($toolset->getToolsetArguments());
|
|
|
|
|
|
|
|
$workflows = $this->newWorkflows($toolset);
|
[Wilds] Continue toward a generalized "arc alias" workflow
Summary:
Ref T13098. This leaves a lot of rough edges but nothing is overtly broken so here's where we're at so far:
Config sources get "scopes", like user configuration vs system configuration. The major reason for this is so that `arc set-config x y` can know where it's supposed to write. This is generalized enough that we can implement `arc set-config --system ...` and `arc alias --local ...` and so on relatively easily later, although scopes themselves are not modular (a third-party can't add a new type of config scope). Maybe we'll modularize this some day but it felt like that's probably YAGNI/overboard since we have no current use cases. For now, a source does not need to belong to any particular scope.
Config may be writable (like user config in `~/.arcrc`) or nonwritable (like `--config` flags). Writable config can now specify how to write to disk. Config files can actually write to disk now, although the only pathway for doing this that exists is via `arc alias`.
Aliases now parse properly and can write to disk. `arc alias` now lets you define aliases, and writes them to disk. **The first time you do this, your `~/.arcrc` file will be rewritten into a format which old `arc` can not read!** It's relatively easily to unmangle/repair these files so I'm planning to just let this happen.
When a toolset is invoked, it now reads and evaluates aliases. Aliases have a lot of new guard rails like suggesting the user try `arc draft` if they type `phage draft`, allowing alias chains, detecting cycles, and limiting chain length.
Workflows can provide help and argument lists in a more structured way. I've moved this to sub-objects: help is now on `WorkflowInformation` (instead of a bunch of different `getHelp()`, `getSynopsis()` methods) and arguments now have a `WorkflowArgument` object instead of a dictionary. I think this pattern is generally better for extending: it lets us add and change stuff with less impact (and greater explicitness) down the road.
`arc alias` now has reasonable help text and argument documentation. The `arc alias` (list) and `arc alias x` (details/remove) flows don't work yet but `arc alias x y` does.
`arc liberate` now uses the new help/argument stuff, although the help needs more beef eventually. I pruned a bunch of long-obsolete or questionable flags and renamed `--all` to `--clean` since `--all` sounds like "liberate all libraries", which is now the default behavior of `arc liberate`.
Test Plan:
You can now define chains of aliases. Finally!
```
$ arc draft4
WARNING Ignoring unrecognized configuration option ("hosts") from source: User Config File (/Users/epriestley/.arcrc).
WARNING Ignoring unrecognized configuration option ("load") from source: Project Config File (/Users/epriestley/dev/core/.arcconfig).
ALIAS arc draft4 -> arc draft3
ALIAS arc draft3 -> arc draft2
ALIAS arc draft2 -> arc diff
Usage Exception: Unrecognized argument '--draft'.
```
This also works now:
```
$ phage alias deploy-secure -- deploy --hosts secure001-4 --limit 1
```
General!
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T13098
Differential Revision: https://secure.phabricator.com/D19697
2018-09-14 22:45:26 +02:00
|
|
|
$this->workflows = $workflows;
|
2018-09-18 19:12:14 +02:00
|
|
|
|
|
|
|
$phutil_workflows = array();
|
|
|
|
foreach ($workflows as $key => $workflow) {
|
|
|
|
$phutil_workflows[$key] = $workflow->newPhutilWorkflow();
|
2018-09-18 22:46:12 +02:00
|
|
|
|
|
|
|
$workflow
|
[Wilds] Continue toward a generalized "arc alias" workflow
Summary:
Ref T13098. This leaves a lot of rough edges but nothing is overtly broken so here's where we're at so far:
Config sources get "scopes", like user configuration vs system configuration. The major reason for this is so that `arc set-config x y` can know where it's supposed to write. This is generalized enough that we can implement `arc set-config --system ...` and `arc alias --local ...` and so on relatively easily later, although scopes themselves are not modular (a third-party can't add a new type of config scope). Maybe we'll modularize this some day but it felt like that's probably YAGNI/overboard since we have no current use cases. For now, a source does not need to belong to any particular scope.
Config may be writable (like user config in `~/.arcrc`) or nonwritable (like `--config` flags). Writable config can now specify how to write to disk. Config files can actually write to disk now, although the only pathway for doing this that exists is via `arc alias`.
Aliases now parse properly and can write to disk. `arc alias` now lets you define aliases, and writes them to disk. **The first time you do this, your `~/.arcrc` file will be rewritten into a format which old `arc` can not read!** It's relatively easily to unmangle/repair these files so I'm planning to just let this happen.
When a toolset is invoked, it now reads and evaluates aliases. Aliases have a lot of new guard rails like suggesting the user try `arc draft` if they type `phage draft`, allowing alias chains, detecting cycles, and limiting chain length.
Workflows can provide help and argument lists in a more structured way. I've moved this to sub-objects: help is now on `WorkflowInformation` (instead of a bunch of different `getHelp()`, `getSynopsis()` methods) and arguments now have a `WorkflowArgument` object instead of a dictionary. I think this pattern is generally better for extending: it lets us add and change stuff with less impact (and greater explicitness) down the road.
`arc alias` now has reasonable help text and argument documentation. The `arc alias` (list) and `arc alias x` (details/remove) flows don't work yet but `arc alias x y` does.
`arc liberate` now uses the new help/argument stuff, although the help needs more beef eventually. I pruned a bunch of long-obsolete or questionable flags and renamed `--all` to `--clean` since `--all` sounds like "liberate all libraries", which is now the default behavior of `arc liberate`.
Test Plan:
You can now define chains of aliases. Finally!
```
$ arc draft4
WARNING Ignoring unrecognized configuration option ("hosts") from source: User Config File (/Users/epriestley/.arcrc).
WARNING Ignoring unrecognized configuration option ("load") from source: Project Config File (/Users/epriestley/dev/core/.arcconfig).
ALIAS arc draft4 -> arc draft3
ALIAS arc draft3 -> arc draft2
ALIAS arc draft2 -> arc diff
Usage Exception: Unrecognized argument '--draft'.
```
This also works now:
```
$ phage alias deploy-secure -- deploy --hosts secure001-4 --limit 1
```
General!
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T13098
Differential Revision: https://secure.phabricator.com/D19697
2018-09-14 22:45:26 +02:00
|
|
|
->setRuntime($this)
|
2018-09-18 22:46:12 +02:00
|
|
|
->setConfigurationEngine($config_engine)
|
|
|
|
->setConfigurationSourceList($config);
|
2018-09-18 19:12:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$unconsumed_argv = $args->getUnconsumedArgumentVector();
|
|
|
|
|
|
|
|
if (!$unconsumed_argv) {
|
|
|
|
// TOOLSETS: This means the user just ran "arc" or some other top-level
|
|
|
|
// toolset without any workflow argument. We should give them a summary
|
|
|
|
// of the toolset, a list of workflows, and a pointer to "arc help" for
|
|
|
|
// more details.
|
|
|
|
|
|
|
|
// A possible exception is "arc --help", which should perhaps pass
|
|
|
|
// through and act like "arc help".
|
|
|
|
throw new PhutilArgumentUsageException(pht('Choose a workflow!'));
|
|
|
|
}
|
|
|
|
|
[Wilds] Continue toward a generalized "arc alias" workflow
Summary:
Ref T13098. This leaves a lot of rough edges but nothing is overtly broken so here's where we're at so far:
Config sources get "scopes", like user configuration vs system configuration. The major reason for this is so that `arc set-config x y` can know where it's supposed to write. This is generalized enough that we can implement `arc set-config --system ...` and `arc alias --local ...` and so on relatively easily later, although scopes themselves are not modular (a third-party can't add a new type of config scope). Maybe we'll modularize this some day but it felt like that's probably YAGNI/overboard since we have no current use cases. For now, a source does not need to belong to any particular scope.
Config may be writable (like user config in `~/.arcrc`) or nonwritable (like `--config` flags). Writable config can now specify how to write to disk. Config files can actually write to disk now, although the only pathway for doing this that exists is via `arc alias`.
Aliases now parse properly and can write to disk. `arc alias` now lets you define aliases, and writes them to disk. **The first time you do this, your `~/.arcrc` file will be rewritten into a format which old `arc` can not read!** It's relatively easily to unmangle/repair these files so I'm planning to just let this happen.
When a toolset is invoked, it now reads and evaluates aliases. Aliases have a lot of new guard rails like suggesting the user try `arc draft` if they type `phage draft`, allowing alias chains, detecting cycles, and limiting chain length.
Workflows can provide help and argument lists in a more structured way. I've moved this to sub-objects: help is now on `WorkflowInformation` (instead of a bunch of different `getHelp()`, `getSynopsis()` methods) and arguments now have a `WorkflowArgument` object instead of a dictionary. I think this pattern is generally better for extending: it lets us add and change stuff with less impact (and greater explicitness) down the road.
`arc alias` now has reasonable help text and argument documentation. The `arc alias` (list) and `arc alias x` (details/remove) flows don't work yet but `arc alias x y` does.
`arc liberate` now uses the new help/argument stuff, although the help needs more beef eventually. I pruned a bunch of long-obsolete or questionable flags and renamed `--all` to `--clean` since `--all` sounds like "liberate all libraries", which is now the default behavior of `arc liberate`.
Test Plan:
You can now define chains of aliases. Finally!
```
$ arc draft4
WARNING Ignoring unrecognized configuration option ("hosts") from source: User Config File (/Users/epriestley/.arcrc).
WARNING Ignoring unrecognized configuration option ("load") from source: Project Config File (/Users/epriestley/dev/core/.arcconfig).
ALIAS arc draft4 -> arc draft3
ALIAS arc draft3 -> arc draft2
ALIAS arc draft2 -> arc diff
Usage Exception: Unrecognized argument '--draft'.
```
This also works now:
```
$ phage alias deploy-secure -- deploy --hosts secure001-4 --limit 1
```
General!
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T13098
Differential Revision: https://secure.phabricator.com/D19697
2018-09-14 22:45:26 +02:00
|
|
|
$alias_effects = id(new ArcanistAliasEngine())
|
|
|
|
->setRuntime($this)
|
|
|
|
->setToolset($toolset)
|
|
|
|
->setWorkflows($workflows)
|
|
|
|
->setConfigurationSourceList($config)
|
|
|
|
->resolveAliases($unconsumed_argv);
|
2018-09-18 19:12:14 +02:00
|
|
|
|
[Wilds] Continue toward a generalized "arc alias" workflow
Summary:
Ref T13098. This leaves a lot of rough edges but nothing is overtly broken so here's where we're at so far:
Config sources get "scopes", like user configuration vs system configuration. The major reason for this is so that `arc set-config x y` can know where it's supposed to write. This is generalized enough that we can implement `arc set-config --system ...` and `arc alias --local ...` and so on relatively easily later, although scopes themselves are not modular (a third-party can't add a new type of config scope). Maybe we'll modularize this some day but it felt like that's probably YAGNI/overboard since we have no current use cases. For now, a source does not need to belong to any particular scope.
Config may be writable (like user config in `~/.arcrc`) or nonwritable (like `--config` flags). Writable config can now specify how to write to disk. Config files can actually write to disk now, although the only pathway for doing this that exists is via `arc alias`.
Aliases now parse properly and can write to disk. `arc alias` now lets you define aliases, and writes them to disk. **The first time you do this, your `~/.arcrc` file will be rewritten into a format which old `arc` can not read!** It's relatively easily to unmangle/repair these files so I'm planning to just let this happen.
When a toolset is invoked, it now reads and evaluates aliases. Aliases have a lot of new guard rails like suggesting the user try `arc draft` if they type `phage draft`, allowing alias chains, detecting cycles, and limiting chain length.
Workflows can provide help and argument lists in a more structured way. I've moved this to sub-objects: help is now on `WorkflowInformation` (instead of a bunch of different `getHelp()`, `getSynopsis()` methods) and arguments now have a `WorkflowArgument` object instead of a dictionary. I think this pattern is generally better for extending: it lets us add and change stuff with less impact (and greater explicitness) down the road.
`arc alias` now has reasonable help text and argument documentation. The `arc alias` (list) and `arc alias x` (details/remove) flows don't work yet but `arc alias x y` does.
`arc liberate` now uses the new help/argument stuff, although the help needs more beef eventually. I pruned a bunch of long-obsolete or questionable flags and renamed `--all` to `--clean` since `--all` sounds like "liberate all libraries", which is now the default behavior of `arc liberate`.
Test Plan:
You can now define chains of aliases. Finally!
```
$ arc draft4
WARNING Ignoring unrecognized configuration option ("hosts") from source: User Config File (/Users/epriestley/.arcrc).
WARNING Ignoring unrecognized configuration option ("load") from source: Project Config File (/Users/epriestley/dev/core/.arcconfig).
ALIAS arc draft4 -> arc draft3
ALIAS arc draft3 -> arc draft2
ALIAS arc draft2 -> arc diff
Usage Exception: Unrecognized argument '--draft'.
```
This also works now:
```
$ phage alias deploy-secure -- deploy --hosts secure001-4 --limit 1
```
General!
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T13098
Differential Revision: https://secure.phabricator.com/D19697
2018-09-14 22:45:26 +02:00
|
|
|
$result_argv = $this->applyAliasEffects($alias_effects, $unconsumed_argv);
|
|
|
|
|
|
|
|
$args->setUnconsumedArgumentVector($result_argv);
|
2018-09-18 19:12:14 +02:00
|
|
|
|
|
|
|
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);
|
2018-09-18 20:07:45 +02:00
|
|
|
|
|
|
|
throw new Exception($message);
|
2018-09-18 19:12:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2018-09-18 20:07:45 +02:00
|
|
|
throw new Exception($problems);
|
|
|
|
}
|
2018-09-18 19:12:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private function loadConfiguration(PhutilArgumentParser $args) {
|
2018-09-18 21:49:51 +02:00
|
|
|
$engine = id(new ArcanistConfigurationEngine())
|
|
|
|
->setArguments($args);
|
2018-09-18 19:12:14 +02:00
|
|
|
|
2018-09-18 21:49:51 +02:00
|
|
|
$working_copy = ArcanistWorkingCopy::newFromWorkingDirectory(getcwd());
|
2018-09-18 20:11:54 +02:00
|
|
|
if ($working_copy) {
|
|
|
|
$engine->setWorkingCopy($working_copy);
|
|
|
|
}
|
2018-09-18 19:12:14 +02:00
|
|
|
|
2018-09-18 22:46:12 +02:00
|
|
|
return $engine;
|
2018-09-18 19:12:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private function loadLibraries(
|
|
|
|
PhutilArgumentParser $args,
|
2018-09-18 21:49:51 +02:00
|
|
|
ArcanistConfigurationSourceList $config) {
|
2018-09-18 19:12:14 +02:00
|
|
|
|
2018-09-18 21:49:51 +02:00
|
|
|
// TOOLSETS: Make this work again -- or replace it entirely with package
|
|
|
|
// management?
|
|
|
|
return;
|
2018-09-18 19:12:14 +02:00
|
|
|
|
2018-09-18 21:49:51 +02:00
|
|
|
$is_trace = $args->getArg('trace');
|
2018-09-18 19:12:14 +02:00
|
|
|
|
|
|
|
$load = array();
|
2018-09-18 21:49:51 +02:00
|
|
|
$working_copy = $this->getWorkingCopy();
|
2018-09-18 19:12:14 +02:00
|
|
|
|
|
|
|
$cli_libraries = $args->getArg('library');
|
|
|
|
if ($cli_libraries) {
|
|
|
|
$load[] = array(
|
|
|
|
'--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;
|
|
|
|
}
|
|
|
|
|
[Wilds] Continue toward a generalized "arc alias" workflow
Summary:
Ref T13098. This leaves a lot of rough edges but nothing is overtly broken so here's where we're at so far:
Config sources get "scopes", like user configuration vs system configuration. The major reason for this is so that `arc set-config x y` can know where it's supposed to write. This is generalized enough that we can implement `arc set-config --system ...` and `arc alias --local ...` and so on relatively easily later, although scopes themselves are not modular (a third-party can't add a new type of config scope). Maybe we'll modularize this some day but it felt like that's probably YAGNI/overboard since we have no current use cases. For now, a source does not need to belong to any particular scope.
Config may be writable (like user config in `~/.arcrc`) or nonwritable (like `--config` flags). Writable config can now specify how to write to disk. Config files can actually write to disk now, although the only pathway for doing this that exists is via `arc alias`.
Aliases now parse properly and can write to disk. `arc alias` now lets you define aliases, and writes them to disk. **The first time you do this, your `~/.arcrc` file will be rewritten into a format which old `arc` can not read!** It's relatively easily to unmangle/repair these files so I'm planning to just let this happen.
When a toolset is invoked, it now reads and evaluates aliases. Aliases have a lot of new guard rails like suggesting the user try `arc draft` if they type `phage draft`, allowing alias chains, detecting cycles, and limiting chain length.
Workflows can provide help and argument lists in a more structured way. I've moved this to sub-objects: help is now on `WorkflowInformation` (instead of a bunch of different `getHelp()`, `getSynopsis()` methods) and arguments now have a `WorkflowArgument` object instead of a dictionary. I think this pattern is generally better for extending: it lets us add and change stuff with less impact (and greater explicitness) down the road.
`arc alias` now has reasonable help text and argument documentation. The `arc alias` (list) and `arc alias x` (details/remove) flows don't work yet but `arc alias x y` does.
`arc liberate` now uses the new help/argument stuff, although the help needs more beef eventually. I pruned a bunch of long-obsolete or questionable flags and renamed `--all` to `--clean` since `--all` sounds like "liberate all libraries", which is now the default behavior of `arc liberate`.
Test Plan:
You can now define chains of aliases. Finally!
```
$ arc draft4
WARNING Ignoring unrecognized configuration option ("hosts") from source: User Config File (/Users/epriestley/.arcrc).
WARNING Ignoring unrecognized configuration option ("load") from source: Project Config File (/Users/epriestley/dev/core/.arcconfig).
ALIAS arc draft4 -> arc draft3
ALIAS arc draft3 -> arc draft2
ALIAS arc draft2 -> arc diff
Usage Exception: Unrecognized argument '--draft'.
```
This also works now:
```
$ phage alias deploy-secure -- deploy --hosts secure001-4 --limit 1
```
General!
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T13098
Differential Revision: https://secure.phabricator.com/D19697
2018-09-14 22:45:26 +02:00
|
|
|
public function getWorkflows() {
|
|
|
|
return $this->workflows;
|
|
|
|
}
|
2018-09-18 19:12:14 +02:00
|
|
|
|
2018-09-21 00:28:19 +02:00
|
|
|
public function getLogEngine() {
|
|
|
|
return $this->logEngine;
|
|
|
|
}
|
|
|
|
|
[Wilds] Continue toward a generalized "arc alias" workflow
Summary:
Ref T13098. This leaves a lot of rough edges but nothing is overtly broken so here's where we're at so far:
Config sources get "scopes", like user configuration vs system configuration. The major reason for this is so that `arc set-config x y` can know where it's supposed to write. This is generalized enough that we can implement `arc set-config --system ...` and `arc alias --local ...` and so on relatively easily later, although scopes themselves are not modular (a third-party can't add a new type of config scope). Maybe we'll modularize this some day but it felt like that's probably YAGNI/overboard since we have no current use cases. For now, a source does not need to belong to any particular scope.
Config may be writable (like user config in `~/.arcrc`) or nonwritable (like `--config` flags). Writable config can now specify how to write to disk. Config files can actually write to disk now, although the only pathway for doing this that exists is via `arc alias`.
Aliases now parse properly and can write to disk. `arc alias` now lets you define aliases, and writes them to disk. **The first time you do this, your `~/.arcrc` file will be rewritten into a format which old `arc` can not read!** It's relatively easily to unmangle/repair these files so I'm planning to just let this happen.
When a toolset is invoked, it now reads and evaluates aliases. Aliases have a lot of new guard rails like suggesting the user try `arc draft` if they type `phage draft`, allowing alias chains, detecting cycles, and limiting chain length.
Workflows can provide help and argument lists in a more structured way. I've moved this to sub-objects: help is now on `WorkflowInformation` (instead of a bunch of different `getHelp()`, `getSynopsis()` methods) and arguments now have a `WorkflowArgument` object instead of a dictionary. I think this pattern is generally better for extending: it lets us add and change stuff with less impact (and greater explicitness) down the road.
`arc alias` now has reasonable help text and argument documentation. The `arc alias` (list) and `arc alias x` (details/remove) flows don't work yet but `arc alias x y` does.
`arc liberate` now uses the new help/argument stuff, although the help needs more beef eventually. I pruned a bunch of long-obsolete or questionable flags and renamed `--all` to `--clean` since `--all` sounds like "liberate all libraries", which is now the default behavior of `arc liberate`.
Test Plan:
You can now define chains of aliases. Finally!
```
$ arc draft4
WARNING Ignoring unrecognized configuration option ("hosts") from source: User Config File (/Users/epriestley/.arcrc).
WARNING Ignoring unrecognized configuration option ("load") from source: Project Config File (/Users/epriestley/dev/core/.arcconfig).
ALIAS arc draft4 -> arc draft3
ALIAS arc draft3 -> arc draft2
ALIAS arc draft2 -> arc diff
Usage Exception: Unrecognized argument '--draft'.
```
This also works now:
```
$ phage alias deploy-secure -- deploy --hosts secure001-4 --limit 1
```
General!
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T13098
Differential Revision: https://secure.phabricator.com/D19697
2018-09-14 22:45:26 +02:00
|
|
|
private function applyAliasEffects(array $effects, array $argv) {
|
|
|
|
assert_instances_of($effects, 'ArcanistAliasEffect');
|
|
|
|
|
2018-09-21 00:28:19 +02:00
|
|
|
$log = $this->getLogEngine();
|
|
|
|
|
[Wilds] Continue toward a generalized "arc alias" workflow
Summary:
Ref T13098. This leaves a lot of rough edges but nothing is overtly broken so here's where we're at so far:
Config sources get "scopes", like user configuration vs system configuration. The major reason for this is so that `arc set-config x y` can know where it's supposed to write. This is generalized enough that we can implement `arc set-config --system ...` and `arc alias --local ...` and so on relatively easily later, although scopes themselves are not modular (a third-party can't add a new type of config scope). Maybe we'll modularize this some day but it felt like that's probably YAGNI/overboard since we have no current use cases. For now, a source does not need to belong to any particular scope.
Config may be writable (like user config in `~/.arcrc`) or nonwritable (like `--config` flags). Writable config can now specify how to write to disk. Config files can actually write to disk now, although the only pathway for doing this that exists is via `arc alias`.
Aliases now parse properly and can write to disk. `arc alias` now lets you define aliases, and writes them to disk. **The first time you do this, your `~/.arcrc` file will be rewritten into a format which old `arc` can not read!** It's relatively easily to unmangle/repair these files so I'm planning to just let this happen.
When a toolset is invoked, it now reads and evaluates aliases. Aliases have a lot of new guard rails like suggesting the user try `arc draft` if they type `phage draft`, allowing alias chains, detecting cycles, and limiting chain length.
Workflows can provide help and argument lists in a more structured way. I've moved this to sub-objects: help is now on `WorkflowInformation` (instead of a bunch of different `getHelp()`, `getSynopsis()` methods) and arguments now have a `WorkflowArgument` object instead of a dictionary. I think this pattern is generally better for extending: it lets us add and change stuff with less impact (and greater explicitness) down the road.
`arc alias` now has reasonable help text and argument documentation. The `arc alias` (list) and `arc alias x` (details/remove) flows don't work yet but `arc alias x y` does.
`arc liberate` now uses the new help/argument stuff, although the help needs more beef eventually. I pruned a bunch of long-obsolete or questionable flags and renamed `--all` to `--clean` since `--all` sounds like "liberate all libraries", which is now the default behavior of `arc liberate`.
Test Plan:
You can now define chains of aliases. Finally!
```
$ arc draft4
WARNING Ignoring unrecognized configuration option ("hosts") from source: User Config File (/Users/epriestley/.arcrc).
WARNING Ignoring unrecognized configuration option ("load") from source: Project Config File (/Users/epriestley/dev/core/.arcconfig).
ALIAS arc draft4 -> arc draft3
ALIAS arc draft3 -> arc draft2
ALIAS arc draft2 -> arc diff
Usage Exception: Unrecognized argument '--draft'.
```
This also works now:
```
$ phage alias deploy-secure -- deploy --hosts secure001-4 --limit 1
```
General!
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T13098
Differential Revision: https://secure.phabricator.com/D19697
2018-09-14 22:45:26 +02:00
|
|
|
$command = null;
|
|
|
|
$arguments = null;
|
|
|
|
foreach ($effects as $effect) {
|
|
|
|
$message = $effect->getMessage();
|
|
|
|
|
|
|
|
if ($message !== null) {
|
2018-09-21 00:28:19 +02:00
|
|
|
$log->writeInfo(pht('ALIAS'), $message);
|
[Wilds] Continue toward a generalized "arc alias" workflow
Summary:
Ref T13098. This leaves a lot of rough edges but nothing is overtly broken so here's where we're at so far:
Config sources get "scopes", like user configuration vs system configuration. The major reason for this is so that `arc set-config x y` can know where it's supposed to write. This is generalized enough that we can implement `arc set-config --system ...` and `arc alias --local ...` and so on relatively easily later, although scopes themselves are not modular (a third-party can't add a new type of config scope). Maybe we'll modularize this some day but it felt like that's probably YAGNI/overboard since we have no current use cases. For now, a source does not need to belong to any particular scope.
Config may be writable (like user config in `~/.arcrc`) or nonwritable (like `--config` flags). Writable config can now specify how to write to disk. Config files can actually write to disk now, although the only pathway for doing this that exists is via `arc alias`.
Aliases now parse properly and can write to disk. `arc alias` now lets you define aliases, and writes them to disk. **The first time you do this, your `~/.arcrc` file will be rewritten into a format which old `arc` can not read!** It's relatively easily to unmangle/repair these files so I'm planning to just let this happen.
When a toolset is invoked, it now reads and evaluates aliases. Aliases have a lot of new guard rails like suggesting the user try `arc draft` if they type `phage draft`, allowing alias chains, detecting cycles, and limiting chain length.
Workflows can provide help and argument lists in a more structured way. I've moved this to sub-objects: help is now on `WorkflowInformation` (instead of a bunch of different `getHelp()`, `getSynopsis()` methods) and arguments now have a `WorkflowArgument` object instead of a dictionary. I think this pattern is generally better for extending: it lets us add and change stuff with less impact (and greater explicitness) down the road.
`arc alias` now has reasonable help text and argument documentation. The `arc alias` (list) and `arc alias x` (details/remove) flows don't work yet but `arc alias x y` does.
`arc liberate` now uses the new help/argument stuff, although the help needs more beef eventually. I pruned a bunch of long-obsolete or questionable flags and renamed `--all` to `--clean` since `--all` sounds like "liberate all libraries", which is now the default behavior of `arc liberate`.
Test Plan:
You can now define chains of aliases. Finally!
```
$ arc draft4
WARNING Ignoring unrecognized configuration option ("hosts") from source: User Config File (/Users/epriestley/.arcrc).
WARNING Ignoring unrecognized configuration option ("load") from source: Project Config File (/Users/epriestley/dev/core/.arcconfig).
ALIAS arc draft4 -> arc draft3
ALIAS arc draft3 -> arc draft2
ALIAS arc draft2 -> arc diff
Usage Exception: Unrecognized argument '--draft'.
```
This also works now:
```
$ phage alias deploy-secure -- deploy --hosts secure001-4 --limit 1
```
General!
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T13098
Differential Revision: https://secure.phabricator.com/D19697
2018-09-14 22:45:26 +02:00
|
|
|
}
|
2018-09-18 19:12:14 +02:00
|
|
|
|
[Wilds] Continue toward a generalized "arc alias" workflow
Summary:
Ref T13098. This leaves a lot of rough edges but nothing is overtly broken so here's where we're at so far:
Config sources get "scopes", like user configuration vs system configuration. The major reason for this is so that `arc set-config x y` can know where it's supposed to write. This is generalized enough that we can implement `arc set-config --system ...` and `arc alias --local ...` and so on relatively easily later, although scopes themselves are not modular (a third-party can't add a new type of config scope). Maybe we'll modularize this some day but it felt like that's probably YAGNI/overboard since we have no current use cases. For now, a source does not need to belong to any particular scope.
Config may be writable (like user config in `~/.arcrc`) or nonwritable (like `--config` flags). Writable config can now specify how to write to disk. Config files can actually write to disk now, although the only pathway for doing this that exists is via `arc alias`.
Aliases now parse properly and can write to disk. `arc alias` now lets you define aliases, and writes them to disk. **The first time you do this, your `~/.arcrc` file will be rewritten into a format which old `arc` can not read!** It's relatively easily to unmangle/repair these files so I'm planning to just let this happen.
When a toolset is invoked, it now reads and evaluates aliases. Aliases have a lot of new guard rails like suggesting the user try `arc draft` if they type `phage draft`, allowing alias chains, detecting cycles, and limiting chain length.
Workflows can provide help and argument lists in a more structured way. I've moved this to sub-objects: help is now on `WorkflowInformation` (instead of a bunch of different `getHelp()`, `getSynopsis()` methods) and arguments now have a `WorkflowArgument` object instead of a dictionary. I think this pattern is generally better for extending: it lets us add and change stuff with less impact (and greater explicitness) down the road.
`arc alias` now has reasonable help text and argument documentation. The `arc alias` (list) and `arc alias x` (details/remove) flows don't work yet but `arc alias x y` does.
`arc liberate` now uses the new help/argument stuff, although the help needs more beef eventually. I pruned a bunch of long-obsolete or questionable flags and renamed `--all` to `--clean` since `--all` sounds like "liberate all libraries", which is now the default behavior of `arc liberate`.
Test Plan:
You can now define chains of aliases. Finally!
```
$ arc draft4
WARNING Ignoring unrecognized configuration option ("hosts") from source: User Config File (/Users/epriestley/.arcrc).
WARNING Ignoring unrecognized configuration option ("load") from source: Project Config File (/Users/epriestley/dev/core/.arcconfig).
ALIAS arc draft4 -> arc draft3
ALIAS arc draft3 -> arc draft2
ALIAS arc draft2 -> arc diff
Usage Exception: Unrecognized argument '--draft'.
```
This also works now:
```
$ phage alias deploy-secure -- deploy --hosts secure001-4 --limit 1
```
General!
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T13098
Differential Revision: https://secure.phabricator.com/D19697
2018-09-14 22:45:26 +02:00
|
|
|
if ($effect->getCommand()) {
|
|
|
|
$command = $effect->getCommand();
|
|
|
|
$arguments = $effect->getArguments();
|
|
|
|
}
|
2018-09-18 19:12:14 +02:00
|
|
|
}
|
|
|
|
|
[Wilds] Continue toward a generalized "arc alias" workflow
Summary:
Ref T13098. This leaves a lot of rough edges but nothing is overtly broken so here's where we're at so far:
Config sources get "scopes", like user configuration vs system configuration. The major reason for this is so that `arc set-config x y` can know where it's supposed to write. This is generalized enough that we can implement `arc set-config --system ...` and `arc alias --local ...` and so on relatively easily later, although scopes themselves are not modular (a third-party can't add a new type of config scope). Maybe we'll modularize this some day but it felt like that's probably YAGNI/overboard since we have no current use cases. For now, a source does not need to belong to any particular scope.
Config may be writable (like user config in `~/.arcrc`) or nonwritable (like `--config` flags). Writable config can now specify how to write to disk. Config files can actually write to disk now, although the only pathway for doing this that exists is via `arc alias`.
Aliases now parse properly and can write to disk. `arc alias` now lets you define aliases, and writes them to disk. **The first time you do this, your `~/.arcrc` file will be rewritten into a format which old `arc` can not read!** It's relatively easily to unmangle/repair these files so I'm planning to just let this happen.
When a toolset is invoked, it now reads and evaluates aliases. Aliases have a lot of new guard rails like suggesting the user try `arc draft` if they type `phage draft`, allowing alias chains, detecting cycles, and limiting chain length.
Workflows can provide help and argument lists in a more structured way. I've moved this to sub-objects: help is now on `WorkflowInformation` (instead of a bunch of different `getHelp()`, `getSynopsis()` methods) and arguments now have a `WorkflowArgument` object instead of a dictionary. I think this pattern is generally better for extending: it lets us add and change stuff with less impact (and greater explicitness) down the road.
`arc alias` now has reasonable help text and argument documentation. The `arc alias` (list) and `arc alias x` (details/remove) flows don't work yet but `arc alias x y` does.
`arc liberate` now uses the new help/argument stuff, although the help needs more beef eventually. I pruned a bunch of long-obsolete or questionable flags and renamed `--all` to `--clean` since `--all` sounds like "liberate all libraries", which is now the default behavior of `arc liberate`.
Test Plan:
You can now define chains of aliases. Finally!
```
$ arc draft4
WARNING Ignoring unrecognized configuration option ("hosts") from source: User Config File (/Users/epriestley/.arcrc).
WARNING Ignoring unrecognized configuration option ("load") from source: Project Config File (/Users/epriestley/dev/core/.arcconfig).
ALIAS arc draft4 -> arc draft3
ALIAS arc draft3 -> arc draft2
ALIAS arc draft2 -> arc diff
Usage Exception: Unrecognized argument '--draft'.
```
This also works now:
```
$ phage alias deploy-secure -- deploy --hosts secure001-4 --limit 1
```
General!
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T13098
Differential Revision: https://secure.phabricator.com/D19697
2018-09-14 22:45:26 +02:00
|
|
|
if ($command !== null) {
|
|
|
|
$argv = array_merge(array($command), $arguments);
|
2018-09-18 19:12:14 +02:00
|
|
|
}
|
|
|
|
|
[Wilds] Continue toward a generalized "arc alias" workflow
Summary:
Ref T13098. This leaves a lot of rough edges but nothing is overtly broken so here's where we're at so far:
Config sources get "scopes", like user configuration vs system configuration. The major reason for this is so that `arc set-config x y` can know where it's supposed to write. This is generalized enough that we can implement `arc set-config --system ...` and `arc alias --local ...` and so on relatively easily later, although scopes themselves are not modular (a third-party can't add a new type of config scope). Maybe we'll modularize this some day but it felt like that's probably YAGNI/overboard since we have no current use cases. For now, a source does not need to belong to any particular scope.
Config may be writable (like user config in `~/.arcrc`) or nonwritable (like `--config` flags). Writable config can now specify how to write to disk. Config files can actually write to disk now, although the only pathway for doing this that exists is via `arc alias`.
Aliases now parse properly and can write to disk. `arc alias` now lets you define aliases, and writes them to disk. **The first time you do this, your `~/.arcrc` file will be rewritten into a format which old `arc` can not read!** It's relatively easily to unmangle/repair these files so I'm planning to just let this happen.
When a toolset is invoked, it now reads and evaluates aliases. Aliases have a lot of new guard rails like suggesting the user try `arc draft` if they type `phage draft`, allowing alias chains, detecting cycles, and limiting chain length.
Workflows can provide help and argument lists in a more structured way. I've moved this to sub-objects: help is now on `WorkflowInformation` (instead of a bunch of different `getHelp()`, `getSynopsis()` methods) and arguments now have a `WorkflowArgument` object instead of a dictionary. I think this pattern is generally better for extending: it lets us add and change stuff with less impact (and greater explicitness) down the road.
`arc alias` now has reasonable help text and argument documentation. The `arc alias` (list) and `arc alias x` (details/remove) flows don't work yet but `arc alias x y` does.
`arc liberate` now uses the new help/argument stuff, although the help needs more beef eventually. I pruned a bunch of long-obsolete or questionable flags and renamed `--all` to `--clean` since `--all` sounds like "liberate all libraries", which is now the default behavior of `arc liberate`.
Test Plan:
You can now define chains of aliases. Finally!
```
$ arc draft4
WARNING Ignoring unrecognized configuration option ("hosts") from source: User Config File (/Users/epriestley/.arcrc).
WARNING Ignoring unrecognized configuration option ("load") from source: Project Config File (/Users/epriestley/dev/core/.arcconfig).
ALIAS arc draft4 -> arc draft3
ALIAS arc draft3 -> arc draft2
ALIAS arc draft2 -> arc diff
Usage Exception: Unrecognized argument '--draft'.
```
This also works now:
```
$ phage alias deploy-secure -- deploy --hosts secure001-4 --limit 1
```
General!
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T13098
Differential Revision: https://secure.phabricator.com/D19697
2018-09-14 22:45:26 +02:00
|
|
|
return $argv;
|
2018-09-18 19:12:14 +02:00
|
|
|
}
|
|
|
|
|
2018-09-24 17:14:44 +02:00
|
|
|
private function installSignalHandler() {
|
|
|
|
$log = $this->getLogEngine();
|
|
|
|
|
|
|
|
if (!function_exists('pcntl_signal')) {
|
|
|
|
$log->writeTrace(
|
|
|
|
pht('PCNTL'),
|
|
|
|
pht(
|
|
|
|
'Unable to install signal handler, pcntl_signal() unavailable. '.
|
|
|
|
'Continuing without signal handling.'));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: SIGHUP, SIGTERM and SIGWINCH are handled by "PhutilSignalRouter".
|
|
|
|
// This logic is largely similar to the logic there, but more specific to
|
|
|
|
// Arcanist workflows.
|
|
|
|
|
|
|
|
pcntl_signal(SIGINT, array($this, 'routeSignal'));
|
|
|
|
}
|
|
|
|
|
|
|
|
public function routeSignal($signo) {
|
|
|
|
switch ($signo) {
|
|
|
|
case SIGINT:
|
|
|
|
$this->routeInterruptSignal($signo);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function routeInterruptSignal($signo) {
|
|
|
|
$log = $this->getLogEngine();
|
|
|
|
|
|
|
|
$last_interrupt = $this->lastInterruptTime;
|
|
|
|
$now = microtime(true);
|
|
|
|
$this->lastInterruptTime = $now;
|
|
|
|
|
|
|
|
$should_exit = false;
|
|
|
|
|
|
|
|
// If we received another SIGINT recently, always exit. This implements
|
|
|
|
// "press ^C twice in quick succession to exit" regardless of what the
|
|
|
|
// workflow may decide to do.
|
|
|
|
$interval = 2;
|
|
|
|
if ($last_interrupt !== null) {
|
|
|
|
if ($now - $last_interrupt < $interval) {
|
|
|
|
$should_exit = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$handler = null;
|
|
|
|
if (!$should_exit) {
|
|
|
|
|
|
|
|
// Look for an interrupt handler in the current workflow stack.
|
|
|
|
|
|
|
|
$stack = $this->getWorkflowStack();
|
|
|
|
foreach ($stack as $workflow) {
|
|
|
|
if ($workflow->canHandleSignal($signo)) {
|
|
|
|
$handler = $workflow;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If no workflow in the current execution stack can handle an interrupt
|
|
|
|
// signal, just exit on the first interrupt.
|
|
|
|
|
|
|
|
if (!$handler) {
|
|
|
|
$should_exit = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[Wilds] Provide a skeleton for prompt behaviors
Summary:
Ref T13098. Ref T13198. Ref T12996. The major ideas here are:
Workflows must define a list of all the prompts they can raise, so that these prompts can be enumerated with `arc prompts <workflow>`.
Prompts themselves should respond properly to ^C (abort immediately) and we should be able to make them nonblocking in the future (particularly, we'd like to be able to continue reading bytes from subprocess buffers while the prompt is shown on screen).
This doesn't have a lot of fancy features yet (non-confirm prompts, default yes, prompts which don't abort on "N", etc) but those should be easy to add later.
In the future, you'll be able to configure a default answer to prompts either in a config file or at runtime with `--config prompts=x.y.z=N` or similar.
This removes the history/readline mode for prompts, where you could use the up arrow to cycle through older responses. I believe this was only really useful for "excuse" prompts and intend to remove those.
Test Plan:
Forced `arc shell-complete` to always prompt, then:
- Got prompted, answered "y", "n", "N", "", "quack". Got sensible behavior in all cases.
- Ran `echo | arc shell-complete`, got a TTY error.
- Ran `arc prompts`, `arc prompts shell-complete`.
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T13098, T13198, T12996
Differential Revision: https://secure.phabricator.com/D19706
2018-09-25 18:07:16 +02:00
|
|
|
// It's common for users to ^C on prompts. Write a newline before writing
|
|
|
|
// a response to the interrupt so the behavior is a little cleaner. This
|
|
|
|
// also avoids lines that read "^C [ INTERRUPT ] ...".
|
|
|
|
$log->writeNewline();
|
|
|
|
|
2018-09-24 17:14:44 +02:00
|
|
|
if ($should_exit) {
|
|
|
|
$log->writeHint(
|
|
|
|
pht('INTERRUPT'),
|
|
|
|
pht('Interrupted by SIGINT (^C).'));
|
|
|
|
exit(128 + $signo);
|
|
|
|
}
|
|
|
|
|
|
|
|
$log->writeHint(
|
|
|
|
pht('INTERRUPT'),
|
|
|
|
pht('Press ^C again to exit.'));
|
|
|
|
|
|
|
|
$handler->handleSignal($signo);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function pushWorkflow(ArcanistWorkflow $workflow) {
|
|
|
|
$this->stack[] = $workflow;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function popWorkflow() {
|
|
|
|
if (!$this->stack) {
|
|
|
|
throw new Exception(pht('Trying to pop an empty workflow stack!'));
|
|
|
|
}
|
|
|
|
|
|
|
|
return array_pop($this->stack);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getWorkflowStack() {
|
|
|
|
return $this->stack;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-09-18 19:12:14 +02:00
|
|
|
}
|