Skip to content

Commit 682d85b

Browse files
samsonasikpeterfoxactions-user
authored
[ChangesReporting][Alternative] Collect changed_files on --no-diffs with json output with use of JsonOutputFactory (#7821)
* Fixes showing files changed in JSON formatter * test change * Revert "test change" This reverts commit e9b5a0f. * normalized line ending * fix phpstan * [ChangesReporting][Alternative] Use JsonOutputFactory to make easy test for cross OS usage * final touch: eol * [ci-review] Rector Rectify * add e2e test for no-diffs format json * Fix with check format on worker as well * final touch: eol for output json file result --------- Co-authored-by: Peter Fox <peter.fox@peterfox.me> Co-authored-by: GitHub Action <actions@github.com>
1 parent cdd219c commit 682d85b

File tree

12 files changed

+214
-68
lines changed

12 files changed

+214
-68
lines changed

.github/workflows/e2e_with_no_diffs.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ jobs:
2525
php_version: ['8.3']
2626
directory:
2727
- 'e2e/applied-rule-removed-node-no-diffs'
28+
- 'e2e/applied-no-diffs-format-json'
2829

2930
name: End to end test - ${{ matrix.directory }}
3031

@@ -47,3 +48,9 @@ jobs:
4748
# run e2e test
4849
- run: php ../e2eTestRunner.php --no-diffs
4950
working-directory: ${{ matrix.directory }}
51+
if: ${{ matrix.directory == 'e2e/applied-rule-removed-node-no-diffs' }}
52+
53+
# run e2e test
54+
- run: php ../e2eTestRunner.php --no-diffs --output-format=json
55+
working-directory: ${{ matrix.directory }}
56+
if: ${{ matrix.directory == 'e2e/applied-no-diffs-format-json' }}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/vendor
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"require": {
3+
"php": "^8.1"
4+
},
5+
"minimum-stability": "dev",
6+
"prefer-stable": true
7+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"totals": {
3+
"changed_files": 1,
4+
"errors": 0
5+
},
6+
"changed_files": [
7+
"src/RemoveAlwaysElse.php"
8+
]
9+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\Config\RectorConfig;
6+
use Rector\EarlyReturn\Rector\If_\RemoveAlwaysElseRector;
7+
8+
return static function (RectorConfig $rectorConfig): void {
9+
$rectorConfig->paths([
10+
__DIR__ . '/src',
11+
]);
12+
13+
$rectorConfig->rules([
14+
RemoveAlwaysElseRector::class,
15+
]);
16+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
class RemoveAlwaysElse
4+
{
5+
public function run($value)
6+
{
7+
if ($value) {
8+
throw new \InvalidStateException;
9+
} else {
10+
return 10;
11+
}
12+
}
13+
}

e2e/e2eTestRunner.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
$e2eCommand .= ' --no-diffs';
3737
}
3838

39+
if (isset($argv[2]) && $argv[2] === '--output-format=json') {
40+
$e2eCommand .= ' --output-format=json';
41+
}
42+
3943
$cliOptions = 'cli-options.txt';
4044
if (file_exists($cliOptions)) {
4145
$e2eCommand .= ' ' . trim((string) file_get_contents($cliOptions));
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\ChangesReporting\Output\Factory;
6+
7+
use Nette\Utils\Json;
8+
use Rector\Parallel\ValueObject\Bridge;
9+
use Rector\ValueObject\Configuration;
10+
use Rector\ValueObject\Error\SystemError;
11+
use Rector\ValueObject\ProcessResult;
12+
13+
/**
14+
* @see \Rector\Tests\ChangesReporting\Output\Factory\JsonOutputFactoryTest
15+
*/
16+
final class JsonOutputFactory
17+
{
18+
public static function create(ProcessResult $processResult, Configuration $configuration): string
19+
{
20+
$errorsJson = [
21+
'totals' => [
22+
'changed_files' => $processResult->getTotalChanged(),
23+
],
24+
];
25+
26+
// We need onlyWithChanges: false to include all file diffs
27+
$fileDiffs = $processResult->getFileDiffs(onlyWithChanges: false);
28+
ksort($fileDiffs);
29+
foreach ($fileDiffs as $fileDiff) {
30+
$filePath = $configuration->isReportingWithRealPath()
31+
? ($fileDiff->getAbsoluteFilePath() ?? '')
32+
: $fileDiff->getRelativeFilePath()
33+
;
34+
35+
if ($configuration->shouldShowDiffs() && $fileDiff->getDiff() !== '') {
36+
$errorsJson[Bridge::FILE_DIFFS][] = [
37+
'file' => $filePath,
38+
'diff' => $fileDiff->getDiff(),
39+
'applied_rectors' => $fileDiff->getRectorClasses(),
40+
];
41+
}
42+
43+
// for Rector CI
44+
$errorsJson['changed_files'][] = $filePath;
45+
}
46+
47+
$systemErrors = $processResult->getSystemErrors();
48+
$errorsJson['totals']['errors'] = count($systemErrors);
49+
50+
$errorsData = self::createErrorsData($systemErrors, $configuration->isReportingWithRealPath());
51+
if ($errorsData !== []) {
52+
$errorsJson['errors'] = $errorsData;
53+
}
54+
55+
return Json::encode($errorsJson, pretty: true);
56+
}
57+
58+
/**
59+
* @param SystemError[] $errors
60+
* @return mixed[]
61+
*/
62+
private static function createErrorsData(array $errors, bool $absoluteFilePath): array
63+
{
64+
$errorsData = [];
65+
66+
foreach ($errors as $error) {
67+
$errorDataJson = [
68+
'message' => $error->getMessage(),
69+
'file' => $absoluteFilePath ? $error->getAbsoluteFilePath() : $error->getRelativeFilePath(),
70+
];
71+
72+
if ($error->getRectorClass() !== null) {
73+
$errorDataJson['caused_by'] = $error->getRectorClass();
74+
}
75+
76+
if ($error->getLine() !== null) {
77+
$errorDataJson['line'] = $error->getLine();
78+
}
79+
80+
$errorsData[] = $errorDataJson;
81+
}
82+
83+
return $errorsData;
84+
}
85+
}

src/ChangesReporting/Output/JsonOutputFormatter.php

Lines changed: 2 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@
44

55
namespace Rector\ChangesReporting\Output;
66

7-
use Nette\Utils\Json;
87
use Rector\ChangesReporting\Contract\Output\OutputFormatterInterface;
9-
use Rector\Parallel\ValueObject\Bridge;
8+
use Rector\ChangesReporting\Output\Factory\JsonOutputFactory;
109
use Rector\ValueObject\Configuration;
11-
use Rector\ValueObject\Error\SystemError;
1210
use Rector\ValueObject\ProcessResult;
1311

1412
final readonly class JsonOutputFormatter implements OutputFormatterInterface
@@ -22,67 +20,6 @@ public function getName(): string
2220

2321
public function report(ProcessResult $processResult, Configuration $configuration): void
2422
{
25-
$errorsJson = [
26-
'totals' => [
27-
'changed_files' => $processResult->getTotalChanged(),
28-
],
29-
];
30-
31-
$fileDiffs = $processResult->getFileDiffs();
32-
ksort($fileDiffs);
33-
foreach ($fileDiffs as $fileDiff) {
34-
$filePath = $configuration->isReportingWithRealPath()
35-
? ($fileDiff->getAbsoluteFilePath() ?? '')
36-
: $fileDiff->getRelativeFilePath()
37-
;
38-
39-
$errorsJson[Bridge::FILE_DIFFS][] = [
40-
'file' => $filePath,
41-
'diff' => $fileDiff->getDiff(),
42-
'applied_rectors' => $fileDiff->getRectorClasses(),
43-
];
44-
45-
// for Rector CI
46-
$errorsJson['changed_files'][] = $filePath;
47-
}
48-
49-
$systemErrors = $processResult->getSystemErrors();
50-
$errorsJson['totals']['errors'] = count($systemErrors);
51-
52-
$errorsData = $this->createErrorsData($systemErrors, $configuration->isReportingWithRealPath());
53-
if ($errorsData !== []) {
54-
$errorsJson['errors'] = $errorsData;
55-
}
56-
57-
$json = Json::encode($errorsJson, pretty: true);
58-
echo $json . PHP_EOL;
59-
}
60-
61-
/**
62-
* @param SystemError[] $errors
63-
* @return mixed[]
64-
*/
65-
private function createErrorsData(array $errors, bool $absoluteFilePath): array
66-
{
67-
$errorsData = [];
68-
69-
foreach ($errors as $error) {
70-
$errorDataJson = [
71-
'message' => $error->getMessage(),
72-
'file' => $absoluteFilePath ? $error->getAbsoluteFilePath() : $error->getRelativeFilePath(),
73-
];
74-
75-
if ($error->getRectorClass() !== null) {
76-
$errorDataJson['caused_by'] = $error->getRectorClass();
77-
}
78-
79-
if ($error->getLine() !== null) {
80-
$errorDataJson['line'] = $error->getLine();
81-
}
82-
83-
$errorsData[] = $errorDataJson;
84-
}
85-
86-
return $errorsData;
23+
echo JsonOutputFactory::create($processResult, $configuration) . PHP_EOL;
8724
}
8825
}

src/Console/Command/WorkerCommand.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Rector\Autoloading\AdditionalAutoloader;
1414
use Rector\Configuration\ConfigurationFactory;
1515
use Rector\Configuration\ConfigurationRuleFilter;
16+
use Rector\Configuration\Option;
1617
use Rector\Console\ProcessConfigureDecorator;
1718
use Rector\Parallel\ValueObject\Bridge;
1819
use Rector\StaticReflection\DynamicSourceLocatorDecorator;
@@ -75,6 +76,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
7576
$promise->then(function (ConnectionInterface $connection) use (
7677
$parallelIdentifier,
7778
$configuration,
79+
$input,
7880
$output
7981
): void {
8082
$inDecoder = new Decoder($connection, true, 512, JSON_INVALID_UTF8_IGNORE);
@@ -85,7 +87,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
8587
ReactCommand::IDENTIFIER => $parallelIdentifier,
8688
]);
8789

88-
$this->runWorker($outEncoder, $inDecoder, $configuration, $output);
90+
$this->runWorker($outEncoder, $inDecoder, $configuration, $input, $output);
8991
});
9092

