Skip to content

Commit 3afc875

Browse files
authored
Merge pull request #737 from lcobucci/support-baseline-configuration
Support baseline configuration
2 parents eed5a57 + f6bea18 commit 3afc875

17 files changed

+581
-5
lines changed

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,20 @@ vendor/bin/roave-backward-compatibility-check --help
122122

123123
## Configuration
124124

125-
There are currently no configuration options available.
125+
The file `.roave-backward-compatibility-check.xml` is read from the current working directory (when it exists) and sets configuration for the command.
126+
127+
It's expected to be an XML file that follows our [schema](resources/schema.xsd):
128+
129+
**Example:**
130+
131+
```xml
132+
<?xml version="1.0" encoding="UTF-8" ?>
133+
<roave-bc-check
134+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
135+
xsi:noNamespaceSchemaLocation="vendor/roave/backward-compatibility-check/resources/schema.xsd">
136+
<baseline>
137+
<ignored-regex>#\[BC\] CHANGED: The parameter \$a of of TestArtifact\\TheClass\#method\(\)#</ignored-regex>
138+
<ignored-regex>#\[BC\] CHANGED: The parameter \$b of of TestArtifact\\TheClass\#method2\(\)#</ignored-regex>
139+
</baseline>
140+
</roave-bc-check>
141+
```

Resources/schema.xsd

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3+
<xs:annotation>
4+
<xs:appinfo source="https://github.com/Roave/BackwardCompatibilityCheck"/>
5+
6+
<xs:documentation source="https://github.com/Roave/BackwardCompatibilityCheck">
7+
This schema file defines the structure for the XML configuration file of roave/backward-compatibility-check.
8+
</xs:documentation>
9+
</xs:annotation>
10+
11+
<xs:element name="roave-bc-check" type="bcCheckType" />
12+
13+
<xs:complexType name="bcCheckType">
14+
<xs:sequence>
15+
<xs:element name="baseline" type="baselineType" minOccurs="0" />
16+
</xs:sequence>
17+
</xs:complexType>
18+
19+
<xs:complexType name="baselineType">
20+
<xs:sequence>
21+
<xs:element name="ignored-regex" minOccurs="0" maxOccurs="unbounded" type="ignore-pattern" />
22+
</xs:sequence>
23+
</xs:complexType>
24+
25+
<xs:simpleType name="ignore-pattern">
26+
<xs:restriction base="xs:string">
27+
<xs:pattern value="#.+#" />
28+
</xs:restriction>
29+
</xs:simpleType>
30+
</xs:schema>

box.json.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"main": "bin/roave-backward-compatibility-check.php",
1111
"output": "dist/roave-backward-compatibility-check.phar",
1212
"files-bin": [
13+
"Resources/schema.xsd",
1314
"LICENSE",
1415
"vendor/composer/composer/LICENSE"
1516
],

composer.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
"description": "Tool to compare two revisions of a public API to check for BC breaks",
44
"require": {
55
"php": "~8.2.0 || ~8.3.0",
6+
"ext-dom": "*",
7+
"ext-json": "*",
8+
"ext-libxml": "*",
9+
"ext-simplexml": "*",
610
"azjezz/psl": "^3.0.2",
711
"composer/composer": "^2.7.6",
812
"nikic/php-parser": "^4.19.1",

composer.lock

Lines changed: 6 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Baseline.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Roave\BackwardCompatibility;
6+
7+
use function array_values;
8+
use function preg_match;
9+
10+
/** @psalm-immutable */
11+
final class Baseline
12+
{
13+
/** @psalm-param list<non-empty-string> $ignoredChanges */
14+
private function __construct(private readonly array $ignoredChanges = [])
15+
{
16+
}
17+
18+
public static function empty(): self
19+
{
20+
return new self();
21+
}
22+
23+
/** @psalm-param list<non-empty-string> $ignoredChanges */
24+
public static function fromList(string ...$ignoredChanges): self
25+
{
26+
return new self(array_values($ignoredChanges));
27+
}
28+
29+
public function ignores(Change $change): bool
30+
{
31+
$changeDescription = $change->__toString();
32+
33+
foreach ($this->ignoredChanges as $ignoredChangeRegex) {
34+
if (preg_match($ignoredChangeRegex, $changeDescription) === 1) {
35+
return true;
36+
}
37+
}
38+
39+
return false;
40+
}
41+
}

src/Changes.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,23 @@ public function mergeWith(self $other): self
6767
return $instance;
6868
}
6969

