diff --git a/README.md b/README.md index d95ae27..79dff5d 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ The ``ec`` binary supports the following options: | ``--uncovered`` | ``-u`` | Lists all files which are not covered by .editorconfig. | | ``--verbose`` | ``-v`` | Shows additional informations, like detailed info about internal time tracking and which binary files have been skipped. | | ``--no-interaction`` | ``-n`` | Do not ask for confirmation, if more than 500 files found and continue scanning. | -| ``--no-error-on-exit`` | | By default ``ec`` returns code 2 when issues occurred. With this option set return code is always 0. | +| ``--no-error-on-exit`` | | By default ``ec`` returns code 2 when issues or code 1 when warnings occurred. With this option set return code is always 0. | **Tip:** The "usage" section on ``ec``'s help page shows some examples. diff --git a/src/Application.php b/src/Application.php index cc6ce83..609bb88 100644 --- a/src/Application.php +++ b/src/Application.php @@ -193,6 +193,22 @@ protected function executing(Input $input, Output $output): int ? $this->scan($finder, $count, $io, (bool)$input->getOption('strict'), (bool)$input->getOption('no-progress'), (bool)$input->getOption('compact'), (bool)$input->getOption('uncovered')) : $this->fix($finder, $io, (bool)$input->getOption('strict')); + if (!empty($this->scanner->getUnavailableFiles())) { + $amountUnavailableFiles = count($this->scanner->getUnavailableFiles()); + $io->warning('Found ' . $amountUnavailableFiles . ' unavailable ' . StringFormatUtility::pluralizeFiles($amountUnavailableFiles) . ' not being scanned!'); + $filePaths = []; + foreach ($this->scanner->getUnavailableFiles() as $unavailableFile) { + $filePaths[] = $unavailableFile->getPathname(); + } + $io->listing($filePaths); + if ($gitOnlyEnabled) { + $io->writeln('The files listed by the "' . $gitOnlyCommand . '" command are not physically present.'); + $io->writeln('This typically occurs when files are deleted without being staged in Git. To verify, check "git status".'); + $io->newLine(); + } + $returnValue = 1; + } + if ($this->isVerbose) { if (!empty($this->scanner->getSkippedBinaryFiles())) { $amountBinaryFiles = count($this->scanner->getSkippedBinaryFiles()); diff --git a/src/EditorConfig/Rules/FileUnavailableException.php b/src/EditorConfig/Rules/FileUnavailableException.php new file mode 100644 index 0000000..d1808a7 --- /dev/null +++ b/src/EditorConfig/Rules/FileUnavailableException.php @@ -0,0 +1,24 @@ +unavailableFile; + } + + public function setUnavailableFile(SplFileInfo $unavailableFile): self + { + $this->unavailableFile = $unavailableFile; + + return $this; + } +} diff --git a/src/EditorConfig/Rules/Validator.php b/src/EditorConfig/Rules/Validator.php index 42d3ba1..a84830d 100644 --- a/src/EditorConfig/Rules/Validator.php +++ b/src/EditorConfig/Rules/Validator.php @@ -34,6 +34,11 @@ public function createValidatedFileResult(SplFileInfo $file, array $editorConfig $this->skippingRules = $skippingRules; $filePath = (string)$file->getRealPath(); + + if (empty($filePath)) { + throw (new FileUnavailableException())->setUnavailableFile($file); + } + $rules = []; if (!MimeTypeUtility::isCommonTextType($filePath) && (MimeTypeUtility::isCommonBinaryType($filePath) || MimeTypeUtility::isBinaryFileType($filePath))) { diff --git a/src/EditorConfig/Scanner.php b/src/EditorConfig/Scanner.php index 327bfce..741b3ab 100644 --- a/src/EditorConfig/Scanner.php +++ b/src/EditorConfig/Scanner.php @@ -5,11 +5,13 @@ namespace Armin\EditorconfigCli\EditorConfig; use Armin\EditorconfigCli\EditorConfig\Rules\FileResult; +use Armin\EditorconfigCli\EditorConfig\Rules\FileUnavailableException; use Armin\EditorconfigCli\EditorConfig\Rules\Validator; use Armin\EditorconfigCli\EditorConfig\Utility\MimeTypeUtility; use Armin\EditorconfigCli\EditorConfig\Utility\TimeTrackingUtility; use Idiosyncratic\EditorConfig\EditorConfig; use Symfony\Component\Finder\Finder; +use Symfony\Component\Finder\SplFileInfo; class Scanner { @@ -38,6 +40,11 @@ class Scanner */ private $skippedBinaryFiles = []; + /** + * @var array|SplFileInfo[] + */ + private $unavailableFiles = []; + public function __construct(?EditorConfig $editorConfig = null, ?Validator $validator = null, string $rootPath = null, array $skippingRules = []) { $this->editorConfig = $editorConfig ?? new EditorConfig(); @@ -71,6 +78,14 @@ public function getSkippedBinaryFiles(): array return $this->skippedBinaryFiles; } + /** + * @return array|SplFileInfo[] + */ + public function getUnavailableFiles(): array + { + return $this->unavailableFiles; + } + /** * @param bool $strict when true, any difference of indention size is spotted * @@ -82,7 +97,13 @@ public function scan(Finder $finderInstance, bool $strict = false, callable $tic foreach ($finderInstance as $file) { $config = $this->editorConfig->getConfigForPath((string)$file->getRealPath()); - $fileResult = $this->validator->createValidatedFileResult($file, $config, $strict, $this->skippingRules); + try { + $fileResult = $this->validator->createValidatedFileResult($file, $config, $strict, $this->skippingRules); + } catch (FileUnavailableException $e) { + $this->unavailableFiles[] = $e->getUnavailableFile(); + continue; + } + $filePath = $fileResult->getFilePath(); if (!empty($this->rootPath)) { $filePath = substr($filePath, strlen($this->rootPath)); diff --git a/tests/Functional/EditorConfig/CommandGitUnavailableFilesTest.php b/tests/Functional/EditorConfig/CommandGitUnavailableFilesTest.php new file mode 100644 index 0000000..7f412f0 --- /dev/null +++ b/tests/Functional/EditorConfig/CommandGitUnavailableFilesTest.php @@ -0,0 +1,58 @@ +workspacePath = sys_get_temp_dir() . '/current_editorconfig_cli_test'; + + parent::setUp(); + + // Copy test files + copy(__DIR__ . '/../../Fixtures/image.jpg', $this->workspacePath . '/' . 'image.jpg'); + copy(__DIR__ . '/../../Fixtures/document.pdf', $this->workspacePath . '/' . 'document.pdf'); + copy(__DIR__ . '/../../Fixtures/kreis-weiß.svg', $this->workspacePath . '/' . 'kreis-weiß.svg'); + + // Set up Git repository for testing + exec('cd ' . $this->workspacePath . ' && ' . self::GIT_BINARY . ' init', $result, $returnCode); + if ($returnCode !== 0) { + throw new \RuntimeException('Unable to create test git repository!'); + } + + // Add files to git stage + exec('cd ' . $this->workspacePath . ' && ' . self::GIT_BINARY . ' add image.jpg'); + exec('cd ' . $this->workspacePath . ' && ' . self::GIT_BINARY . ' add document.pdf'); + exec('cd ' . $this->workspacePath . ' && ' . self::GIT_BINARY . ' add kreis-weiß.svg'); + + // Remove files physically + exec('rm -f ' . $this->workspacePath . '/image.jpg'); + exec('rm -f ' . $this->workspacePath . '/document.pdf'); + } + + public function testUnavailableFiles() + { + $command = new Application(); + $command->setAutoExit(false); + $commandTester = new CommandTester($command); + $commandTester->execute(['-d' => $this->workspacePath, '--no-progress' => true, '--git-only' => true]); + + self::assertSame(1, $commandTester->getStatusCode()); + self::assertStringContainsString('[WARNING] Found 2 unavailable files not being scanned!', $commandTester->getDisplay()); + self::assertStringContainsString('* ' . $this->workspacePath . '/document.pdf', $commandTester->getDisplay()); + self::assertStringContainsString('* ' . $this->workspacePath . '/image.jpg', $commandTester->getDisplay()); + } +}