From 687ed3774502d9f690b88c0d0186e1887a19ca0e Mon Sep 17 00:00:00 2001 From: Timo Schinkel Date: Wed, 29 Jan 2025 11:52:17 +0100 Subject: [PATCH] Introduce `--strict` flag to `list-unowned-files` command Introduce `--strict` flag to `list-unowned-files` command, which will make this command very usable in a CI environment. solves #22 --- CHANGELOG.md | 4 ++ README.md | 1 + src/Command/ListUnownedFilesCommand.php | 13 +++- tests/Command/ListUnownedFilesCommandTest.php | 61 +++++++++++++++++-- 4 files changed, 74 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb9616f..c892a8a 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.5.0] - 2025-01-29 +### Added +- Optional option `--strict` for `list-unowned-files` to make the command usable in CI ([#23](https://github.com/timoschinkel/codeowners-cli/pull/23)) + ## [1.4.1] - 2025-01-29 ### Removed - Dropped support for PHP 8.0 diff --git a/README.md b/README.md index 86e0f4c..fe24b93 100755 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ Arguments: Options: -c, --codeowners=CODEOWNERS Location of code owners file, defaults to /CODEOWNERS + --strict Return a non-zero exit code when there are unowned files ``` For example: diff --git a/src/Command/ListUnownedFilesCommand.php b/src/Command/ListUnownedFilesCommand.php index 36b25ba..df839f1 100644 --- a/src/Command/ListUnownedFilesCommand.php +++ b/src/Command/ListUnownedFilesCommand.php @@ -12,6 +12,7 @@ use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Finder\Finder; @@ -54,6 +55,12 @@ public function configure(): void 'c', InputArgument::OPTIONAL, 'Location of code owners file, defaults to /CODEOWNERS' + ) + ->addOption( + 'strict', + null, + InputOption::VALUE_NONE, + 'Return a non-zero exit code when there are unowned files' ); } @@ -83,6 +90,7 @@ public function execute(InputInterface $input, OutputInterface $output): int $finder = new Finder(); + $unownedFilesFound = false; foreach ($finder->in($paths)->files() as $file) { /** @var SplFileInfo $file */ try { @@ -93,11 +101,14 @@ public function execute(InputInterface $input, OutputInterface $output): int $matcher->match($filePath); } catch (NoMatchFoundException $exception) { + $unownedFilesFound = true; $output->writeln((string)$file); } } - return 0; + return $input->getOption('strict') && $unownedFilesFound + ? 1 + : 0; } private function normalizePaths(array $paths): array diff --git a/tests/Command/ListUnownedFilesCommandTest.php b/tests/Command/ListUnownedFilesCommandTest.php index 88f6989..4b06d9e 100644 --- a/tests/Command/ListUnownedFilesCommandTest.php +++ b/tests/Command/ListUnownedFilesCommandTest.php @@ -81,12 +81,65 @@ public function testCommandListFilesOwnedBySpecifiedOwner(): void $this->patternMatcherFactory->reveal() ); - $output = $this->executeCommand($command, [ + ['exit' => $exit, 'output' => $output] = $this->executeCommand($command, [ 'paths' => [ $filesystem->url() . '/folder', ] ]); + self::assertEquals(0, $exit); + self::assertEquals( + $filesystem->url() . '/folder/not-owned-by-owner' . PHP_EOL, + $output + ); + } + + public function testCommandListFilesOwnedBySpecifiedOwnerInStrictMode(): void + { + $filesystem = vfsStream::setup('root', 444, [ + 'CODEOWNERS' => '#', + 'folder' => [ + 'owned-by-owner' => '#', + 'not-owned-by-owner' => '#', + ], + ]); + + $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('folder/owned-by-owner') + ->willReturn(new Pattern('*', ['@owner'])); + + $this->patternMatcher + ->match('folder/not-owned-by-owner') + ->willThrow(NoMatchFoundException::class); + + $this->patternMatcherFactory + ->getPatternMatcher($filesystem->url() . '/CODEOWNERS') + ->willReturn($this->patternMatcher->reveal()); + + $command = new ListUnownedFilesCommand( + $filesystem->url(), + $this->fileLocatorFactory->reveal(), + $this->patternMatcherFactory->reveal() + ); + + ['exit' => $exit, 'output' => $output] = $this->executeCommand($command, [ + 'paths' => [ + $filesystem->url() . '/folder', + ], + '--strict' => true + ]); + + self::assertNotEquals(0, $exit); self::assertEquals( $filesystem->url() . '/folder/not-owned-by-owner' . PHP_EOL, $output @@ -146,14 +199,14 @@ public function testCommandPassesSpecifiedCodeownersFileToFileLocator(): void ]); } - private function executeCommand(Command $command, array $parameters): string + private function executeCommand(Command $command, array $parameters): array { $application = new Application(); $application->add($command); $tester = new CommandTester($application->find($command->getName())); - $tester->execute($parameters); + $exit = $tester->execute($parameters); - return $tester->getDisplay(); + return ['exit' => $exit, 'output' => $tester->getDisplay()]; } }