diff --git a/src/future/exec/ExecFuture.php b/src/future/exec/ExecFuture.php index e56376dd..48911930 100644 --- a/src/future/exec/ExecFuture.php +++ b/src/future/exec/ExecFuture.php @@ -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; diff --git a/src/future/exec/PhutilExecutableFuture.php b/src/future/exec/PhutilExecutableFuture.php index 3038032c..663ce567 100644 --- a/src/future/exec/PhutilExecutableFuture.php +++ b/src/future/exec/PhutilExecutableFuture.php @@ -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; } diff --git a/src/repository/api/ArcanistRepositoryAPI.php b/src/repository/api/ArcanistRepositoryAPI.php index e1b47507..5ac9be43 100644 --- a/src/repository/api/ArcanistRepositoryAPI.php +++ b/src/repository/api/ArcanistRepositoryAPI.php @@ -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); + } + }