diff --git a/CHANGELOG.md b/CHANGELOG.md index 704b58e..3fd595b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.6.0] - 2025-05-28 +### Added +- `-o`/`--owner-only` option for `codeowners owner` to print only the owner ([#26](https://github.com/timoschinkel/codeowners-cli/pull/26)) + ## [1.5.1] - 2025-01-30 ### Added - Support for Symfony 7 (next to Symfony 6) ([#24](https://github.com/timoschinkel/codeowners-cli/pull/24)) diff --git a/README.md b/README.md index fe24b93..229cb7a 100755 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ Arguments: Options: -c, --codeowners=CODEOWNERS Location of code owners file, defaults to /CODEOWNERS + -o, --owner-only Suppress normal output, only output the owner when applicable ``` For example: diff --git a/composer.json b/composer.json index de97401..ead8726 100755 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ }, "require-dev": { "squizlabs/php_codesniffer": "^3.5", - "vimeo/psalm": "^6.0", + "vimeo/psalm": "~6.4.0", "phpunit/phpunit": "^9.4", "mikey179/vfsstream": "^1.6", "phpspec/prophecy-phpunit": "^2.0" diff --git a/src/Command/OwnerCommand.php b/src/Command/OwnerCommand.php index ae6bd25..d5a57f6 100755 --- a/src/Command/OwnerCommand.php +++ b/src/Command/OwnerCommand.php @@ -7,15 +7,12 @@ use CodeOwners\Cli\FileLocator\FileLocatorFactoryInterface; use CodeOwners\Cli\PatternMatcherFactoryInterface; use CodeOwners\Exception\NoMatchFoundException; -use CodeOwners\Parser; use CodeOwners\Pattern; -use CodeOwners\PatternMatcher; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Exception\InvalidArgumentException; -use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputDefinition; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; final class OwnerCommand extends Command @@ -58,6 +55,12 @@ public function configure(): void InputArgument::OPTIONAL, 'Location of code owners file, defaults to /CODEOWNERS' ) + ->addOption( + 'owner-only', + 'o', + InputOption::VALUE_NONE, + 'Suppress normal output, only output the owner when applicable' + ) ; } @@ -84,19 +87,27 @@ protected function execute(InputInterface $input, OutputInterface $output): int foreach ($paths as $path) { if (file_exists($this->workingDirectory . '/' . $path) === false) { - $output->writeln("🚫 \"{$path}\" does not exist"); + if ($input->getOption('owner-only') !== true) { + $output->writeln("🚫 \"{$path}\" does not exist"); + } continue; } try { $pattern = $matcher->match($path); - $owners = $this->formatOwners($pattern); - $output->writeln( - "✅ \"{$path}\" is owned by {$owners} according to pattern \"{$pattern->getPattern()}\"" - ); + if ($input->getOption('owner-only') === true) { + $output->writeln(join(PHP_EOL, $pattern->getOwners())); + } else { + $owners = $this->formatOwners($pattern); + $output->writeln( + "✅ \"{$path}\" is owned by {$owners} according to pattern \"{$pattern->getPattern()}\"" + ); + } } catch (NoMatchFoundException $exception) { - $output->writeln("🚫 \"{$path}\" has no code owner"); + if ($input->getOption('owner-only') !== true) { + $output->writeln("🚫 \"{$path}\" has no code owner"); + } } } diff --git a/tests/Command/OwnerCommandTest.php b/tests/Command/OwnerCommandTest.php index 70b3e7e..d68e708 100644 --- a/tests/Command/OwnerCommandTest.php +++ b/tests/Command/OwnerCommandTest.php @@ -90,6 +90,52 @@ public function testCommandDisplaysOwnersFoundForPaths(): void self::assertMatchesRegularExpression('/"file-non-existent".+does not exist/m', $output); } + public function testCommandDisplaysOwnersOnlyFoundForPaths(): void + { + $filesystem = vfsStream::setup('root', 444, [ + 'CODEOWNERS' => '#', + 'file-a' => '#', + 'file-b' => '#', + ]); + + $fileLocator = $this->prophesize(FileLocatorInterface::class); + $fileLocator->locateFile() + ->shouldBeCalled() + ->willReturn($filesystem->url() . '/CODEOWNERS'); + + $this->fileLocatorFactory + ->getFileLocator(Argument::type('string'), null) + ->shouldBeCalled() + ->willReturn($fileLocator->reveal()); + + $this->patternMatcher + ->match('file-a') + ->shouldBeCalled() + ->willReturn(new Pattern('file-a', ['@owner-a', '@owner-b'])); + $this->patternMatcher + ->match('file-b') + ->shouldBeCalled() + ->willThrow(NoMatchFoundException::class); + + $this->patternMatcherFactory + ->getPatternMatcher($filesystem->url() . '/CODEOWNERS') + ->shouldBeCalled() + ->willReturn($this->patternMatcher->reveal()); + + $command = new OwnerCommand( + $filesystem->url(), + $this->fileLocatorFactory->reveal(), + $this->patternMatcherFactory->reveal() + ); + + $output = $this->executeCommand( + $command, + ['paths' => ['file-a', 'file-b', 'file-non-existent'], '--owner-only' => true] + ); + + self::assertEquals('@owner-a' . PHP_EOL . '@owner-b' . PHP_EOL, $output); + } + public function testCommandPassesCodeownerFileLocation(): void { $filesystem = vfsStream::setup('root', 444, []);