From 258193a83ee29a3ec01f86aeb8c2ce9d4e45ec97 Mon Sep 17 00:00:00 2001 From: Simon Schaufelberger Date: Sat, 27 Jun 2026 01:53:17 +0200 Subject: [PATCH] [FEATURE] Add --rules-summary CLI option --- e2e/rules-summary/cli-options.txt | 1 + e2e/rules-summary/expected-output.txt | 26 ++++++++++++++++ .../expected-result/fixture.typoscript | 1 + e2e/rules-summary/fixtures/fixture.typoscript | 2 ++ e2e/rules-summary/fractor.php | 10 +++++++ e2e/rules-summary/result/fixture.typoscript | 1 + e2e/run-test.sh | 2 +- .../Output/ConsoleOutputFormatter.php | 30 +++++++++++++++++++ .../Configuration/ConfigurationFactory.php | 3 ++ packages/fractor/src/Configuration/Option.php | 5 ++++ .../ValueObject/Configuration.php | 6 ++++ .../src/Console/Command/ProcessCommand.php | 7 +++++ .../fractor/src/ValueObject/ProcessResult.php | 22 ++++++++++++++ 13 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 e2e/rules-summary/cli-options.txt create mode 100644 e2e/rules-summary/expected-output.txt create mode 100644 e2e/rules-summary/expected-result/fixture.typoscript create mode 100644 e2e/rules-summary/fixtures/fixture.typoscript create mode 100644 e2e/rules-summary/fractor.php create mode 100644 e2e/rules-summary/result/fixture.typoscript diff --git a/e2e/rules-summary/cli-options.txt b/e2e/rules-summary/cli-options.txt new file mode 100644 index 00000000..bd1fd3cb --- /dev/null +++ b/e2e/rules-summary/cli-options.txt @@ -0,0 +1 @@ +--rules-summary diff --git a/e2e/rules-summary/expected-output.txt b/e2e/rules-summary/expected-output.txt new file mode 100644 index 00000000..f148a58c --- /dev/null +++ b/e2e/rules-summary/expected-output.txt @@ -0,0 +1,26 @@ + +1 file with changes +=================== + +1) rules-summary/result/fixture.typoscript:1 + + ---------- begin diff ---------- +@@ @@ +-config.xhtmlDoctype = 1 +-config.metaCharset = utf-8 ++config.doctype = 1 + ----------- end diff ----------- + +Applied rules: + * RenameConfigXhtmlDoctypeToDoctypeFractor + * RemoveConfigMetaCharsetFractor + + +Rules Summary +------------- + + * RenameConfigXhtmlDoctypeToDoctypeFractor was applied 1 time + * RemoveConfigMetaCharsetFractor was applied 1 time + + [OK] 1 file has been changed by Fractor + diff --git a/e2e/rules-summary/expected-result/fixture.typoscript b/e2e/rules-summary/expected-result/fixture.typoscript new file mode 100644 index 00000000..dfc3a281 --- /dev/null +++ b/e2e/rules-summary/expected-result/fixture.typoscript @@ -0,0 +1 @@ +config.doctype = 1 diff --git a/e2e/rules-summary/fixtures/fixture.typoscript b/e2e/rules-summary/fixtures/fixture.typoscript new file mode 100644 index 00000000..efd7b323 --- /dev/null +++ b/e2e/rules-summary/fixtures/fixture.typoscript @@ -0,0 +1,2 @@ +config.xhtmlDoctype = 1 +config.metaCharset = utf-8 diff --git a/e2e/rules-summary/fractor.php b/e2e/rules-summary/fractor.php new file mode 100644 index 00000000..882239ae --- /dev/null +++ b/e2e/rules-summary/fractor.php @@ -0,0 +1,10 @@ +withPaths([__DIR__ . '/result/']) + ->withSets([Typo3LevelSetList::UP_TO_TYPO3_14]); diff --git a/e2e/rules-summary/result/fixture.typoscript b/e2e/rules-summary/result/fixture.typoscript new file mode 100644 index 00000000..dfc3a281 --- /dev/null +++ b/e2e/rules-summary/result/fixture.typoscript @@ -0,0 +1 @@ +config.doctype = 1 diff --git a/e2e/run-test.sh b/e2e/run-test.sh index 1777d566..6ce62e6c 100755 --- a/e2e/run-test.sh +++ b/e2e/run-test.sh @@ -10,7 +10,7 @@ cd $TESTS_BASE_DIR rm -r composer.lock vendor || true composer install -for TEST_DIR in only-option typo3-extension typo3-typoscript typo3-xml typo3-yaml +for TEST_DIR in only-option rules-summary typo3-extension typo3-typoscript typo3-xml typo3-yaml do set +x echo diff --git a/packages/fractor/src/ChangesReporting/Output/ConsoleOutputFormatter.php b/packages/fractor/src/ChangesReporting/Output/ConsoleOutputFormatter.php index 97e96d18..4f7c500f 100644 --- a/packages/fractor/src/ChangesReporting/Output/ConsoleOutputFormatter.php +++ b/packages/fractor/src/ChangesReporting/Output/ConsoleOutputFormatter.php @@ -8,6 +8,7 @@ use a9f\Fractor\Configuration\ValueObject\Configuration; use a9f\Fractor\Differ\ValueObject\FileDiff; use a9f\Fractor\ValueObject\ProcessResult; +use Nette\Utils\Strings; use Symfony\Component\Console\Style\SymfonyStyle; final readonly class ConsoleOutputFormatter implements OutputFormatterInterface @@ -38,6 +39,10 @@ public function report(ProcessResult $processResult, Configuration $configuratio $this->symfonyStyle->newLine(); } + if ($configuration->shouldShowRulesSummary()) { + $this->reportRulesSummary($processResult, $configuration); + } + $message = $this->createSuccessMessage($processResult, $configuration); $this->symfonyStyle->success($message); } @@ -93,4 +98,29 @@ private function createSuccessMessage(ProcessResult $processResult, Configuratio : ($changeCount === 1 ? 'has' : 'have') . ' been changed' ); } + + private function reportRulesSummary(ProcessResult $processResult, Configuration $configuration): void + { + $ruleApplicationCounts = $processResult->getRuleApplicationCounts(); + if ($ruleApplicationCounts === []) { + return; + } + + $verb = $configuration->isDryRun() ? 'would have been applied' : 'was applied'; + + $this->symfonyStyle->section('Rules Summary'); + + foreach ($ruleApplicationCounts as $ruleClass => $count) { + $ruleShortClass = (string) Strings::after($ruleClass, '\\', -1); + $this->symfonyStyle->writeln(sprintf( + ' * %s %s %d time%s', + $ruleShortClass, + $verb, + $count, + $count > 1 ? 's' : '' + )); + } + + $this->symfonyStyle->newLine(); + } } diff --git a/packages/fractor/src/Configuration/ConfigurationFactory.php b/packages/fractor/src/Configuration/ConfigurationFactory.php index 67702217..3848c06d 100644 --- a/packages/fractor/src/Configuration/ConfigurationFactory.php +++ b/packages/fractor/src/Configuration/ConfigurationFactory.php @@ -42,6 +42,8 @@ public function createFromInput(InputInterface $input): Configuration $memoryLimit = $this->resolveMemoryLimit($input); + $showRulesSummary = (bool) $input->getOption(Option::RULES_SUMMARY); + return new Configuration( $dryRun, $showProgressBar, @@ -51,6 +53,7 @@ public function createFromInput(InputInterface $input): Configuration $showDiffs, $memoryLimit, $onlyRule, + $showRulesSummary, $showChangelog ); } diff --git a/packages/fractor/src/Configuration/Option.php b/packages/fractor/src/Configuration/Option.php index 11b4997a..d0732c5a 100644 --- a/packages/fractor/src/Configuration/Option.php +++ b/packages/fractor/src/Configuration/Option.php @@ -89,4 +89,9 @@ final class Option * @var string */ public const MEMORY_LIMIT = 'memory-limit'; + + /** + * @var string + */ + public const RULES_SUMMARY = 'rules-summary'; } diff --git a/packages/fractor/src/Configuration/ValueObject/Configuration.php b/packages/fractor/src/Configuration/ValueObject/Configuration.php index e7ce7153..36d55487 100644 --- a/packages/fractor/src/Configuration/ValueObject/Configuration.php +++ b/packages/fractor/src/Configuration/ValueObject/Configuration.php @@ -25,6 +25,7 @@ public function __construct( private bool $showDiffs = true, private string|null $memoryLimit = null, private ?string $onlyRule = null, + private bool $showRulesSummary = false, private bool $showChangelog = false, ) { Assert::allStringNotEmpty($this->paths, 'No directories given'); @@ -76,6 +77,11 @@ public function getOnlyRule(): ?string return $this->onlyRule; } + public function shouldShowRulesSummary(): bool + { + return $this->showRulesSummary; + } + public function shouldShowChangelog(): bool { return $this->showChangelog; diff --git a/packages/fractor/src/Console/Command/ProcessCommand.php b/packages/fractor/src/Console/Command/ProcessCommand.php index c869d404..58433caa 100644 --- a/packages/fractor/src/Console/Command/ProcessCommand.php +++ b/packages/fractor/src/Console/Command/ProcessCommand.php @@ -92,6 +92,13 @@ protected function configure(): void // filter by rule and path $this->addOption(Option::ONLY, null, InputOption::VALUE_REQUIRED, 'Fully qualified rule class name'); + + $this->addOption( + Option::RULES_SUMMARY, + null, + InputOption::VALUE_NONE, + 'Show summary of rules applied during the run.' + ); } protected function execute(InputInterface $input, OutputInterface $output): int diff --git a/packages/fractor/src/ValueObject/ProcessResult.php b/packages/fractor/src/ValueObject/ProcessResult.php index c40b3ae1..304f7e22 100644 --- a/packages/fractor/src/ValueObject/ProcessResult.php +++ b/packages/fractor/src/ValueObject/ProcessResult.php @@ -4,6 +4,7 @@ namespace a9f\Fractor\ValueObject; +use a9f\Fractor\Application\Contract\FractorRule; use a9f\Fractor\Differ\ValueObject\FileDiff; use Webmozart\Assert\Assert; @@ -34,4 +35,25 @@ public function getTotalChanged(): int { return $this->totalChanged; } + + /** + * @return array, int> + */ + public function getRuleApplicationCounts(): array + { + $ruleCounts = []; + + foreach ($this->fileDiffs as $fileDiff) { + foreach ($fileDiff->getFractorClasses() as $fractorClass) { + if (! isset($ruleCounts[$fractorClass])) { + $ruleCounts[$fractorClass] = 0; + } + + ++$ruleCounts[$fractorClass]; + } + } + + arsort($ruleCounts); + return $ruleCounts; + } }