9193
$streamSelectLoop->run();
@@ -97,6 +99,7 @@ private function runWorker(
9799
Encoder $encoder,
98100
Decoder $decoder,
99101
Configuration $configuration,
102+
InputInterface $input,
100103
OutputInterface $output
101104
): void {
102105
$this->additionalAutoloader->autoloadPaths();
@@ -128,7 +131,7 @@ private function runWorker(
128131
$encoder->on(ReactEvent::ERROR, $handleErrorCallback);
129132

130133
// 2. collect diffs + errors from file processor
131-
$decoder->on(ReactEvent::DATA, function (array $json) use ($preFileCallback, $encoder, $configuration): void {
134+
$decoder->on(ReactEvent::DATA, function (array $json) use ($preFileCallback, $encoder, $configuration, $input): void {
132135
$action = $json[ReactCommand::ACTION];
133136
if ($action !== Action::MAIN) {
134137
return;
@@ -151,7 +154,7 @@ private function runWorker(
151154
$encoder->write([
152155
ReactCommand::ACTION => Action::RESULT,
153156
self::RESULT => [
154-
Bridge::FILE_DIFFS => $processResult->getFileDiffs(),
157+
Bridge::FILE_DIFFS => $processResult->getFileDiffs($input->getOption(Option::OUTPUT_FORMAT) !== 'json'),
155158
Bridge::FILES_COUNT => count($filePaths),
156159
Bridge::SYSTEM_ERRORS => $processResult->getSystemErrors(),
157160
Bridge::SYSTEM_ERRORS_COUNT => count($processResult->getSystemErrors()),

0 commit comments

Comments
 (0)