Skip to content

Commit 0680f22

Browse files
committed
Parse and apply baseline from configuration
This integrates the changes filter into the backward compatibility check command, parsing the configuration file prior to any process to ease the integration of new properties. Signed-off-by: Luís Cobucci <[email protected]>
1 parent 6fca151 commit 0680f22

File tree

5 files changed

+192
-3
lines changed

5 files changed

+192
-3
lines changed

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,4 +121,19 @@ vendor/bin/roave-backward-compatibility-check --help
121121

122122
## Configuration
123123

124-
There are currently no configuration options available.
124+
The file `.roave-backward-compatibility-check.json` is read from the current working directory (when it exists) and sets configuration for the command.
125+
126+
It's expected to be a JSON encoded file that, optionally, contains the following properties:
127+
128+
* `baseline`: list of regexes used to filter detected changes; useful to avoid detection of known BC-breaks
129+
130+
**Example:**
131+
132+
```json
133+
{
134+
"baseline": [
135+
"#\\[BC\\] CHANGED: The parameter \\$a of TestArtifact\\\\TheClass\\#method()#",
136+
"#\\[BC\\] CHANGED: The parameter \\$b of TestArtifact\\\\TheClass\\#method2()#"
137+
]
138+
}
139+
```

src/Command/AssertBackwardsCompatible.php

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Psl;
88
use Psl\Env;
9+
use Psl\File;
910
use Psl\Iter;
1011
use Psl\Str;
1112
use Psl\Type;
@@ -34,6 +35,8 @@
3435

3536
final class AssertBackwardsCompatible extends Command
3637
{
38+
private const CONFIGURATION_FILENAME = '.roave-backward-compatibility-check.json';
39+
3740
/** @throws LogicException */
3841
public function __construct(
3942
private PerformCheckoutOfRevision $git,
@@ -113,7 +116,9 @@ public function execute(InputInterface $input, OutputInterface $output): int
113116
$stdErr = $output->getErrorOutput();
114117

115118
// @todo fix flaky assumption about the path of the source repo...
116-
$sourceRepo = CheckedOutRepository::fromPath(Env\current_dir());
119+
$currentDirectory = Env\current_dir();
120+
121+
$sourceRepo = CheckedOutRepository::fromPath($currentDirectory);
117122

118123
$fromRevision = $input->getOption('from') !== null
119124
? $this->parseRevisionFromInput($input, $sourceRepo)
@@ -125,6 +130,8 @@ public function execute(InputInterface $input, OutputInterface $output): int
125130

126131
$toRevision = $this->parseRevision->fromStringForRepository($to, $sourceRepo);
127132

133+
$configuration = $this->determineConfiguration($currentDirectory, $stdErr);
134+
128135
$stdErr->writeln(Str\format(
129136
'Comparing from %s to %s...',
130137
Type\string()->coerce($fromRevision),
@@ -148,7 +155,7 @@ public function execute(InputInterface $input, OutputInterface $output): int
148155
$toPath->__toString(),
149156
($this->locateDependencies)($toPath->__toString(), $includeDevelopmentDependencies),
150157
),
151-
);
158+
)->applyBaseline($configuration->baseline);
152159

153160
$formatters = [
154161
'console' => new SymfonyConsoleTextFormatter($stdErr),
@@ -213,4 +220,24 @@ private function determineFromRevisionFromRepository(
213220
$repository,
214221
);
215222
}
223+
224+
private function determineConfiguration(
225+
string $currentDirectory,
226+
OutputInterface $stdErr,
227+
): Configuration {
228+
$fileName = $currentDirectory . '/' . self::CONFIGURATION_FILENAME;
229+
230+
try {
231+
$configContents = File\read($fileName);
232+
} catch (File\Exception\InvalidArgumentException) {
233+
return Configuration::default();
234+
}
235+
236+
$stdErr->writeln(Str\format(
237+
'Using "%s" as configuration file',
238+
Type\string()->coerce($fileName),
239+
));
240+
241+
return Configuration::fromJson($configContents);
242+
}
216243
}

src/Command/Configuration.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Roave\BackwardCompatibility\Command;
6+
7+
use Psl\Json;
8+
use Psl\Type;
9+
use Roave\BackwardCompatibility\Baseline;
10+
use RuntimeException;
11+
12+
final class Configuration
13+
{
14+
private function __construct(public readonly Baseline $baseline)
15+
{
16+
}
17+
18+
public static function default(): self
19+
{
20+
return new self(Baseline::empty());
21+
}
22+
23+
public static function fromJson(string $jsonContents): self
24+
{
25+
try {
26+
$configuration = Json\typed(
27+
$jsonContents,
28+
Type\shape(
29+
['baseline' => Type\optional(Type\vec(Type\string()))],
30+
),
31+
);
32+
} catch (Json\Exception\DecodeException $exception) {
33+
throw new RuntimeException(
34+
'It was not possible to parse the configuration',
35+
previous: $exception,
36+
);
37+
}
38+
39+
$baseline = $configuration['baseline'] ?? [];
40+
41+
return new self(Baseline::fromList(...$baseline));
42+
}
43+
}