70+
public function applyBaseline(Baseline $baseline): self
71+
{
72+
$instance = new self([]);
73+
74+
$instance->unBufferedChanges = (function () use ($baseline): Generator {
75+
foreach ($this as $change) {
76+
if ($baseline->ignores($change)) {
77+
continue;
78+
}
79+
80+
yield $change;
81+
}
82+
})();
83+
84+
return $instance;
85+
}
86+
7087
/**
7188
* {@inheritDoc}
7289
*

src/Command/AssertBackwardsCompatible.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,9 @@ public function execute(InputInterface $input, OutputInterface $output): int
114114
$stdErr = $output->getErrorOutput();
115115

116116
// @todo fix flaky assumption about the path of the source repo...
117-
$sourceRepo = CheckedOutRepository::fromPath(Env\current_dir());
117+
$currentDirectory = Env\current_dir();
118+
119+
$sourceRepo = CheckedOutRepository::fromPath($currentDirectory);
118120

119121
$fromRevision = $input->getOption('from') !== null
120122
? $this->parseRevisionFromInput($input, $sourceRepo)
@@ -126,6 +128,8 @@ public function execute(InputInterface $input, OutputInterface $output): int
126128

127129
$toRevision = $this->parseRevision->fromStringForRepository($to, $sourceRepo);
128130

131+
$configuration = (new DetermineConfigurationFromFilesystem())($currentDirectory, $stdErr);
132+
129133
$stdErr->writeln(Str\format(
130134
'Comparing from %s to %s...',
131135
Type\string()->coerce($fromRevision),
@@ -149,7 +153,7 @@ public function execute(InputInterface $input, OutputInterface $output): int
149153
$toPath->__toString(),
150154
($this->locateDependencies)($toPath->__toString(), $includeDevelopmentDependencies),
151155
),
152-
);
156+
)->applyBaseline($configuration->baseline);
153157

154158
$formatters = [
155159
'console' => new SymfonyConsoleTextFormatter($stdErr),
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Roave\BackwardCompatibility\Command;
6+
7+
use Psl\Str;
8+
use Psl\Type;
9+
use Roave\BackwardCompatibility\Configuration\Configuration;
10+
use Roave\BackwardCompatibility\Configuration\ParseConfigurationFile;
11+
use Roave\BackwardCompatibility\Configuration\ParseXmlConfigurationFile;
12+
use Symfony\Component\Console\Output\OutputInterface;
13+
14+
/** @internal */
15+
final class DetermineConfigurationFromFilesystem
16+
{
17+
public function __construct(
18+
private readonly ParseConfigurationFile $parser = new ParseXmlConfigurationFile(),
19+
) {
20+
}
21+
22+
public function __invoke(
23+
string $currentDirectory,
24+
OutputInterface $stdErr,
25+
): Configuration {
26+
$configuration = $this->parser->parse($currentDirectory);
27+
28+
if ($configuration->filename !== null) {
29+
$stdErr->writeln(Str\format(
30+
'Using "%s" as configuration file',
31+
Type\string()->coerce($configuration->filename),
32+
));
33+
}
34+
35+
return $configuration;
36+
}
37+
}

src/Configuration/Configuration.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Roave\BackwardCompatibility\Configuration;
6+
7+
use Roave\BackwardCompatibility\Baseline;
8+
9+
/** @psalm-immutable */
10+
final class Configuration
11+
{
12+
private function __construct(
13+
public readonly Baseline $baseline,
14+
public readonly string|null $filename,
15+
) {
16+
}
17+
18+
public static function default(): self
19+
{
20+
return new self(Baseline::empty(), null);
21+
}
22+
23+
public static function fromFile(Baseline $baseline, string $filename): self
24+
{
25+
return new self($baseline, $filename);
26+
}
27+
}

0 commit comments

Comments
 (0)