-
Notifications
You must be signed in to change notification settings - Fork 66
Support baseline configuration #737
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
279d7f4
8734285
b29a141
024306b
f824a0b
f6bea18
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> | ||
| <xs:annotation> | ||
| <xs:appinfo source="https://github.com/Roave/BackwardCompatibilityCheck"/> | ||
|
|
||
| <xs:documentation source="https://github.com/Roave/BackwardCompatibilityCheck"> | ||
| This schema file defines the structure for the XML configuration file of roave/backward-compatibility-check. | ||
| </xs:documentation> | ||
| </xs:annotation> | ||
|
|
||
| <xs:element name="roave-bc-check" type="bcCheckType" /> | ||
|
|
||
| <xs:complexType name="bcCheckType"> | ||
| <xs:sequence> | ||
| <xs:element name="baseline" type="baselineType" minOccurs="0" /> | ||
| </xs:sequence> | ||
| </xs:complexType> | ||
|
|
||
| <xs:complexType name="baselineType"> | ||
| <xs:sequence> | ||
| <xs:element name="ignored-regex" minOccurs="0" maxOccurs="unbounded" type="ignore-pattern" /> | ||
| </xs:sequence> | ||
| </xs:complexType> | ||
|
|
||
| <xs:simpleType name="ignore-pattern"> | ||
| <xs:restriction base="xs:string"> | ||
| <xs:pattern value="#.+#" /> | ||
| </xs:restriction> | ||
| </xs:simpleType> | ||
| </xs:schema> |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Roave\BackwardCompatibility; | ||
|
|
||
| use function array_values; | ||
| use function preg_match; | ||
|
|
||
| /** @psalm-immutable */ | ||
| final class Baseline | ||
| { | ||
| /** @psalm-param list<non-empty-string> $ignoredChanges */ | ||
| private function __construct(private readonly array $ignoredChanges = []) | ||
| { | ||
| } | ||
|
|
||
| public static function empty(): self | ||
| { | ||
| return new self(); | ||
| } | ||
|
|
||
| /** @psalm-param list<non-empty-string> $ignoredChanges */ | ||
| public static function fromList(string ...$ignoredChanges): self | ||
| { | ||
| return new self(array_values($ignoredChanges)); | ||
| } | ||
|
|
||
| public function ignores(Change $change): bool | ||
| { | ||
| $changeDescription = $change->__toString(); | ||
|
|
||
| foreach ($this->ignoredChanges as $ignoredChangeRegex) { | ||
| if (preg_match($ignoredChangeRegex, $changeDescription) === 1) { | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Roave\BackwardCompatibility\Command; | ||
|
|
||
| use Psl\Str; | ||
| use Psl\Type; | ||
| use Roave\BackwardCompatibility\Configuration\Configuration; | ||
| use Roave\BackwardCompatibility\Configuration\ParseConfigurationFile; | ||
| use Roave\BackwardCompatibility\Configuration\ParseXmlConfigurationFile; | ||
| use Symfony\Component\Console\Output\OutputInterface; | ||
|
|
||
| /** @internal */ | ||
| final class DetermineConfigurationFromFilesystem | ||
lcobucci marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| { | ||
| public function __construct( | ||
| private readonly ParseConfigurationFile $parser = new ParseXmlConfigurationFile(), | ||
Ocramius marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ) { | ||
| } | ||
|
|
||
| public function __invoke( | ||
| string $currentDirectory, | ||
| OutputInterface $stdErr, | ||
| ): Configuration { | ||
| $configuration = $this->parser->parse($currentDirectory); | ||
|
|
||
| if ($configuration->filename !== null) { | ||
| $stdErr->writeln(Str\format( | ||
| 'Using "%s" as configuration file', | ||
| Type\string()->coerce($configuration->filename), | ||
| )); | ||
| } | ||
|
|
||
| return $configuration; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Roave\BackwardCompatibility\Configuration; | ||
|
|
||
| use Roave\BackwardCompatibility\Baseline; | ||
|
|
||
| /** @psalm-immutable */ | ||
lcobucci marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| final class Configuration | ||
| { | ||
| private function __construct( | ||
| public readonly Baseline $baseline, | ||
| public readonly string|null $filename, | ||
| ) { | ||
| } | ||
|
|
||
| public static function default(): self | ||
| { | ||
| return new self(Baseline::empty(), null); | ||
| } | ||
|
|
||
| public static function fromFile(Baseline $baseline, string $filename): self | ||
| { | ||
| return new self($baseline, $filename); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Roave\BackwardCompatibility\Configuration; | ||
|
|
||
| use LibXMLError; | ||
| use RuntimeException; | ||
|
|
||
| use function sprintf; | ||
| use function trim; | ||
|
|
||
| use const PHP_EOL; | ||
|
|
||
| /** @internal */ | ||
| final class InvalidConfigurationStructure extends RuntimeException | ||
lcobucci marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| { | ||
| /** @param list<LibXMLError> $errors */ | ||
| public static function fromLibxmlErrors(array $errors): self | ||
| { | ||
| $message = 'The provided configuration is invalid, errors:' . PHP_EOL; | ||
|
Check warning on line 21 in src/Configuration/InvalidConfigurationStructure.php
|
||
|
|
||
| foreach ($errors as $error) { | ||
| $message .= sprintf( | ||
| ' - [Line %d] %s' . PHP_EOL, | ||
|
Check warning on line 25 in src/Configuration/InvalidConfigurationStructure.php
|
||
| $error->line, | ||
| trim($error->message), | ||
|
Check warning on line 27 in src/Configuration/InvalidConfigurationStructure.php
|
||
| ); | ||
| } | ||
|
|
||
| return new self($message); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Roave\BackwardCompatibility\Configuration; | ||
|
|
||
| interface ParseConfigurationFile | ||
| { | ||
| /** @throws InvalidConfigurationStructure When an incorrect file was found on the directory. */ | ||
| public function parse(string $currentDirectory): Configuration; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Roave\BackwardCompatibility\Configuration; | ||
|
|
||
| use DOMDocument; | ||
| use Psl\File; | ||
| use Roave\BackwardCompatibility\Baseline; | ||
| use SimpleXMLElement; | ||
|
|
||
| use function assert; | ||
| use function libxml_get_errors; | ||
| use function libxml_use_internal_errors; | ||
|
|
||
| /** @internal */ | ||
| final class ParseXmlConfigurationFile implements ParseConfigurationFile | ||
lcobucci marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| { | ||
| private const CONFIGURATION_FILENAME = '.roave-backward-compatibility-check.xml'; | ||
|
|
||
| private const SCHEMA = __DIR__ . '/../../Resources/schema.xsd'; | ||
|
|
||
| public function parse(string $currentDirectory): Configuration | ||
| { | ||
| $filename = $currentDirectory . '/' . self::CONFIGURATION_FILENAME; | ||
|
|
||
| try { | ||
| $xmlContents = File\read($filename); | ||
|
|
||
| $this->validateStructure($xmlContents); | ||
| } catch (File\Exception\InvalidArgumentException) { | ||
| return Configuration::default(); | ||
| } | ||
|
|
||
| $configuration = new SimpleXMLElement($xmlContents); | ||
|
|
||
| return Configuration::fromFile( | ||
| $this->parseBaseline($configuration), | ||
| $filename, | ||
| ); | ||
| } | ||
|
|
||
| private function validateStructure(string $xmlContents): void | ||
| { | ||
| $previousConfiguration = libxml_use_internal_errors(true); | ||
|
|
||
| $xmlDocument = new DOMDocument(); | ||
| $xmlDocument->loadXML($xmlContents); | ||
|
|
||
| $configurationIsValid = $xmlDocument->schemaValidate(self::SCHEMA); | ||
|
|
||
| $parsingErrors = libxml_get_errors(); | ||
| libxml_use_internal_errors($previousConfiguration); | ||
|
Check warning on line 53 in src/Configuration/ParseXmlConfigurationFile.php
|
||
|
|
||
| if ($configurationIsValid) { | ||
| return; | ||
| } | ||
|
|
||
| throw InvalidConfigurationStructure::fromLibxmlErrors($parsingErrors); | ||
| } | ||
|
|
||
| private function parseBaseline(SimpleXMLElement $element): Baseline | ||
| { | ||
| $ignoredItems = []; | ||
|
|
||
| foreach ($element->xpath('baseline/ignored-regex') ?? [] as $node) { | ||
| $ignoredItem = (string) $node; | ||
|
|
||
| assert($ignoredItem !== ''); | ||
| $ignoredItems[] = $ignoredItem; | ||
| } | ||
|
|
||
| return Baseline::fromList(...$ignoredItems); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.