From f1a250dbcccd645a575177dea79a13ff91455e9f Mon Sep 17 00:00:00 2001 From: Bartlomiej Sacharski Date: Sat, 26 Sep 2020 21:42:23 +0200 Subject: [PATCH] [issue-2170] Modified runLocally function to accept closure As noted in the issue #2170, the logic in the recipe/check_remote.php would try to get the binary path of local and remote git executable. However, due to specific implementation, the get('bin/git') would have been called twice in the context of remote host, instead of invoking it once on remote and once on local machine. The runLocally function has thus been reworked, to accept Closure, which will be invoked in the context of the localhost machine. Once a Closure has been passed, the function will push the Localhost to the Context, invoke the closure, store the stdout (if available), pop the Localhost from the Context and finally return the stdout. For compatibility reasons, the function also accepts the string argument, however it is wrapped in a closure and once again passed to the runLocally function. The recipe/check_remote.php file has been reworked, to utilize the Closure, to make sure that the get('bin/git') is being called in the Localhost context. --- recipe/deploy/check_remote.php | 5 ++++- src/functions.php | 40 +++++++++++++++++++++++++--------- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/recipe/deploy/check_remote.php b/recipe/deploy/check_remote.php index 820e8865f..3528a1c95 100644 --- a/recipe/deploy/check_remote.php +++ b/recipe/deploy/check_remote.php @@ -39,7 +39,10 @@ $opt = '--heads'; } - $remoteLs = runLocally(sprintf("%s ls-remote $opt $repository $ref", get('bin/git'))); + $remoteLs = runLocally(function () use ($opt, $repository, $ref) { + $cmd = sprintf("%s ls-remote $opt $repository $ref", get('bin/git')); + return run($cmd); + }); if (strstr($remoteLs, "\n")) { throw new Exception("Could not determine target revision. '$ref' matched multiple commits."); } diff --git a/src/functions.php b/src/functions.php index de994a4ee..2cb014c21 100644 --- a/src/functions.php +++ b/src/functions.php @@ -7,6 +7,7 @@ namespace Deployer; +use Closure; use Deployer\Exception\Exception; use Deployer\Exception\GracefulShutdownException; use Deployer\Exception\RunException; @@ -339,24 +340,43 @@ function run($command, $options = []) /** * Execute commands on local machine * - * @param string $command Command to run locally. + * @param string|Closure $command Command to run locally, or a closure that will be executed in local machine context * @param array $options * @return string Output of command. */ function runLocally($command, $options = []) { - $process = Deployer::get()->processRunner; - $command = parse($command); + if ($command instanceof Closure) { + $input = Context::has() ? input() : null; + $output = Context::has() ? output() : null; - $env = array_merge_alternate(get('env', []), $options['env'] ?? []); - if (!empty($env)) { - $env = array_to_string($env); - $command = "export $env; $command"; - } + $localhostContext = new Context(new Localhost(), $input, $output); + Context::push($localhostContext); + try { + $output = $command(); + if (!is_string($output)) { + $output = ''; + } + } finally { + Context::pop(); + } + + $output = rtrim($output); + return $output; + } else { + $command = parse($command); + $env = array_merge_alternate(get('env', []), $options['env'] ?? []); - $output = $process->run(new Localhost(), $command, $options); + return runLocally(function () use ($command, $options, $env) { + if (!empty($env)) { + $env = array_to_string($env); + $command = "export $env; $command"; + } - return rtrim($output); + $output = run($command, $options); + return $output; + }); + } } /**