diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 8b1e721..8a95150 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -246,6 +246,12 @@ parameters: count: 1 path: src/Gitonomy/Git/Repository.php + - + message: '#^Method Gitonomy\\Git\\Repository\:\:runProcess\(\) has parameter \$args with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Gitonomy/Git/Repository.php + - message: '#^Method Gitonomy\\Git\\Repository\:\:shell\(\) has parameter \$env with no value type specified in iterable type array\.$#' identifier: missingType.iterableValue diff --git a/src/Gitonomy/Git/Repository.php b/src/Gitonomy/Git/Repository.php index 3e49988..ac294e6 100644 --- a/src/Gitonomy/Git/Repository.php +++ b/src/Gitonomy/Git/Repository.php @@ -470,6 +470,11 @@ public function setDescription(string $description): static * This command is a facility command. You can run any command * directly on git repository. * + * Note that this only returns the standard output of the process. Some + * git commands (`push`, for instance) write their meaningful output to + * stderr even when they succeed: use {@see runProcess()} instead if you + * need access to it. + * * @param string $command Git command to run (checkout, branch, tag) * @param array $args Arguments of git command * @@ -478,6 +483,26 @@ public function setDescription(string $description): static * @throws RuntimeException Error while executing git command (debug-mode only) */ public function run(string $command, array $args = []): ?string + { + $process = $this->runProcess($command, $args); + + return $process->isSuccessful() ? $process->getOutput() : null; + } + + /** + * Same as {@see run()}, but returns the full, already-run process + * instead of only its standard output on success. + * + * This is useful for commands like `push`, which write their + * meaningful output to stderr even when they succeed, so it can't be + * read through run(). + * + * @param string $command Git command to run (checkout, branch, tag) + * @param array $args Arguments of git command + * + * @throws RuntimeException Error while executing git command (debug-mode only) + */ + public function runProcess(string $command, array $args = []): Process { $process = $this->getProcess($command, $args); @@ -488,13 +513,11 @@ public function run(string $command, array $args = []): ?string $process->run(); - $output = $process->getOutput(); - if ($this->logger && $this->debug) { $duration = microtime(true) - $before; $this->logger->debug(\sprintf('last command (%s) duration: %sms', $command, \sprintf('%.2f', $duration * 1000))); $this->logger->debug(\sprintf('last command (%s) return code: %s', $command, $process->getExitCode())); - $this->logger->debug(\sprintf('last command (%s) output: %s', $command, $output)); + $this->logger->debug(\sprintf('last command (%s) output: %s', $command, $process->getOutput())); } if (!$process->isSuccessful()) { @@ -507,11 +530,9 @@ public function run(string $command, array $args = []): ?string if ($this->debug) { throw new ProcessException($process); } - - return null; } - return $output; + return $process; } /** diff --git a/tests/Gitonomy/Git/Tests/RepositoryTest.php b/tests/Gitonomy/Git/Tests/RepositoryTest.php index e7bc480..e92b2b0 100644 --- a/tests/Gitonomy/Git/Tests/RepositoryTest.php +++ b/tests/Gitonomy/Git/Tests/RepositoryTest.php @@ -30,6 +30,27 @@ public function testRunReturnsNullInsteadOfThrowingWhenDebugIsFalse(): void $this->assertNull($repository->run('not-a-command')); } + public function testRunProcessGivesAccessToStderrOnSuccess(): void + { + $repository = self::createFoobarRepository(false); + + // `git checkout` reports the switched branch on stderr, even on success. + $process = $repository->runProcess('checkout', ['master']); + + $this->assertTrue($process->isSuccessful()); + $this->assertStringContainsString('master', $process->getErrorOutput()); + } + + public function testRunProcessReturnsFailedProcessInsteadOfThrowingWhenDebugIsFalse(): void + { + $repository = self::createFoobarRepository(true); + $repository = new Repository($repository->getPath(), array_merge(self::getOptions(), ['debug' => false])); + + $process = $repository->runProcess('not-a-command'); + + $this->assertFalse($process->isSuccessful()); + } + public function testGetShortHashThrowsCleanExceptionWhenDebugIsFalse(): void { $repository = self::createFoobarRepository(true);