test/e2e/Command/AssertBackwardsCompatibleTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ final class AssertBackwardsCompatibleTest extends TestCase
2828
]
2929
}
3030

31+
JSON;
32+
33+
private const BASELINE_CONFIGURATION = <<<'JSON'
34+
{
35+
"baseline": ["#\\[BC\\] CHANGED: The parameter \\$a of TestArtifact\\\\TheClass\\#method()#"]
36+
}
37+
3138
JSON;
3239

3340
private const CLASS_VERSIONS = [
@@ -264,6 +271,21 @@ public function testWillPickLatestTaggedVersionOnNoGivenFrom(): void
264271
}
265272
}
266273

274+
public function testWillAllowSpecifyingBaselineConfiguration(): void
275+
{
276+
File\write($this->sourcesRepository . '/.roave-backward-compatibility-check.json', self::BASELINE_CONFIGURATION);
277+
278+
$output = Shell\execute(__DIR__ . '/../../../bin/roave-backward-compatibility-check', [
279+
'--from=' . $this->versions[0],
280+
'--to=' . $this->versions[1],
281+
], $this->sourcesRepository, [], Shell\ErrorOutputBehavior::Append);
282+
283+
self::assertStringContainsString(
284+
'.roave-backward-compatibility-check.json" as configuration file',
285+
$output,
286+
);
287+
}
288+
267289
private function tagOnVersion(string $tagName, int $version): void
268290
{
269291
Shell\execute('git', ['checkout', $this->versions[$version]], $this->sourcesRepository);
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace RoaveTest\BackwardCompatibility\Command;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Roave\BackwardCompatibility\Baseline;
9+
use Roave\BackwardCompatibility\Command\Configuration;
10+
use RuntimeException;
11+
12+
/** @covers \Roave\BackwardCompatibility\Command\Configuration */
13+
final class ConfigurationTest extends TestCase
14+
{
15+
public function testBaselineShouldBeEmptyForDefaultConfiguration(): void
16+
{
17+
$config = Configuration::default();
18+
19+
self::assertEquals(Baseline::empty(), $config->baseline);
20+
}
21+
22+
/** @dataProvider validConfigurations */
23+
public function testBaselineShouldBeReadFromJsonContents(
24+
string $jsonContents,
25+
Baseline $expectedBaseline,
26+
): void {
27+
$config = Configuration::fromJson($jsonContents);
28+
29+
self::assertEquals($expectedBaseline, $config->baseline);
30+
}
31+
32+
/** @psalm-return iterable<string, array{string, Baseline}> */
33+
public function validConfigurations(): iterable
34+
{
35+
yield 'empty object' => ['{}', Baseline::empty()];
36+
yield 'empty array' => ['[]', Baseline::empty()];
37+
yield 'empty baseline property' => ['{"baseline":[]}', Baseline::empty()];
38+
39+
yield 'baseline with strings' => [
40+
<<<'JSON'
41+
{"baseline": ["#\\[BC\\] CHANGED: The parameter \\$a#"]}
42+
JSON,
43+
Baseline::fromList('#\[BC\] CHANGED: The parameter \$a#'),
44+
];
45+
46+
yield 'random properties are ignored' => [
47+
<<<'JSON'
48+
{
49+
"baseline": ["#\\[BC\\] CHANGED: The parameter \\$a#"],
50+
"random": false
51+
}
52+
JSON,
53+
Baseline::fromList('#\[BC\] CHANGED: The parameter \$a#'),
54+
];
55+
}
56+
57+
/** @dataProvider invalidConfigurations */
58+
public function testExceptionShouldBeTriggeredOnInvalidConfiguration(
59+
string $jsonContents,
60+
): void {
61+
$this->expectException(RuntimeException::class);
62+
63+
Configuration::fromJson($jsonContents);
64+
}
65+
66+
/** @psalm-return iterable<string, array{string}> */
67+
public function invalidConfigurations(): iterable
68+
{
69+
yield 'empty content' => [''];
70+
yield 'empty string' => ['""'];
71+
yield 'int' => ['0'];
72+
yield 'float' => ['0.1'];
73+
yield 'boolean' => ['false'];
74+
yield 'baseline with string' => ['{"baseline": "this should be a list"}'];
75+
yield 'baseline with int' => ['{"baseline": 0}'];
76+
yield 'baseline with float' => ['{"baseline": 0.0}'];
77+
yield 'baseline with bool' => ['{"baseline": true}'];
78+
yield 'baseline with array of float' => ['{"baseline": [0.0]}'];
79+
yield 'baseline with array of bool' => ['{"baseline": [false]}'];
80+
yield 'baseline with array of object' => ['{"baseline": [{}]}'];
81+
}
82+
}

0 commit comments

Comments
 (0)