mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-12-22 21:40:54 +01:00
Add a mode to "ExecFuture" that makes "resolvex()" semantics the default
Summary: Ref T13490. "ExecFuture" can raise a "CommandException" when the subprocess fails. In most cases, this is desirable, since we usually do not expect subprocesses to fail. Currently, "execx()" (which raises these exceptions) is the synchronous default (with the more verbose "exec_manual()" as an alternative if you expect the command may return an error code), but the Future variation has no way to hint that external resolution should raise an exception if the process exits with an error. Since the "HardpointEngine" yield construct can resolve external futures, add a mode for this to simplify implementing "HardpointQuery" classes a bit. Without this, they either need to retain "$futures" and manually call "resolvex()", or check the "$err" result and throw some other exception, both of which are low-value boilerplate (queries that want to do this still can, of course). This is basically like providing a hint that the futures should be resolved with "resolvex()" instead of "resolve()". Also, make this the default behavior of a new "$api->newFuture()" wrapper. Test Plan: Intentionally broke a command in a HardpointQuery, got a sensible exception automatically during external future resolution. Maniphest Tasks: T13490 Differential Revision: https://secure.phabricator.com/D21089
This commit is contained in:
parent
73f48aca74
commit
92be6df0eb
3 changed files with 59 additions and 28 deletions
|
@ -309,31 +309,10 @@ final class ExecFuture extends PhutilExecutableFuture {
|
|||
* @task resolve
|
||||
*/
|
||||
public function resolvex() {
|
||||
list($err, $stdout, $stderr) = $this->resolve();
|
||||
if ($err) {
|
||||
$cmd = $this->getCommand();
|
||||
|
||||
if ($this->getWasKilledByTimeout()) {
|
||||
// NOTE: The timeout can be a float and PhutilNumber only handles
|
||||
// integers, so just use "%s" to render it.
|
||||
$message = pht(
|
||||
'Command killed by timeout after running for more than %s seconds.',
|
||||
$this->terminateTimeout);
|
||||
} else {
|
||||
$message = pht('Command failed with error #%d!', $err);
|
||||
}
|
||||
|
||||
throw new CommandException(
|
||||
$message,
|
||||
$cmd,
|
||||
$err,
|
||||
$stdout,
|
||||
$stderr);
|
||||
}
|
||||
return array($stdout, $stderr);
|
||||
$result = $this->resolve();
|
||||
return $this->raiseResultError($result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolve a command you expect to return valid JSON. Works like
|
||||
* @{method:resolvex}, but also throws if stderr is nonempty, or stdout is not
|
||||
|
@ -388,20 +367,56 @@ final class ExecFuture extends PhutilExecutableFuture {
|
|||
proc_terminate($this->proc, $signal);
|
||||
}
|
||||
|
||||
$this->closeProcess();
|
||||
|
||||
$result = array(
|
||||
128 + $signal,
|
||||
$this->stdout,
|
||||
$this->stderr,
|
||||
);
|
||||
|
||||
$this->setResult($result);
|
||||
|
||||
$this->closeProcess();
|
||||
$this->recordResult($result);
|
||||
}
|
||||
|
||||
return $this->getResult();
|
||||
}
|
||||
|
||||
private function recordResult(array $result) {
|
||||
$resolve_on_error = $this->getResolveOnError();
|
||||
if (!$resolve_on_error) {
|
||||
$result = $this->raiseResultError($result);
|
||||
}
|
||||
|
||||
$this->setResult($result);
|
||||
}
|
||||
|
||||
private function raiseResultError($result) {
|
||||
list($err, $stdout, $stderr) = $result;
|
||||
|
||||
if ($err) {
|
||||
$cmd = $this->getCommand();
|
||||
|
||||
if ($this->getWasKilledByTimeout()) {
|
||||
// NOTE: The timeout can be a float and PhutilNumber only handles
|
||||
// integers, so just use "%s" to render it.
|
||||
$message = pht(
|
||||
'Command killed by timeout after running for more than %s seconds.',
|
||||
$this->terminateTimeout);
|
||||
} else {
|
||||
$message = pht('Command failed with error #%d!', $err);
|
||||
}
|
||||
|
||||
throw new CommandException(
|
||||
$message,
|
||||
$cmd,
|
||||
$err,
|
||||
$stdout,
|
||||
$stderr);
|
||||
}
|
||||
|
||||
return array($stdout, $stderr);
|
||||
}
|
||||
|
||||
|
||||
/* -( Internals )---------------------------------------------------------- */
|
||||
|
||||
|
@ -633,7 +648,7 @@ final class ExecFuture extends PhutilExecutableFuture {
|
|||
$err),
|
||||
);
|
||||
|
||||
$this->setResult($result);
|
||||
$this->recordResult($result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -777,7 +792,7 @@ final class ExecFuture extends PhutilExecutableFuture {
|
|||
$signal_info.$this->stderr,
|
||||
);
|
||||
|
||||
$this->setResult($result);
|
||||
$this->recordResult($result);
|
||||
|
||||
$this->closeProcess();
|
||||
return true;
|
||||
|
|
|
@ -8,6 +8,7 @@ abstract class PhutilExecutableFuture extends Future {
|
|||
private $command;
|
||||
private $env;
|
||||
private $cwd;
|
||||
private $resolveOnError = true;
|
||||
|
||||
final public function __construct($pattern /* , ... */) {
|
||||
$args = func_get_args();
|
||||
|
@ -34,6 +35,15 @@ abstract class PhutilExecutableFuture extends Future {
|
|||
return;
|
||||
}
|
||||
|
||||
final public function setResolveOnError($resolve_on_error) {
|
||||
$this->resolveOnError = $resolve_on_error;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getResolveOnError() {
|
||||
return $this->resolveOnError;
|
||||
}
|
||||
|
||||
final public function getCommand() {
|
||||
return $this->command;
|
||||
}
|
||||
|
|
|
@ -673,4 +673,10 @@ abstract class ArcanistRepositoryAPI extends Phobject {
|
|||
return new ArcanistBranchRef();
|
||||
}
|
||||
|
||||
final public function newFuture($pattern /* , ... */) {
|
||||
$args = func_get_args();
|
||||
return $this->buildLocalFuture($args)
|
||||
->setResolveOnError(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue