From b20973ec20cda0f8e16f5e3e83e4aa1ff9f7a36a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=A4u=C3=9Fler?= Date: Tue, 21 Mar 2023 11:05:20 +0100 Subject: [PATCH 1/9] [FEATURE] Let ProviderFactory manage template providers --- config/services.php | 4 -- config/services.yaml | 4 -- src/Bootstrap.php | 6 ++- src/Console/Application.php | 26 +--------- .../UnknownTemplateProviderException.php | 43 +++++++++++++++++ src/Template/Provider/ComposerProvider.php | 5 -- src/Template/Provider/PackagistProvider.php | 5 -- src/Template/Provider/ProviderFactory.php | 33 +++++++++++-- src/Template/Provider/ProviderInterface.php | 2 - src/Template/Provider/VcsProvider.php | 5 -- tests/src/Console/ApplicationTest.php | 24 ---------- .../UnknownTemplateProviderExceptionTest.php | 47 +++++++++++++++++++ tests/src/Fixtures/DummyComposerProvider.php | 5 -- tests/src/Fixtures/DummyProvider.php | 5 -- .../Template/Provider/ProviderFactoryTest.php | 23 +++++++-- 15 files changed, 144 insertions(+), 93 deletions(-) create mode 100644 src/Exception/UnknownTemplateProviderException.php create mode 100644 tests/src/Exception/UnknownTemplateProviderExceptionTest.php diff --git a/config/services.php b/config/services.php index d1faa39a..fbe7ba13 100644 --- a/config/services.php +++ b/config/services.php @@ -26,7 +26,6 @@ use Cocur\Slugify; use CPSIT\ProjectBuilder\Builder; use CPSIT\ProjectBuilder\IO; -use CPSIT\ProjectBuilder\Template; use CPSIT\ProjectBuilder\Twig; use GuzzleHttp\Client as GuzzleClient; use Nyholm\Psr7; @@ -55,9 +54,6 @@ $container->registerForAutoconfiguration(IO\Validator\ValidatorInterface::class) ->addTag('io.validator') ; - $container->registerForAutoconfiguration(Template\Provider\ProviderInterface::class) - ->addTag('template.provider') - ; $container->registerForAutoconfiguration(Twig\Filter\TwigFilterInterface::class) ->addTag('twig.filter') ; diff --git a/config/services.yaml b/config/services.yaml index 8d50e999..9378b1b5 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -44,10 +44,6 @@ services: CPSIT\ProjectBuilder\IO\Messenger: alias: 'app.messenger' - CPSIT\ProjectBuilder\Template\Provider\ProviderFactory: - arguments: - $providers: !tagged_iterator template.provider - CPSIT\ProjectBuilder\Twig\Extension\ProjectBuilderExtension: arguments: $filters: !tagged_iterator twig.filter diff --git a/src/Bootstrap.php b/src/Bootstrap.php index dd060507..3adaad49 100644 --- a/src/Bootstrap.php +++ b/src/Bootstrap.php @@ -94,12 +94,16 @@ public static function simulateCreateProject(Script\Event $event): never private static function createApplication(IO\Messenger $messenger, string $targetDirectory): Console\Application { + $filesystem = new Filesystem\Filesystem(); + $providerFactory = Template\Provider\ProviderFactory::create($messenger, $filesystem); + return new Console\Application( $messenger, Builder\Config\ConfigReader::create(), new Error\ErrorHandler($messenger), - new Filesystem\Filesystem(), + $filesystem, $targetDirectory, + $providerFactory->getAll(), ); } diff --git a/src/Console/Application.php b/src/Console/Application.php index 5290391f..d29e1b2c 100644 --- a/src/Console/Application.php +++ b/src/Console/Application.php @@ -51,12 +51,7 @@ final class Application private const ABORTED = 2; /** - * @var non-empty-list - */ - private array $templateProviders; - - /** - * @param list $templateProviders + * @param non-empty-list $templateProviders */ public function __construct( private readonly IO\Messenger $messenger, @@ -64,13 +59,8 @@ public function __construct( private readonly Error\ErrorHandler $errorHandler, private readonly Filesystem\Filesystem $filesystem, private readonly string $targetDirectory, - array $templateProviders = [], + private array $templateProviders, ) { - if ([] === $templateProviders) { - $templateProviders = $this->createDefaultTemplateProviders(); - } - - $this->templateProviders = $templateProviders; } public function run(): int @@ -170,16 +160,4 @@ private function buildContainer(Builder\Config\Config $config): \Symfony\Compone return $container; } - - /** - * @return non-empty-list - */ - private function createDefaultTemplateProviders(): array - { - return [ - new Template\Provider\PackagistProvider($this->messenger, $this->filesystem), - new Template\Provider\ComposerProvider($this->messenger, $this->filesystem), - new Template\Provider\VcsProvider($this->messenger, $this->filesystem), - ]; - } } diff --git a/src/Exception/UnknownTemplateProviderException.php b/src/Exception/UnknownTemplateProviderException.php new file mode 100644 index 00000000..810abd45 --- /dev/null +++ b/src/Exception/UnknownTemplateProviderException.php @@ -0,0 +1,43 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Exception; + +use function sprintf; + +/** + * UnknownTemplateProviderException. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + */ +final class UnknownTemplateProviderException extends Exception +{ + public static function create(string $identifier): self + { + return new self( + sprintf('The template provider "%s" does not exist or is not available.', $identifier), + 1677140774, + ); + } +} diff --git a/src/Template/Provider/ComposerProvider.php b/src/Template/Provider/ComposerProvider.php index a52a95dd..e59a5a52 100644 --- a/src/Template/Provider/ComposerProvider.php +++ b/src/Template/Provider/ComposerProvider.php @@ -99,9 +99,4 @@ public static function getType(): string { return self::TYPE; } - - public static function supports(string $type): bool - { - return self::TYPE === $type; - } } diff --git a/src/Template/Provider/PackagistProvider.php b/src/Template/Provider/PackagistProvider.php index 28eefe01..0c267285 100644 --- a/src/Template/Provider/PackagistProvider.php +++ b/src/Template/Provider/PackagistProvider.php @@ -52,9 +52,4 @@ public static function getType(): string { return self::TYPE; } - - public static function supports(string $type): bool - { - return self::TYPE === $type; - } } diff --git a/src/Template/Provider/ProviderFactory.php b/src/Template/Provider/ProviderFactory.php index c53c4c68..f7cfb621 100644 --- a/src/Template/Provider/ProviderFactory.php +++ b/src/Template/Provider/ProviderFactory.php @@ -24,6 +24,8 @@ namespace CPSIT\ProjectBuilder\Template\Provider; use CPSIT\ProjectBuilder\Exception; +use CPSIT\ProjectBuilder\IO; +use Symfony\Component\Filesystem; /** * ProviderFactory. @@ -34,21 +36,42 @@ final class ProviderFactory { /** - * @param iterable $providers + * @param non-empty-list $providers */ - public function __construct( - private readonly iterable $providers, + private function __construct( + private readonly array $providers, ) { } + public static function create(IO\Messenger $messenger, Filesystem\Filesystem $filesystem): self + { + return new self([ + // sorted by priority + new PackagistProvider($messenger, $filesystem), + new ComposerProvider($messenger, $filesystem), + new VcsProvider($messenger, $filesystem), + ]); + } + + /** + * @throws Exception\UnknownTemplateProviderException + */ public function get(string $type): ProviderInterface { foreach ($this->providers as $provider) { - if ($provider::supports($type)) { + if ($type === $provider::getType()) { return $provider; } } - throw Exception\UnsupportedTypeException::create($type); + throw Exception\UnknownTemplateProviderException::create($type); + } + + /** + * @return non-empty-list + */ + public function getAll(): array + { + return $this->providers; } } diff --git a/src/Template/Provider/ProviderInterface.php b/src/Template/Provider/ProviderInterface.php index 5dd019e4..dfb90db3 100644 --- a/src/Template/Provider/ProviderInterface.php +++ b/src/Template/Provider/ProviderInterface.php @@ -47,6 +47,4 @@ public function installTemplateSource(Template\TemplateSource $templateSource): public static function getName(): string; public static function getType(): string; - - public static function supports(string $type): bool; } diff --git a/src/Template/Provider/VcsProvider.php b/src/Template/Provider/VcsProvider.php index ec56f281..759b1853 100644 --- a/src/Template/Provider/VcsProvider.php +++ b/src/Template/Provider/VcsProvider.php @@ -95,9 +95,4 @@ public static function getType(): string { return self::TYPE; } - - public static function supports(string $type): bool - { - return self::TYPE === $type; - } } diff --git a/tests/src/Console/ApplicationTest.php b/tests/src/Console/ApplicationTest.php index e2d43c6c..97aff798 100644 --- a/tests/src/Console/ApplicationTest.php +++ b/tests/src/Console/ApplicationTest.php @@ -199,30 +199,6 @@ public function runGeneratesNewProjectFromSelectedTemplate(): void ); } - /** - * @test - */ - public function runUsesDefaultTemplateProvidersIfNoProvidersAreConfigured(): void - { - $subject = new Src\Console\Application( - $this->messenger, - $this->configReader, - new Src\Error\ErrorHandler($this->messenger), - $this->filesystem, - $this->targetDirectory, - ); - - self::$io->setUserInputs(['Try another template provider.']); - - $subject->run(); - - $output = self::$io->getOutput(); - - self::assertStringContainsString(Src\Template\Provider\PackagistProvider::getName(), $output); - self::assertStringContainsString(Src\Template\Provider\ComposerProvider::getName(), $output); - self::assertStringContainsString(Src\Template\Provider\VcsProvider::getName(), $output); - } - private function createTemplateSource(): Src\Template\TemplateSource { $sourcePath = dirname(__DIR__).'/Fixtures/Templates/json-template'; diff --git a/tests/src/Exception/UnknownTemplateProviderExceptionTest.php b/tests/src/Exception/UnknownTemplateProviderExceptionTest.php new file mode 100644 index 00000000..ccac8877 --- /dev/null +++ b/tests/src/Exception/UnknownTemplateProviderExceptionTest.php @@ -0,0 +1,47 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Tests\Exception; + +use CPSIT\ProjectBuilder as Src; +use PHPUnit\Framework\TestCase; + +/** + * UnknownTemplateProviderExceptionTest. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + */ +final class UnknownTemplateProviderExceptionTest extends TestCase +{ + /** + * @test + */ + public function createReturnsExceptionForUnknownTemplateProvider(): void + { + $actual = Src\Exception\UnknownTemplateProviderException::create('foo'); + + self::assertSame('The template provider "foo" does not exist or is not available.', $actual->getMessage()); + self::assertSame(1677140774, $actual->getCode()); + } +} diff --git a/tests/src/Fixtures/DummyComposerProvider.php b/tests/src/Fixtures/DummyComposerProvider.php index cdb9f044..0a227631 100644 --- a/tests/src/Fixtures/DummyComposerProvider.php +++ b/tests/src/Fixtures/DummyComposerProvider.php @@ -75,9 +75,4 @@ public static function getType(): string { return self::TYPE; } - - public static function supports(string $type): bool - { - return self::TYPE === $type; - } } diff --git a/tests/src/Fixtures/DummyProvider.php b/tests/src/Fixtures/DummyProvider.php index a8b60261..fd682d8d 100644 --- a/tests/src/Fixtures/DummyProvider.php +++ b/tests/src/Fixtures/DummyProvider.php @@ -90,9 +90,4 @@ public static function getType(): string { return self::TYPE; } - - public static function supports(string $type): bool - { - return self::TYPE === $type; - } } diff --git a/tests/src/Template/Provider/ProviderFactoryTest.php b/tests/src/Template/Provider/ProviderFactoryTest.php index efe4616a..7fd6f33a 100644 --- a/tests/src/Template/Provider/ProviderFactoryTest.php +++ b/tests/src/Template/Provider/ProviderFactoryTest.php @@ -25,6 +25,7 @@ use CPSIT\ProjectBuilder as Src; use CPSIT\ProjectBuilder\Tests; +use Symfony\Component\Filesystem; /** * ProviderFactoryTest. @@ -38,7 +39,10 @@ final class ProviderFactoryTest extends Tests\ContainerAwareTestCase protected function setUp(): void { - $this->subject = self::$container->get(Src\Template\Provider\ProviderFactory::class); + $this->subject = Src\Template\Provider\ProviderFactory::create( + self::$container->get(Src\IO\Messenger::class), + self::$container->get(Filesystem\Filesystem::class), + ); } /** @@ -46,9 +50,7 @@ protected function setUp(): void */ public function getThrowsExceptionIfNoProviderOfGivenTypeIsAvailable(): void { - $this->expectException(Src\Exception\UnsupportedTypeException::class); - $this->expectExceptionCode(1652800199); - $this->expectExceptionMessage('The type "foo" is not supported.'); + $this->expectExceptionObject(Src\Exception\UnknownTemplateProviderException::create('foo')); $this->subject->get('foo'); } @@ -71,4 +73,17 @@ public function getReturnsProviderOfGivenType(): void $this->subject->get('vcs'), ); } + + /** + * @test + */ + public function getAllReturnsAllProviders(): void + { + $actual = $this->subject->getAll(); + + self::assertCount(3, $actual); + self::assertInstanceOf(Src\Template\Provider\PackagistProvider::class, $actual[0]); + self::assertInstanceOf(Src\Template\Provider\ComposerProvider::class, $actual[1]); + self::assertInstanceOf(Src\Template\Provider\VcsProvider::class, $actual[2]); + } } From e4b13a18bf5a83fc7748e6be54fb22e5cae04fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=A4u=C3=9Fler?= Date: Wed, 22 Mar 2023 11:50:21 +0100 Subject: [PATCH 2/9] [FEATURE] Move JSON schema validation to dedicated service class --- config/services.php | 2 + docs/development/architecture/components.md | 18 ++++++ src/Builder/Config/ConfigFactory.php | 25 ++------ src/Helper/FilesystemHelper.php | 9 +++ src/Json/SchemaValidator.php | 59 ++++++++++++++++++ tests/src/Fixtures/Files/test.schema.json | 12 ++++ tests/src/Helper/FilesystemHelperTest.php | 21 +++++++ tests/src/Json/SchemaValidatorTest.php | 67 +++++++++++++++++++++ 8 files changed, 192 insertions(+), 21 deletions(-) create mode 100644 src/Json/SchemaValidator.php create mode 100644 tests/src/Fixtures/Files/test.schema.json create mode 100644 tests/src/Json/SchemaValidatorTest.php diff --git a/config/services.php b/config/services.php index fbe7ba13..87dd672d 100644 --- a/config/services.php +++ b/config/services.php @@ -29,6 +29,7 @@ use CPSIT\ProjectBuilder\Twig; use GuzzleHttp\Client as GuzzleClient; use Nyholm\Psr7; +use Opis\JsonSchema; use Psr\Http\Client; use Psr\Http\Message; use SebastianFeldmann\Cli; @@ -80,6 +81,7 @@ // Add external services $services->set(ExpressionLanguage\ExpressionLanguage::class); $services->set(Filesystem\Filesystem::class); + $services->set(JsonSchema\Validator::class); $services->set(Slugify\Slugify::class); $services->set(Client\ClientInterface::class, GuzzleClient::class); $services->set(Loader\LoaderInterface::class, Loader\FilesystemLoader::class); diff --git a/docs/development/architecture/components.md b/docs/development/architecture/components.md index 3f82ef49..f89700aa 100644 --- a/docs/development/architecture/components.md +++ b/docs/development/architecture/components.md @@ -69,6 +69,24 @@ Each validator implements [`ValidatorInterface`](https://github.com/CPS-IT/proje Not all validators can be used for each interaction with the `InputReader`. ``` +## JSON schema validation + +While working with JSON files, it's often useful to validate them against a defined +JSON schema. For this, the [**`Json\SchemaValidator`**](https://github.com/CPS-IT/project-builder/blob/main/src/Json/SchemaValidator.php) +component can be used. + +Example: + +```php +/** @var \CPSIT\ProjectBuilder\Json\SchemaValidator $schemaValidator */ + +$data = json_decode($json); +$validationResult = $schemaValidator->validate($data, $schemaFile); + +$isValid = $validationResult->isValid(); // Check if JSON is valid +$error = $validationResult->error(); // Get validation errors +``` + ## Naming With the [**`Naming\NameVariantBuilder`**](https://github.com/CPS-IT/project-builder/blob/main/src/Naming/NameVariantBuilder.php) diff --git a/src/Builder/Config/ConfigFactory.php b/src/Builder/Config/ConfigFactory.php index 34b98f44..a820afc7 100644 --- a/src/Builder/Config/ConfigFactory.php +++ b/src/Builder/Config/ConfigFactory.php @@ -25,6 +25,7 @@ use CPSIT\ProjectBuilder\Exception; use CPSIT\ProjectBuilder\Helper; +use CPSIT\ProjectBuilder\Json; use CPSIT\ProjectBuilder\Paths; use CuyZ\Valinor\Cache; use CuyZ\Valinor\Mapper; @@ -50,7 +51,7 @@ final class ConfigFactory private function __construct( private readonly Mapper\TreeMapper $mapper, - private readonly JsonSchema\Validator $validator, + private readonly Json\SchemaValidator $schemaValidator, ) { } @@ -65,7 +66,7 @@ public static function create(): self ->mapper() ; - return new self($mapper, new JsonSchema\Validator()); + return new self($mapper, new Json\SchemaValidator(new JsonSchema\Validator())); } public function buildFromFile(string $file, string $identifier): Config @@ -87,7 +88,7 @@ public function buildFromFile(string $file, string $identifier): Config public function buildFromString(string $content, string $identifier, FileType $fileType): Config { $parsedContent = $this->parseContent($content, $fileType); - $validationResult = $this->validateConfig($parsedContent); + $validationResult = $this->schemaValidator->validate($parsedContent, Paths::PROJECT_SCHEMA_CONFIG); if (!$validationResult->isValid()) { throw Exception\InvalidConfigurationException::forValidationErrors($validationResult->error()); @@ -98,24 +99,6 @@ public function buildFromString(string $content, string $identifier, FileType $f return $this->mapper->map(Config::class, $source); } - private function validateConfig(stdClass $parsedContent): JsonSchema\ValidationResult - { - $schemaFile = Filesystem\Path::join(Helper\FilesystemHelper::getProjectRootPath(), Paths::PROJECT_SCHEMA_CONFIG); - $schemaReference = 'file://'.$schemaFile; - $schemaResolver = $this->validator->resolver(); - - // @codeCoverageIgnoreStart - if (null === $schemaResolver) { - $schemaResolver = new JsonSchema\Resolvers\SchemaResolver(); - $this->validator->setResolver($schemaResolver); - } - // @codeCoverageIgnoreEnd - - $schemaResolver->registerFile($schemaReference, $schemaFile); - - return $this->validator->validate($parsedContent, $schemaReference); - } - private function generateMapperSource(string $content, string $identifier, FileType $fileType): Mapper\Source\Source { $parsedContent = match ($fileType) { diff --git a/src/Helper/FilesystemHelper.php b/src/Helper/FilesystemHelper.php index c3faca52..2336aaf2 100644 --- a/src/Helper/FilesystemHelper.php +++ b/src/Helper/FilesystemHelper.php @@ -68,4 +68,13 @@ public static function getProjectRootPath(): string return $rootPath ?? dirname(__DIR__, 2); } + + public static function resolveRelativePath(string $relativePath): string + { + if (Filesystem\Path::isAbsolute($relativePath)) { + return $relativePath; + } + + return Filesystem\Path::makeAbsolute($relativePath, self::getProjectRootPath()); + } } diff --git a/src/Json/SchemaValidator.php b/src/Json/SchemaValidator.php new file mode 100644 index 00000000..2bbbd8fb --- /dev/null +++ b/src/Json/SchemaValidator.php @@ -0,0 +1,59 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Json; + +use CPSIT\ProjectBuilder\Helper; +use Opis\JsonSchema; + +/** + * SchemaValidator. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + */ +final class SchemaValidator +{ + public function __construct( + private readonly JsonSchema\Validator $validator, + ) { + } + + public function validate(mixed $data, string $schemaFile): JsonSchema\ValidationResult + { + $schemaFile = Helper\FilesystemHelper::resolveRelativePath($schemaFile); + $schemaReference = 'file://'.$schemaFile; + $schemaResolver = $this->validator->resolver(); + + // @codeCoverageIgnoreStart + if (null === $schemaResolver) { + $schemaResolver = new JsonSchema\Resolvers\SchemaResolver(); + $this->validator->setResolver($schemaResolver); + } + // @codeCoverageIgnoreEnd + + $schemaResolver->registerFile($schemaReference, $schemaFile); + + return $this->validator->validate($data, $schemaReference); + } +} diff --git a/tests/src/Fixtures/Files/test.schema.json b/tests/src/Fixtures/Files/test.schema.json new file mode 100644 index 00000000..5f0e02af --- /dev/null +++ b/tests/src/Fixtures/Files/test.schema.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema#", + "type": "object", + "properties": { + "foo": { + "type": "string" + } + }, + "required": [ + "foo" + ] +} diff --git a/tests/src/Helper/FilesystemHelperTest.php b/tests/src/Helper/FilesystemHelperTest.php index 8ee638c0..c8c4daf2 100644 --- a/tests/src/Helper/FilesystemHelperTest.php +++ b/tests/src/Helper/FilesystemHelperTest.php @@ -84,4 +84,25 @@ public function getProjectRootPathReturnsProjectRootPathFromComposerPackageArtif { self::assertSame(dirname(__DIR__, 3), Src\Helper\FilesystemHelper::getProjectRootPath()); } + + /** + * @test + */ + public function resolveRelativePathReturnsGivenPathIfItIsAnAbsolutePath(): void + { + $path = '/foo/baz'; + + self::assertSame($path, Src\Helper\FilesystemHelper::resolveRelativePath($path)); + } + + /** + * @test + */ + public function resolveRelativePathMakesRelativePathAbsolute(): void + { + $path = 'foo'; + $expected = dirname(__DIR__, 3).'/foo'; + + self::assertSame($expected, Src\Helper\FilesystemHelper::resolveRelativePath($path)); + } } diff --git a/tests/src/Json/SchemaValidatorTest.php b/tests/src/Json/SchemaValidatorTest.php new file mode 100644 index 00000000..2356bf73 --- /dev/null +++ b/tests/src/Json/SchemaValidatorTest.php @@ -0,0 +1,67 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Tests\Json; + +use CPSIT\ProjectBuilder as Src; +use CPSIT\ProjectBuilder\Tests; +use Generator; + +use function dirname; + +/** + * SchemaValidatorTest. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + */ +final class SchemaValidatorTest extends Tests\ContainerAwareTestCase +{ + private Src\Json\SchemaValidator $subject; + + protected function setUp(): void + { + $this->subject = self::$container->get(Src\Json\SchemaValidator::class); + } + + /** + * @test + * + * @dataProvider validateValidatesJsonDataProvider + */ + public function validateValidatesJson(mixed $data, bool $expected): void + { + $schemaFile = dirname(__DIR__).'/Fixtures/Files/test.schema.json'; + + self::assertSame($expected, $this->subject->validate($data, $schemaFile)->isValid()); + } + + /** + * @return Generator + */ + public function validateValidatesJsonDataProvider(): Generator + { + yield 'valid json' => [(object) ['foo' => 'baz'], true]; + yield 'invalid json' => [null, false]; + } +} From a3740677e747f6fdf33a8e7fb61b90fc3203463c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=A4u=C3=9Fler?= Date: Wed, 22 Mar 2023 12:18:45 +0100 Subject: [PATCH 3/9] [TASK] Make ProviderFactory available in service container --- src/Bootstrap.php | 2 +- src/Template/Provider/ProviderFactory.php | 17 ++++++++--------- .../Template/Provider/ProviderFactoryTest.php | 6 +----- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/Bootstrap.php b/src/Bootstrap.php index 3adaad49..63bb0f2e 100644 --- a/src/Bootstrap.php +++ b/src/Bootstrap.php @@ -95,7 +95,7 @@ public static function simulateCreateProject(Script\Event $event): never private static function createApplication(IO\Messenger $messenger, string $targetDirectory): Console\Application { $filesystem = new Filesystem\Filesystem(); - $providerFactory = Template\Provider\ProviderFactory::create($messenger, $filesystem); + $providerFactory = new Template\Provider\ProviderFactory($messenger, $filesystem); return new Console\Application( $messenger, diff --git a/src/Template/Provider/ProviderFactory.php b/src/Template/Provider/ProviderFactory.php index f7cfb621..d4a86629 100644 --- a/src/Template/Provider/ProviderFactory.php +++ b/src/Template/Provider/ProviderFactory.php @@ -36,21 +36,20 @@ final class ProviderFactory { /** - * @param non-empty-list $providers + * @var non-empty-list */ - private function __construct( - private readonly array $providers, - ) { - } + private readonly array $providers; - public static function create(IO\Messenger $messenger, Filesystem\Filesystem $filesystem): self - { - return new self([ + public function __construct( + IO\Messenger $messenger, + Filesystem\Filesystem $filesystem, + ) { + $this->providers = [ // sorted by priority new PackagistProvider($messenger, $filesystem), new ComposerProvider($messenger, $filesystem), new VcsProvider($messenger, $filesystem), - ]); + ]; } /** diff --git a/tests/src/Template/Provider/ProviderFactoryTest.php b/tests/src/Template/Provider/ProviderFactoryTest.php index 7fd6f33a..8e96fb88 100644 --- a/tests/src/Template/Provider/ProviderFactoryTest.php +++ b/tests/src/Template/Provider/ProviderFactoryTest.php @@ -25,7 +25,6 @@ use CPSIT\ProjectBuilder as Src; use CPSIT\ProjectBuilder\Tests; -use Symfony\Component\Filesystem; /** * ProviderFactoryTest. @@ -39,10 +38,7 @@ final class ProviderFactoryTest extends Tests\ContainerAwareTestCase protected function setUp(): void { - $this->subject = Src\Template\Provider\ProviderFactory::create( - self::$container->get(Src\IO\Messenger::class), - self::$container->get(Filesystem\Filesystem::class), - ); + $this->subject = self::$container->get(Src\Template\Provider\ProviderFactory::class); } /** From da16731ba5f9ef4fcba80d6fd1523115f947c7b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=A4u=C3=9Fler?= Date: Wed, 22 Mar 2023 15:58:57 +0100 Subject: [PATCH 4/9] [FEATURE] Implement concept for artifact migration --- config/services.php | 3 + config/services.yaml | 6 +- rector.php | 5 + resources/build-artifact.schema.json | 2 +- src/Builder/Artifact/Artifact.php | 31 ++- src/Builder/Artifact/BuildArtifact.php | 49 ++--- src/Builder/Artifact/GeneratorArtifact.php | 32 ++- .../Artifact/Migration/BaseVersion.php | 64 ++++++ src/Builder/Artifact/Migration/Version.php | 46 ++++ src/Builder/Artifact/Migration/Version1.php | 56 +++++ src/Builder/Artifact/PackageArtifact.php | 39 ++-- src/Builder/Artifact/ResultArtifact.php | 48 ++--- src/Builder/Artifact/TemplateArtifact.php | 44 ++-- src/Builder/ArtifactGenerator.php | 177 +++++++++++++++ src/Builder/ArtifactReader.php | 202 ++++++++++++++++++ src/Builder/BuildResult.php | 32 ++- src/Builder/Generator/Generator.php | 3 +- src/Builder/Generator/Step/CleanUpStep.php | 12 +- .../Generator/Step/DumpBuildArtifactStep.php | 11 +- .../Step/GenerateBuildArtifactStep.php | 9 +- src/Exception/InvalidArtifactException.php | 77 +++++++ src/Helper/ArrayHelper.php | 30 +++ tests/config/services.yaml | 3 + tests/src/Builder/Artifact/ArtifactTest.php | 111 ++++++++++ .../Builder/Artifact/BuildArtifactTest.php | 62 ++++++ .../Artifact/GeneratorArtifactTest.php | 70 ++++++ .../Artifact/Migration/BaseVersionTest.php | 135 ++++++++++++ .../Artifact/Migration/Version1Test.php | 67 ++++++ .../Builder/Artifact/PackageArtifactTest.php | 70 ++++++ .../Builder/Artifact/ResultArtifactTest.php | 79 +++++++ .../Builder/Artifact/TemplateArtifactTest.php | 77 +++++++ tests/src/Builder/ArtifactGeneratorTest.php | 134 ++++++++++++ tests/src/Builder/ArtifactReaderTest.php | 128 +++++++++++ tests/src/Builder/BuildResultTest.php | 32 ++- .../Generator/Step/CleanUpStepTest.php | 1 + .../Step/CollectBuildInstructionsStepTest.php | 2 + .../Step/DumpBuildArtifactStepTest.php | 45 ++-- .../Step/GenerateBuildArtifactStepTest.php | 24 ++- .../InstallComposerDependenciesStepTest.php | 1 + .../Step/ProcessSharedSourceFilesStepTest.php | 1 + .../Step/ProcessSourceFilesStepTest.php | 2 + .../Generator/Step/ShowNextStepsStepTest.php | 1 + .../src/Event/BuildStepProcessedEventTest.php | 1 + .../src/Event/BuildStepRevertedEventTest.php | 1 + .../Event/ProjectBuildFinishedEventTest.php | 1 + .../InvalidArtifactExceptionTest.php | 105 +++++++++ tests/src/Fixtures/DummyVersion.php | 61 ++++++ tests/src/Fixtures/Files/build-artifact.json | 79 +++++++ .../Files/invalid-artifact-version.json | 5 + .../src/Fixtures/Files/invalid-artifact.json | 4 + tests/src/Fixtures/Files/invalid-json.json | 1 + tests/src/Helper/ArrayHelperTest.php | 44 ++++ 52 files changed, 2137 insertions(+), 188 deletions(-) create mode 100644 src/Builder/Artifact/Migration/BaseVersion.php create mode 100644 src/Builder/Artifact/Migration/Version.php create mode 100644 src/Builder/Artifact/Migration/Version1.php create mode 100644 src/Builder/ArtifactGenerator.php create mode 100644 src/Builder/ArtifactReader.php create mode 100644 src/Exception/InvalidArtifactException.php create mode 100644 tests/src/Builder/Artifact/ArtifactTest.php create mode 100644 tests/src/Builder/Artifact/BuildArtifactTest.php create mode 100644 tests/src/Builder/Artifact/GeneratorArtifactTest.php create mode 100644 tests/src/Builder/Artifact/Migration/BaseVersionTest.php create mode 100644 tests/src/Builder/Artifact/Migration/Version1Test.php create mode 100644 tests/src/Builder/Artifact/PackageArtifactTest.php create mode 100644 tests/src/Builder/Artifact/ResultArtifactTest.php create mode 100644 tests/src/Builder/Artifact/TemplateArtifactTest.php create mode 100644 tests/src/Builder/ArtifactGeneratorTest.php create mode 100644 tests/src/Builder/ArtifactReaderTest.php create mode 100644 tests/src/Exception/InvalidArtifactExceptionTest.php create mode 100644 tests/src/Fixtures/DummyVersion.php create mode 100644 tests/src/Fixtures/Files/build-artifact.json create mode 100644 tests/src/Fixtures/Files/invalid-artifact-version.json create mode 100644 tests/src/Fixtures/Files/invalid-artifact.json create mode 100644 tests/src/Fixtures/Files/invalid-json.json diff --git a/config/services.php b/config/services.php index 87dd672d..5109ea12 100644 --- a/config/services.php +++ b/config/services.php @@ -43,6 +43,9 @@ DependencyInjection\Loader\Configurator\ContainerConfigurator $configurator, DependencyInjection\ContainerBuilder $container, ): void { + $container->registerForAutoconfiguration(Builder\Artifact\Migration\Version::class) + ->addTag('artifact.version_migration') + ; $container->registerForAutoconfiguration(Builder\Writer\WriterInterface::class) ->addTag('builder.writer') ; diff --git a/config/services.yaml b/config/services.yaml index 9378b1b5..c592fb38 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -7,7 +7,7 @@ services: CPSIT\ProjectBuilder\: resource: '../src/*' exclude: - - '../src/Builder/Artifact/*' + - '../src/Builder/Artifact/*.php' - '../src/Builder/Config/*' - '../src/Builder/Generator/Step/CleanUpStep.php' - '../src/Builder/Generator/Step/DumpBuildArtifactStep.php' @@ -20,6 +20,10 @@ services: - '../src/Resource/Local/ProcessedFile.php' - '../src/Template/TemplateSource.php' + CPSIT\ProjectBuilder\Builder\ArtifactReader: + arguments: + $versions: !tagged_iterator artifact.version_migration + CPSIT\ProjectBuilder\Builder\Config\Config: alias: 'app.config' diff --git a/rector.php b/rector.php index b3099f4b..7bfea8eb 100644 --- a/rector.php +++ b/rector.php @@ -23,6 +23,7 @@ use Rector\Config\RectorConfig; use Rector\Core\ValueObject\PhpVersion; +use Rector\Php73\Rector\FuncCall\JsonThrowOnErrorRector; use Rector\Php74\Rector\LNumber\AddLiteralSeparatorToNumberRector; use Rector\Set\ValueObject\LevelSetList; @@ -37,6 +38,10 @@ __DIR__.'/tests/src/Fixtures/Templates/*/vendor/*', AddLiteralSeparatorToNumberRector::class, + JsonThrowOnErrorRector::class => [ + __DIR__.'/src/Builder/ArtifactGenerator.php', + __DIR__.'/src/Builder/ArtifactReader.php', + ], ]); $rectorConfig->phpVersion(PhpVersion::PHP_81); diff --git a/resources/build-artifact.schema.json b/resources/build-artifact.schema.json index 20266602..ad15dcb4 100644 --- a/resources/build-artifact.schema.json +++ b/resources/build-artifact.schema.json @@ -1,5 +1,5 @@ { - "$schema": "https://json-schema.org/draft-04/schema", + "$schema": "https://json-schema.org/draft/2019-09/schema#", "type": "object", "title": "Build artifact for projects generated with the Project Builder", "properties": { diff --git a/src/Builder/Artifact/Artifact.php b/src/Builder/Artifact/Artifact.php index bd8849fd..c6429181 100644 --- a/src/Builder/Artifact/Artifact.php +++ b/src/Builder/Artifact/Artifact.php @@ -33,17 +33,38 @@ * * @internal * - * @template T of array + * @phpstan-type ArtifactType array{ + * artifact: BuildArtifact, + * template: TemplateArtifact, + * generator: GeneratorArtifact, + * result: ResultArtifact + * } */ -abstract class Artifact implements JsonSerializable +final class Artifact implements JsonSerializable { + public function __construct( + public readonly BuildArtifact $artifact, + public readonly TemplateArtifact $template, + public readonly GeneratorArtifact $generator, + public readonly ResultArtifact $result, + ) { + } + /** - * @return T + * @phpstan-return ArtifactType */ - abstract public function dump(): array; + public function dump(): array + { + return [ + 'artifact' => $this->artifact, + 'template' => $this->template, + 'generator' => $this->generator, + 'result' => $this->result, + ]; + } /** - * @return T + * @phpstan-return ArtifactType */ public function jsonSerialize(): array { diff --git a/src/Builder/Artifact/BuildArtifact.php b/src/Builder/Artifact/BuildArtifact.php index 980557cb..7ee19b41 100644 --- a/src/Builder/Artifact/BuildArtifact.php +++ b/src/Builder/Artifact/BuildArtifact.php @@ -23,12 +23,7 @@ namespace CPSIT\ProjectBuilder\Builder\Artifact; -use Composer\Package; -use CPSIT\ProjectBuilder\Builder; -use CPSIT\ProjectBuilder\Helper; -use Symfony\Component\Finder; - -use function time; +use JsonSerializable; /** * BuildArtifact. @@ -37,41 +32,29 @@ * @license GPL-3.0-or-later * * @internal - * - * @extends Artifact */ -final class BuildArtifact extends Artifact +final class BuildArtifact implements JsonSerializable { - private const VERSION = 1; - public function __construct( - private readonly string $file, - private readonly Builder\BuildResult $buildResult, - private readonly Package\RootPackageInterface $rootPackage, + public readonly int $version, + public readonly string $path, + public readonly int $date, ) { } - public function dump(): array + /** + * @return array{ + * version: int, + * path: string, + * date: int, + * } + */ + public function jsonSerialize(): array { return [ - 'artifact' => [ - 'version' => self::VERSION, - 'file' => $this->file, - 'date' => time(), - ], - 'template' => new TemplateArtifact($this->buildResult), - 'generator' => new GeneratorArtifact($this->rootPackage), - 'result' => new ResultArtifact($this->buildResult), + 'version' => $this->version, + 'path' => $this->path, + 'date' => $this->date, ]; } - - public function getFile(): Finder\SplFileInfo - { - return Helper\FilesystemHelper::createFileObject($this->buildResult->getWrittenDirectory(), $this->file); - } } diff --git a/src/Builder/Artifact/GeneratorArtifact.php b/src/Builder/Artifact/GeneratorArtifact.php index dd7cd519..755b9bef 100644 --- a/src/Builder/Artifact/GeneratorArtifact.php +++ b/src/Builder/Artifact/GeneratorArtifact.php @@ -23,7 +23,7 @@ namespace CPSIT\ProjectBuilder\Builder\Artifact; -use Composer\Package; +use JsonSerializable; /** * GeneratorArtifact. @@ -32,32 +32,26 @@ * @license GPL-3.0-or-later * * @internal - * - * @extends Artifact */ -final class GeneratorArtifact extends Artifact +final class GeneratorArtifact implements JsonSerializable { public function __construct( - private readonly Package\RootPackageInterface $rootPackage, + public readonly PackageArtifact $package, + public readonly string $executor, ) { } - public function dump(): array + /** + * @return array{ + * package: PackageArtifact, + * executor: string, + * } + */ + public function jsonSerialize(): array { return [ - 'package' => new PackageArtifact($this->rootPackage), - 'executor' => $this->determineExecutor(), + 'package' => $this->package, + 'executor' => $this->executor, ]; } - - private function determineExecutor(): string - { - return match (getenv('PROJECT_BUILDER_EXECUTOR')) { - 'docker' => 'docker', - default => 'composer', - }; - } } diff --git a/src/Builder/Artifact/Migration/BaseVersion.php b/src/Builder/Artifact/Migration/BaseVersion.php new file mode 100644 index 00000000..1215096d --- /dev/null +++ b/src/Builder/Artifact/Migration/BaseVersion.php @@ -0,0 +1,64 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Builder\Artifact\Migration; + +use CPSIT\ProjectBuilder\Helper; + +use function is_callable; + +/** + * BaseVersion. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + */ +abstract class BaseVersion implements Version +{ + /** + * @param array $artifact + * @param non-empty-string $path + * @param non-empty-string|null $targetPath + */ + protected function remapValue( + array &$artifact, + string $path, + string $targetPath = null, + mixed $newValue = null, + ): void { + $currentValue = Helper\ArrayHelper::getValueByPath($artifact, $path); + + if (is_callable($newValue)) { + $newValue = $newValue($currentValue); + } elseif (null === $newValue) { + $newValue = $currentValue; + } + + if (null === $targetPath) { + Helper\ArrayHelper::setValueByPath($artifact, $path, $newValue); + } else { + Helper\ArrayHelper::setValueByPath($artifact, $targetPath, $newValue); + Helper\ArrayHelper::removeByPath($artifact, $path); + } + } +} diff --git a/src/Builder/Artifact/Migration/Version.php b/src/Builder/Artifact/Migration/Version.php new file mode 100644 index 00000000..2986fca9 --- /dev/null +++ b/src/Builder/Artifact/Migration/Version.php @@ -0,0 +1,46 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Builder\Artifact\Migration; + +/** + * Version. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + * + * @internal + */ +interface Version +{ + /** + * @param array $artifact + * + * @return array + */ + public function migrate(array $artifact): array; + + public static function getSourceVersion(): int; + + public static function getTargetVersion(): int; +} diff --git a/src/Builder/Artifact/Migration/Version1.php b/src/Builder/Artifact/Migration/Version1.php new file mode 100644 index 00000000..23c1ac87 --- /dev/null +++ b/src/Builder/Artifact/Migration/Version1.php @@ -0,0 +1,56 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Builder\Artifact\Migration; + +/** + * Version1. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + */ +final class Version1 extends BaseVersion +{ + public function migrate(array $artifact): array + { + // Silent migration from artifact.file to artifact.path + // since this was wrong implemented in the first place + $this->remapValue( + $artifact, + 'artifact.file', + 'artifact.path', + ); + + return $artifact; + } + + public static function getSourceVersion(): int + { + return 1; + } + + public static function getTargetVersion(): int + { + return 1; + } +} diff --git a/src/Builder/Artifact/PackageArtifact.php b/src/Builder/Artifact/PackageArtifact.php index b84ab7dd..0f8f882d 100644 --- a/src/Builder/Artifact/PackageArtifact.php +++ b/src/Builder/Artifact/PackageArtifact.php @@ -23,7 +23,7 @@ namespace CPSIT\ProjectBuilder\Builder\Artifact; -use Composer\Package; +use JsonSerializable; /** * PackageArtifact. @@ -32,30 +32,35 @@ * @license GPL-3.0-or-later * * @internal - * - * @extends Artifact */ -final class PackageArtifact extends Artifact +final class PackageArtifact implements JsonSerializable { public function __construct( - private readonly Package\PackageInterface $package, + public readonly string $name, + public readonly string $version, + public readonly ?string $sourceReference, + public readonly ?string $sourceUrl, + public readonly ?string $distUrl, ) { } - public function dump(): array + /** + * @return array{ + * name: string, + * version: string, + * sourceReference: string|null, + * sourceUrl: string|null, + * distUrl: string|null, + * } + */ + public function jsonSerialize(): array { return [ - 'name' => $this->package->getName(), - 'version' => $this->package->getVersion(), - 'sourceReference' => $this->package->getSourceReference(), - 'sourceUrl' => $this->package->getSourceUrl(), - 'distUrl' => $this->package->getDistUrl(), + 'name' => $this->name, + 'version' => $this->version, + 'sourceReference' => $this->sourceReference, + 'sourceUrl' => $this->sourceUrl, + 'distUrl' => $this->distUrl, ]; } } diff --git a/src/Builder/Artifact/ResultArtifact.php b/src/Builder/Artifact/ResultArtifact.php index 8d7abd33..ce35e8a6 100644 --- a/src/Builder/Artifact/ResultArtifact.php +++ b/src/Builder/Artifact/ResultArtifact.php @@ -23,8 +23,8 @@ namespace CPSIT\ProjectBuilder\Builder\Artifact; -use CPSIT\ProjectBuilder\Builder; -use CPSIT\ProjectBuilder\Resource; +use JsonSerializable; +use stdClass; /** * ResultArtifact. @@ -33,38 +33,34 @@ * @license GPL-3.0-or-later * * @internal - * - * @extends Artifact, - * steps: list, - * processedFiles: list - * }> */ -final class ResultArtifact extends Artifact +final class ResultArtifact implements JsonSerializable { + /** + * @param array $properties + * @param list $steps + * @param list $processedFiles + */ public function __construct( - private readonly Builder\BuildResult $buildResult, + public readonly array $properties, + public readonly array $steps, + public readonly array $processedFiles, ) { } - public function dump(): array + /** + * @return array{ + * properties: stdClass, + * steps: list, + * processedFiles: list, + * } + */ + public function jsonSerialize(): array { return [ - 'properties' => $this->buildResult->getInstructions()->getTemplateVariables(), - 'steps' => array_map( - fn (Builder\Config\ValueObject\Step $step) => [ - 'type' => $step->getType(), - 'applied' => $this->buildResult->isStepApplied($step->getType()), - ], - $this->buildResult->getInstructions()->getConfig()->getSteps(), - ), - 'processedFiles' => array_map( - fn (Resource\Local\ProcessedFile $processedFile) => [ - 'source' => $processedFile->getOriginalFile()->getRelativePathname(), - 'target' => $processedFile->getTargetFile()->getRelativePathname(), - ], - $this->buildResult->getProcessedFiles(), - ), + 'properties' => (object) $this->properties, + 'steps' => $this->steps, + 'processedFiles' => $this->processedFiles, ]; } } diff --git a/src/Builder/Artifact/TemplateArtifact.php b/src/Builder/Artifact/TemplateArtifact.php index 4f1a5936..eb075103 100644 --- a/src/Builder/Artifact/TemplateArtifact.php +++ b/src/Builder/Artifact/TemplateArtifact.php @@ -23,7 +23,7 @@ namespace CPSIT\ProjectBuilder\Builder\Artifact; -use CPSIT\ProjectBuilder\Builder\BuildResult; +use JsonSerializable; /** * TemplateArtifact. @@ -32,35 +32,35 @@ * @license GPL-3.0-or-later * * @internal - * - * @extends Artifact */ -final class TemplateArtifact extends Artifact +final class TemplateArtifact implements JsonSerializable { + /** + * @param array{name: string, url: string} $provider + */ public function __construct( - private readonly BuildResult $buildResult, + public readonly string $identifier, + public readonly string $hash, + public readonly PackageArtifact $package, + public readonly array $provider, ) { } - public function dump(): array + /** + * @return array{ + * identifier: string, + * hash: string, + * package: PackageArtifact, + * provider: array{name: string, url: string}, + * } + */ + public function jsonSerialize(): array { - $config = $this->buildResult->getInstructions()->getConfig(); - $package = $config->getTemplateSource()->getPackage(); - $provider = $config->getTemplateSource()->getProvider(); - return [ - 'identifier' => $config->getIdentifier(), - 'hash' => $config->buildHash(), - 'package' => new PackageArtifact($package), - 'provider' => [ - 'name' => $provider::getName(), - 'url' => $provider->getUrl(), - ], + 'identifier' => $this->identifier, + 'hash' => $this->hash, + 'package' => $this->package, + 'provider' => $this->provider, ]; } } diff --git a/src/Builder/ArtifactGenerator.php b/src/Builder/ArtifactGenerator.php new file mode 100644 index 00000000..59d6ea25 --- /dev/null +++ b/src/Builder/ArtifactGenerator.php @@ -0,0 +1,177 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Builder; + +use Composer\Package; +use CPSIT\ProjectBuilder\Exception; +use CPSIT\ProjectBuilder\Json; +use CPSIT\ProjectBuilder\Paths; +use CPSIT\ProjectBuilder\Resource; +use Symfony\Component\Finder; + +use function array_map; +use function getenv; +use function json_decode; +use function json_encode; + +/** + * ArtifactGenerator. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + * + * @internal + */ +final class ArtifactGenerator +{ + public const VERSION = 1; + + public function __construct( + private readonly Json\SchemaValidator $schemaValidator, + ) { + } + + /** + * @throws Exception\InvalidArtifactException + */ + public function build( + Finder\SplFileInfo $file, + BuildResult $buildResult, + Package\RootPackageInterface $rootPackage, + int $version = self::VERSION, + ): Artifact\Artifact { + // Generate artifact + $artifact = new Artifact\Artifact( + $this->generateBuildArtifact($file, $version), + $this->generateTemplateArtifact($buildResult), + $this->generateGeneratorArtifact($rootPackage), + $this->generateResultArtifact($buildResult), + ); + + // Validate generated artifact + $json = json_decode((string) json_encode($artifact)); + $validationResult = $this->schemaValidator->validate($json, Paths::BUILD_ARTIFACT_SCHEMA); + + if (!$validationResult->isValid()) { + throw Exception\InvalidArtifactException::forValidationErrors($validationResult->error()); + } + + return $artifact; + } + + private function generateBuildArtifact(Finder\SplFileInfo $file, int $version): Artifact\BuildArtifact + { + return new Artifact\BuildArtifact( + $version, + $file->getRelativePathname(), + time(), + ); + } + + private function generateTemplateArtifact(BuildResult $buildResult): Artifact\TemplateArtifact + { + $config = $buildResult->getInstructions()->getConfig(); + $package = $config->getTemplateSource()->getPackage(); + $provider = $config->getTemplateSource()->getProvider(); + + $providerArtifact = [ + 'name' => $provider::getName(), + 'url' => $provider->getUrl(), + ]; + + return new Artifact\TemplateArtifact( + $config->getIdentifier(), + $config->buildHash(), + $this->generatePackageArtifact($package), + $providerArtifact, + ); + } + + private function generateGeneratorArtifact(Package\RootPackageInterface $rootPackage): Artifact\GeneratorArtifact + { + return new Artifact\GeneratorArtifact( + $this->generatePackageArtifact($rootPackage), + $this->determineExecutor(), + ); + } + + private function generateResultArtifact(BuildResult $buildResult): Artifact\ResultArtifact + { + $steps = array_map( + fn (Config\ValueObject\Step $step) => $this->mapStep($step, $buildResult), + $buildResult->getInstructions()->getConfig()->getSteps(), + ); + $processedFiles = array_map( + $this->mapProcessedFile(...), + $buildResult->getProcessedFiles(), + ); + + return new Artifact\ResultArtifact( + $buildResult->getInstructions()->getTemplateVariables(), + $steps, + $processedFiles, + ); + } + + /** + * @return array{type: string, applied: bool} + */ + private function mapStep(Config\ValueObject\Step $step, BuildResult $buildResult): array + { + return [ + 'type' => $step->getType(), + 'applied' => $buildResult->isStepApplied($step->getType()), + ]; + } + + /** + * @return array{source: string, target: string} + */ + private function mapProcessedFile(Resource\Local\ProcessedFile $processedFile): array + { + return [ + 'source' => $processedFile->getOriginalFile()->getRelativePathname(), + 'target' => $processedFile->getTargetFile()->getRelativePathname(), + ]; + } + + private function generatePackageArtifact(Package\PackageInterface $package): Artifact\PackageArtifact + { + return new Artifact\PackageArtifact( + $package->getName(), + $package->getVersion(), + $package->getSourceReference(), + $package->getSourceUrl(), + $package->getDistUrl(), + ); + } + + private function determineExecutor(): string + { + return match (getenv('PROJECT_BUILDER_EXECUTOR')) { + 'docker' => 'docker', + default => 'composer', + }; + } +} diff --git a/src/Builder/ArtifactReader.php b/src/Builder/ArtifactReader.php new file mode 100644 index 00000000..a1e7be39 --- /dev/null +++ b/src/Builder/ArtifactReader.php @@ -0,0 +1,202 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Builder; + +use CPSIT\ProjectBuilder\Exception; +use CPSIT\ProjectBuilder\Helper; +use CPSIT\ProjectBuilder\Json; +use CPSIT\ProjectBuilder\Paths; +use CuyZ\Valinor; +use DateTimeInterface; +use Opis\JsonSchema; +use Symfony\Component\Filesystem; + +use function array_filter; +use function file_get_contents; +use function is_array; +use function is_numeric; +use function json_decode; +use function ksort; +use function range; + +/** + * ArtifactReader. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + */ +final class ArtifactReader +{ + private readonly Valinor\Mapper\TreeMapper $mapper; + + /** + * @var list + */ + private readonly array $versions; + + /** + * @param iterable $versions + */ + public function __construct( + iterable $versions, + private readonly Filesystem\Filesystem $filesystem, + private readonly Json\SchemaValidator $schemaValidator, + ) { + $this->mapper = $this->createMapper(); + $this->versions = $this->orderVersions($versions); + } + + /** + * @throws Exception\InvalidArtifactException + * @throws Valinor\Mapper\MappingError + */ + public function fromFile(string $file): Artifact\Artifact + { + $artifact = $this->parseArtifactFile($file); + $migratedArtifact = $this->performMigrations($artifact); + $validationResult = $this->schemaValidator->validate( + JsonSchema\Helper::toJSON($migratedArtifact), + Paths::BUILD_ARTIFACT_SCHEMA, + ); + + // Validate migrated artifact + if (!$validationResult->isValid()) { + throw Exception\InvalidArtifactException::forValidationErrors($validationResult->error()); + } + + // Create mapper source from artifact file + $source = Valinor\Mapper\Source\Source::array($migratedArtifact); + + return $this->mapper->map(Artifact\Artifact::class, $source); + } + + /** + * @return array + * + * @throws Exception\InvalidArtifactException + */ + private function parseArtifactFile(string $file): array + { + if (!$this->filesystem->exists($file)) { + throw Exception\InvalidArtifactException::forFile($file); + } + + $content = file_get_contents($file); + + // @codeCoverageIgnoreStart + if (false === $content) { + throw Exception\InvalidArtifactException::forFile($file); + } + // @codeCoverageIgnoreEnd + + $artifact = json_decode($content, true); + + // Assure artifact is an array + if (!is_array($artifact)) { + throw Exception\InvalidArtifactException::forFile($file); + } + + // Assure artifact is an associative array + if ($artifact !== array_filter($artifact, 'is_string', ARRAY_FILTER_USE_KEY)) { + throw Exception\InvalidArtifactException::forFile($file); + } + + return $artifact; + } + + /** + * @param array $artifact + * + * @return array + * + * @throws Exception\InvalidArtifactException + */ + private function performMigrations(array $artifact): array + { + $artifactVersion = $this->determineArtifactVersion($artifact); + $migrationPath = range($artifactVersion, ArtifactGenerator::VERSION); + + foreach ($migrationPath as $sourceVersion) { + foreach ($this->versions as $version) { + if ($sourceVersion === $version::getSourceVersion()) { + $artifact = $version->migrate($artifact); + } + } + } + + return $artifact; + } + + /** + * @param array $artifact + * + * @throws Exception\InvalidArtifactException + */ + private function determineArtifactVersion(array $artifact): int + { + $version = Helper\ArrayHelper::getValueByPath($artifact, 'artifact.version'); + + if (!is_numeric($version)) { + throw Exception\InvalidArtifactException::forInvalidVersion(); + } + + return (int) $version; + } + + private function createMapper(): Valinor\Mapper\TreeMapper + { + return (new Valinor\MapperBuilder()) + ->allowPermissiveTypes() + ->supportDateFormats(DateTimeInterface::ATOM) + ->mapper() + ; + } + + /** + * @param iterable $versions + * + * @return list + */ + private function orderVersions(iterable $versions): array + { + $prefixedVersions = []; + $orderedVersions = []; + + foreach ($versions as $version) { + $migrationPath = $version::getSourceVersion().'_'.$version::getTargetVersion(); + $prefixedVersions[$migrationPath] ??= []; + $prefixedVersions[$migrationPath][] = $version; + } + + ksort($prefixedVersions); + + foreach ($prefixedVersions as $prefixedVersionList) { + foreach ($prefixedVersionList as $version) { + $orderedVersions[] = $version; + } + } + + return $orderedVersions; + } +} diff --git a/src/Builder/BuildResult.php b/src/Builder/BuildResult.php index 6f9ec996..a04c3a1e 100644 --- a/src/Builder/BuildResult.php +++ b/src/Builder/BuildResult.php @@ -23,8 +23,10 @@ namespace CPSIT\ProjectBuilder\Builder; +use CPSIT\ProjectBuilder\Helper; use CPSIT\ProjectBuilder\Resource; use Symfony\Component\Filesystem; +use Symfony\Component\Finder; use function array_values; @@ -37,7 +39,7 @@ final class BuildResult { private bool $mirrored = false; - private ?Artifact\BuildArtifact $buildArtifact = null; + private ?Finder\SplFileInfo $artifactFile = null; /** * @var array @@ -46,6 +48,7 @@ final class BuildResult public function __construct( private readonly BuildInstructions $instructions, + private readonly ArtifactGenerator $artifactGenerator, ) { } @@ -66,18 +69,30 @@ public function setMirrored(bool $mirrored): self return $this; } - public function getBuildArtifact(): ?Artifact\BuildArtifact + public function getArtifactFile(): ?Finder\SplFileInfo { - return $this->buildArtifact; + return $this->artifactFile; } - public function setBuildArtifact(Artifact\BuildArtifact $buildArtifact): self + /** + * @impure + */ + public function setArtifactFile(?Finder\SplFileInfo $artifactFile): self { - $this->buildArtifact = $buildArtifact; + $this->artifactFile = $artifactFile; return $this; } + public function getArtifact(): ?Artifact\Artifact + { + if (null !== $this->artifactFile) { + return $this->generateArtifact($this->artifactFile); + } + + return null; + } + /** * @return array */ @@ -134,4 +149,11 @@ public function getWrittenDirectory(): string return $this->instructions->getTemporaryDirectory(); } + + private function generateArtifact(Finder\SplFileInfo $artifactFile): Artifact\Artifact + { + $composer = Resource\Local\Composer::createComposer(Helper\FilesystemHelper::getProjectRootPath()); + + return $this->artifactGenerator->build($artifactFile, $this, $composer->getPackage()); + } } diff --git a/src/Builder/Generator/Generator.php b/src/Builder/Generator/Generator.php index a1449c64..dc6998cf 100644 --- a/src/Builder/Generator/Generator.php +++ b/src/Builder/Generator/Generator.php @@ -54,6 +54,7 @@ public function __construct( private readonly Filesystem\Filesystem $filesystem, private readonly EventDispatcher\EventDispatcherInterface $eventDispatcher, private readonly Builder\Writer\JsonFileWriter $writer, + private readonly Builder\ArtifactGenerator $artifactGenerator, ) { } @@ -64,7 +65,7 @@ public function run(string $targetDirectory): Builder\BuildResult } $instructions = new Builder\BuildInstructions($this->config, $targetDirectory); - $result = new Builder\BuildResult($instructions); + $result = new Builder\BuildResult($instructions, $this->artifactGenerator); $this->eventDispatcher->dispatch(new Event\ProjectBuildStartedEvent($instructions)); diff --git a/src/Builder/Generator/Step/CleanUpStep.php b/src/Builder/Generator/Step/CleanUpStep.php index e2b5c4d4..ecdf70b0 100644 --- a/src/Builder/Generator/Step/CleanUpStep.php +++ b/src/Builder/Generator/Step/CleanUpStep.php @@ -87,19 +87,19 @@ public static function supports(string $type): bool private function backupBuildArtifact(Builder\BuildResult $buildResult): ?Finder\SplFileInfo { - $buildArtifact = $buildResult->getBuildArtifact(); + $artifactFile = $buildResult->getArtifactFile(); - if (null === $buildArtifact || !$this->filesystem->exists($buildArtifact->getFile()->getPathname())) { + if (null === $artifactFile || !$this->filesystem->exists($artifactFile->getPathname())) { return null; } $backupFile = Helper\FilesystemHelper::createFileObject( Helper\FilesystemHelper::getNewTemporaryDirectory(), - $buildArtifact->getFile()->getFilename(), + $artifactFile->getFilename(), ); $this->filesystem->copy( - $buildArtifact->getFile()->getPathname(), + $artifactFile->getPathname(), $backupFile->getPathname(), ); @@ -108,13 +108,13 @@ private function backupBuildArtifact(Builder\BuildResult $buildResult): ?Finder\ private function restoreBuildArtifact(Builder\BuildResult $buildResult, Finder\SplFileInfo $artifactBackup): void { - if (null === $buildResult->getBuildArtifact()) { + if (null === $buildResult->getArtifactFile()) { return; } $this->filesystem->copy( $artifactBackup->getPathname(), - $buildResult->getBuildArtifact()->getFile()->getPathname(), + $buildResult->getArtifactFile()->getPathname(), ); $this->filesystem->remove($artifactBackup->getPath()); } diff --git a/src/Builder/Generator/Step/DumpBuildArtifactStep.php b/src/Builder/Generator/Step/DumpBuildArtifactStep.php index da77548c..0e60387f 100644 --- a/src/Builder/Generator/Step/DumpBuildArtifactStep.php +++ b/src/Builder/Generator/Step/DumpBuildArtifactStep.php @@ -47,21 +47,22 @@ public function __construct( public function run(Builder\BuildResult $buildResult): bool { - $buildArtifact = $buildResult->getBuildArtifact(); + $artifactFile = $buildResult->getArtifactFile(); + $artifact = $buildResult->getArtifact(); - if (null === $buildArtifact) { + if (null === $artifactFile || null === $artifact) { return true; } $buildResult->applyStep($this); - return $this->writer->write($buildArtifact->getFile(), $buildArtifact); + return $this->writer->write($artifactFile, $artifact); } public function revert(Builder\BuildResult $buildResult): void { - if (null !== $buildResult->getBuildArtifact()) { - $this->filesystem->remove($buildResult->getBuildArtifact()->getFile()->getPathname()); + if (null !== $buildResult->getArtifactFile()) { + $this->filesystem->remove($buildResult->getArtifactFile()->getPathname()); } } diff --git a/src/Builder/Generator/Step/GenerateBuildArtifactStep.php b/src/Builder/Generator/Step/GenerateBuildArtifactStep.php index a9fdab11..b0a62d9c 100644 --- a/src/Builder/Generator/Step/GenerateBuildArtifactStep.php +++ b/src/Builder/Generator/Step/GenerateBuildArtifactStep.php @@ -26,7 +26,6 @@ use CPSIT\ProjectBuilder\Builder; use CPSIT\ProjectBuilder\Helper; use CPSIT\ProjectBuilder\IO; -use CPSIT\ProjectBuilder\Resource; use Symfony\Component\Filesystem; use Symfony\Component\Finder; @@ -64,13 +63,7 @@ public function run(Builder\BuildResult $buildResult): bool return !$this->stopped; } - $artifact = new Builder\Artifact\BuildArtifact( - $artifactFile->getRelativePathname(), - $buildResult, - Resource\Local\Composer::createComposer(Helper\FilesystemHelper::getProjectRootPath())->getPackage(), - ); - - $buildResult->setBuildArtifact($artifact); + $buildResult->setArtifactFile($artifactFile); $buildResult->applyStep($this); return true; diff --git a/src/Exception/InvalidArtifactException.php b/src/Exception/InvalidArtifactException.php new file mode 100644 index 00000000..a17c79e8 --- /dev/null +++ b/src/Exception/InvalidArtifactException.php @@ -0,0 +1,77 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Exception; + +use Opis\JsonSchema; + +use function sprintf; + +/** + * InvalidArtifactException. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + */ +final class InvalidArtifactException extends Exception +{ + public static function forFile(string $file): self + { + return new self( + sprintf('The artifact file "%s" is invalid and cannot be processed.', $file), + 1677141460, + ); + } + + public static function forPath(string $path): self + { + return new self( + sprintf('Invalid value at "%s" in artifact.', $path), + 1677140440, + ); + } + + public static function forInvalidVersion(): self + { + return new self('Unable to detect a valid artifact version.', 1677141758); + } + + public static function forValidationErrors(?JsonSchema\Errors\ValidationError $error): self + { + $decoratedErrors = ''; + + if (null !== $error) { + $formatter = new JsonSchema\Errors\ErrorFormatter(); + $formattedErrors = $formatter->format($error, false); + + foreach ($formattedErrors as $path => $errorMessage) { + $decoratedErrors .= PHP_EOL.sprintf(' * Error at property path "%s": %s', $path, $errorMessage); + } + } + + return new self( + sprintf('The artifact does not match the build artifact schema.%s', $decoratedErrors), + 1677601857, + ); + } +} diff --git a/src/Helper/ArrayHelper.php b/src/Helper/ArrayHelper.php index a370c27a..2fa25370 100644 --- a/src/Helper/ArrayHelper.php +++ b/src/Helper/ArrayHelper.php @@ -25,6 +25,9 @@ use ArrayObject; +use function count; +use function is_array; + /** * ArrayHelper. * @@ -78,6 +81,33 @@ public static function setValueByPath(array|ArrayObject &$subject, string $path, $reference = $value; } + /** + * @param iterable $subject + * @param non-empty-string $path + */ + public static function removeByPath(iterable &$subject, string $path): void + { + $pathSegments = array_filter(explode('.', $path)); + $maxSegmentIndex = count($pathSegments) - 1; + $reference = &$subject; + + foreach ($pathSegments as $currentSegmentIndex => $pathSegment) { + // Early return if given path segment does not exist + if (!self::pathSegmentExists($reference, $pathSegment)) { + return; + } + + // Unset target path + if ($currentSegmentIndex === $maxSegmentIndex) { + unset($reference[$pathSegment]); + + return; + } + + $reference = &$reference[$pathSegment]; + } + } + private static function pathSegmentExists(mixed $subject, string $pathSegment): bool { if (!is_array($subject) && !($subject instanceof ArrayObject)) { diff --git a/tests/config/services.yaml b/tests/config/services.yaml index 07f3abce..a2768108 100644 --- a/tests/config/services.yaml +++ b/tests/config/services.yaml @@ -16,3 +16,6 @@ services: CPSIT\ProjectBuilder\Tests\Fixtures\DummyTemplateRenderingEventListener: tags: ['event.listener'] + + CPSIT\ProjectBuilder\Tests\Fixtures\DummyVersion: + tags: ['artifact.version_migration'] diff --git a/tests/src/Builder/Artifact/ArtifactTest.php b/tests/src/Builder/Artifact/ArtifactTest.php new file mode 100644 index 00000000..8d146b6d --- /dev/null +++ b/tests/src/Builder/Artifact/ArtifactTest.php @@ -0,0 +1,111 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Tests\Builder\Artifact; + +use CPSIT\ProjectBuilder as Src; +use PHPUnit\Framework\TestCase; + +/** + * ArtifactTest. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + */ +final class ArtifactTest extends TestCase +{ + private Src\Builder\Artifact\BuildArtifact $buildArtifact; + private Src\Builder\Artifact\TemplateArtifact $templateArtifact; + private Src\Builder\Artifact\GeneratorArtifact $generatorArtifact; + private Src\Builder\Artifact\ResultArtifact $resultArtifact; + private Src\Builder\Artifact\Artifact $subject; + + protected function setUp(): void + { + $this->buildArtifact = new Src\Builder\Artifact\BuildArtifact(1, 'file', 123); + $this->templateArtifact = new Src\Builder\Artifact\TemplateArtifact( + 'identifier', + 'hash', + new Src\Builder\Artifact\PackageArtifact( + 'name', + 'version', + 'sourceReference', + 'sourceUrl', + 'distUrl', + ), + [ + 'name' => 'name', + 'url' => 'url', + ], + ); + $this->generatorArtifact = new Src\Builder\Artifact\GeneratorArtifact( + new Src\Builder\Artifact\PackageArtifact( + 'name', + 'version', + 'sourceReference', + 'sourceUrl', + 'distUrl', + ), + 'composer', + ); + $this->resultArtifact = new Src\Builder\Artifact\ResultArtifact( + [ + 'foo' => 'foo', + 'baz' => 'baz', + ], + [ + [ + 'type' => 'type', + 'applied' => true, + ], + ], + [ + [ + 'source' => 'source', + 'target' => 'target', + ], + ], + ); + $this->subject = new Src\Builder\Artifact\Artifact( + $this->buildArtifact, + $this->templateArtifact, + $this->generatorArtifact, + $this->resultArtifact, + ); + } + + /** + * @test + */ + public function dumpReturnsDumpedArtifact(): void + { + $expected = [ + 'artifact' => $this->buildArtifact, + 'template' => $this->templateArtifact, + 'generator' => $this->generatorArtifact, + 'result' => $this->resultArtifact, + ]; + + self::assertSame($expected, $this->subject->dump()); + } +} diff --git a/tests/src/Builder/Artifact/BuildArtifactTest.php b/tests/src/Builder/Artifact/BuildArtifactTest.php new file mode 100644 index 00000000..1c24a916 --- /dev/null +++ b/tests/src/Builder/Artifact/BuildArtifactTest.php @@ -0,0 +1,62 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Tests\Builder\Artifact; + +use CPSIT\ProjectBuilder as Src; +use PHPUnit\Framework\TestCase; + +use function json_encode; + +/** + * BuildArtifactTest. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + */ +final class BuildArtifactTest extends TestCase +{ + private Src\Builder\Artifact\BuildArtifact $subject; + + protected function setUp(): void + { + $this->subject = new Src\Builder\Artifact\BuildArtifact(1, 'file', 123); + } + + /** + * @test + */ + public function artifactIsJsonSerializable(): void + { + $expected = [ + 'version' => $this->subject->version, + 'path' => $this->subject->path, + 'date' => $this->subject->date, + ]; + + self::assertJsonStringEqualsJsonString( + json_encode($expected, JSON_THROW_ON_ERROR), + json_encode($this->subject, JSON_THROW_ON_ERROR), + ); + } +} diff --git a/tests/src/Builder/Artifact/GeneratorArtifactTest.php b/tests/src/Builder/Artifact/GeneratorArtifactTest.php new file mode 100644 index 00000000..ffe9a330 --- /dev/null +++ b/tests/src/Builder/Artifact/GeneratorArtifactTest.php @@ -0,0 +1,70 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Tests\Builder\Artifact; + +use CPSIT\ProjectBuilder as Src; +use PHPUnit\Framework\TestCase; + +use function json_encode; + +/** + * GeneratorArtifactTest. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + */ +final class GeneratorArtifactTest extends TestCase +{ + private Src\Builder\Artifact\GeneratorArtifact $subject; + + protected function setUp(): void + { + $this->subject = new Src\Builder\Artifact\GeneratorArtifact( + new Src\Builder\Artifact\PackageArtifact( + 'name', + 'version', + 'sourceReference', + 'sourceUrl', + 'distUrl', + ), + 'composer', + ); + } + + /** + * @test + */ + public function artifactIsJsonSerializable(): void + { + $expected = [ + 'package' => $this->subject->package, + 'executor' => $this->subject->executor, + ]; + + self::assertJsonStringEqualsJsonString( + json_encode($expected, JSON_THROW_ON_ERROR), + json_encode($this->subject, JSON_THROW_ON_ERROR), + ); + } +} diff --git a/tests/src/Builder/Artifact/Migration/BaseVersionTest.php b/tests/src/Builder/Artifact/Migration/BaseVersionTest.php new file mode 100644 index 00000000..399423aa --- /dev/null +++ b/tests/src/Builder/Artifact/Migration/BaseVersionTest.php @@ -0,0 +1,135 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Tests\Builder\Artifact\Migration; + +use CPSIT\ProjectBuilder\Tests; +use PHPUnit\Framework\TestCase; + +use function strrev; + +/** + * BaseVersionTest. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + */ +final class BaseVersionTest extends TestCase +{ + private Tests\Fixtures\DummyVersion $subject; + + /** + * @var array + */ + private array $artifact; + + protected function setUp(): void + { + $this->subject = new Tests\Fixtures\DummyVersion(); + $this->artifact = [ + 'foo' => [ + 'baz' => 'hello world', + ], + 'baz' => 'dummy', + ]; + } + + /** + * @test + */ + public function remapValueCanRemapPathToOtherPath(): void + { + $this->subject->remapArguments = [ + 'foo.baz', + 'baz', + ]; + + $expected = [ + 'foo' => [], + 'baz' => 'hello world', + ]; + + self::assertSame($expected, $this->subject->migrate($this->artifact)); + } + + /** + * @test + */ + public function remapValueCanRemapPathToOtherValue(): void + { + $this->subject->remapArguments = [ + 'foo.baz', + null, + 'bye!', + ]; + + $expected = [ + 'foo' => [ + 'baz' => 'bye!', + ], + 'baz' => 'dummy', + ]; + + self::assertSame($expected, $this->subject->migrate($this->artifact)); + } + + /** + * @test + */ + public function remapValueCanRemapPathToOtherValueFromCallable(): void + { + $this->subject->remapArguments = [ + 'foo.baz', + null, + static fn (string $currentValue) => strrev($currentValue), + ]; + + $expected = [ + 'foo' => [ + 'baz' => 'dlrow olleh', + ], + 'baz' => 'dummy', + ]; + + self::assertSame($expected, $this->subject->migrate($this->artifact)); + } + + /** + * @test + */ + public function remapValueCanRemapPathToOtherPathAndValue(): void + { + $this->subject->remapArguments = [ + 'foo.baz', + 'baz', + static fn (string $currentValue) => strrev($currentValue), + ]; + + $expected = [ + 'foo' => [], + 'baz' => 'dlrow olleh', + ]; + + self::assertSame($expected, $this->subject->migrate($this->artifact)); + } +} diff --git a/tests/src/Builder/Artifact/Migration/Version1Test.php b/tests/src/Builder/Artifact/Migration/Version1Test.php new file mode 100644 index 00000000..97ef9f2d --- /dev/null +++ b/tests/src/Builder/Artifact/Migration/Version1Test.php @@ -0,0 +1,67 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Tests\Builder\Artifact\Migration; + +use CPSIT\ProjectBuilder as Src; +use PHPUnit\Framework\TestCase; + +/** + * Version1Test. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + */ +final class Version1Test extends TestCase +{ + private Src\Builder\Artifact\Migration\Version1 $subject; + + /** + * @var array + */ + private array $artifact; + + protected function setUp(): void + { + $this->subject = new Src\Builder\Artifact\Migration\Version1(); + $this->artifact = [ + 'artifact' => [ + 'file' => 'foo', + ], + ]; + } + + /** + * @test + */ + public function migrateMigratesArtifactFileToArtifactPath(): void + { + $expected = [ + 'artifact' => [ + 'path' => 'foo', + ], + ]; + + self::assertSame($expected, $this->subject->migrate($this->artifact)); + } +} diff --git a/tests/src/Builder/Artifact/PackageArtifactTest.php b/tests/src/Builder/Artifact/PackageArtifactTest.php new file mode 100644 index 00000000..e8118a03 --- /dev/null +++ b/tests/src/Builder/Artifact/PackageArtifactTest.php @@ -0,0 +1,70 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Tests\Builder\Artifact; + +use CPSIT\ProjectBuilder as Src; +use PHPUnit\Framework\TestCase; + +use function json_encode; + +/** + * PackageArtifactTest. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + */ +final class PackageArtifactTest extends TestCase +{ + private Src\Builder\Artifact\PackageArtifact $subject; + + protected function setUp(): void + { + $this->subject = new Src\Builder\Artifact\PackageArtifact( + 'name', + 'version', + 'sourceReference', + 'sourceUrl', + 'distUrl', + ); + } + + /** + * @test + */ + public function artifactIsJsonSerializable(): void + { + $expected = [ + 'name' => $this->subject->name, + 'version' => $this->subject->version, + 'sourceReference' => $this->subject->sourceReference, + 'sourceUrl' => $this->subject->sourceUrl, + 'distUrl' => $this->subject->distUrl, + ]; + + self::assertJsonStringEqualsJsonString( + json_encode($expected, JSON_THROW_ON_ERROR), + json_encode($this->subject, JSON_THROW_ON_ERROR), + ); + } +} diff --git a/tests/src/Builder/Artifact/ResultArtifactTest.php b/tests/src/Builder/Artifact/ResultArtifactTest.php new file mode 100644 index 00000000..a66ca473 --- /dev/null +++ b/tests/src/Builder/Artifact/ResultArtifactTest.php @@ -0,0 +1,79 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Tests\Builder\Artifact; + +use CPSIT\ProjectBuilder as Src; +use PHPUnit\Framework\TestCase; + +use function json_encode; + +/** + * ResultArtifactTest. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + */ +final class ResultArtifactTest extends TestCase +{ + private Src\Builder\Artifact\ResultArtifact $subject; + + protected function setUp(): void + { + $this->subject = new Src\Builder\Artifact\ResultArtifact( + [ + 'foo' => 'foo', + 'baz' => 'baz', + ], + [ + [ + 'type' => 'type', + 'applied' => true, + ], + ], + [ + [ + 'source' => 'source', + 'target' => 'target', + ], + ], + ); + } + + /** + * @test + */ + public function artifactIsJsonSerializable(): void + { + $expected = [ + 'properties' => $this->subject->properties, + 'steps' => $this->subject->steps, + 'processedFiles' => $this->subject->processedFiles, + ]; + + self::assertJsonStringEqualsJsonString( + json_encode($expected, JSON_THROW_ON_ERROR), + json_encode($this->subject, JSON_THROW_ON_ERROR), + ); + } +} diff --git a/tests/src/Builder/Artifact/TemplateArtifactTest.php b/tests/src/Builder/Artifact/TemplateArtifactTest.php new file mode 100644 index 00000000..d3552082 --- /dev/null +++ b/tests/src/Builder/Artifact/TemplateArtifactTest.php @@ -0,0 +1,77 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Tests\Builder\Artifact; + +use CPSIT\ProjectBuilder as Src; +use PHPUnit\Framework\TestCase; + +use function json_encode; + +/** + * TemplateArtifactTest. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + */ +final class TemplateArtifactTest extends TestCase +{ + private Src\Builder\Artifact\TemplateArtifact $subject; + + protected function setUp(): void + { + $this->subject = new Src\Builder\Artifact\TemplateArtifact( + 'identifier', + 'hash', + new Src\Builder\Artifact\PackageArtifact( + 'name', + 'version', + 'sourceReference', + 'sourceUrl', + 'distUrl', + ), + [ + 'name' => 'name', + 'url' => 'url', + ], + ); + } + + /** + * @test + */ + public function artifactIsJsonSerializable(): void + { + $expected = [ + 'identifier' => $this->subject->identifier, + 'hash' => $this->subject->hash, + 'package' => $this->subject->package, + 'provider' => $this->subject->provider, + ]; + + self::assertJsonStringEqualsJsonString( + json_encode($expected, JSON_THROW_ON_ERROR), + json_encode($this->subject, JSON_THROW_ON_ERROR), + ); + } +} diff --git a/tests/src/Builder/ArtifactGeneratorTest.php b/tests/src/Builder/ArtifactGeneratorTest.php new file mode 100644 index 00000000..aabd7b43 --- /dev/null +++ b/tests/src/Builder/ArtifactGeneratorTest.php @@ -0,0 +1,134 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Tests\Builder; + +use Composer\Package; +use CPSIT\ProjectBuilder as Src; +use CPSIT\ProjectBuilder\Tests; +use Symfony\Component\Finder; + +use function dirname; + +/** + * ArtifactGeneratorTest. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + */ +final class ArtifactGeneratorTest extends Tests\ContainerAwareTestCase +{ + private Src\Builder\ArtifactGenerator $subject; + private Finder\SplFileInfo $artifactFile; + private Src\Builder\BuildResult $buildResult; + private Package\RootPackageInterface $rootPackage; + + protected function setUp(): void + { + $this->subject = self::$container->get(Src\Builder\ArtifactGenerator::class); + $this->artifactFile = Src\Helper\FilesystemHelper::createFileObject( + Src\Helper\FilesystemHelper::getNewTemporaryDirectory(), + 'build-artifact.json', + ); + $this->buildResult = new Src\Builder\BuildResult( + new Src\Builder\BuildInstructions( + self::$container->get('app.config'), + 'foo', + ), + $this->subject, + ); + $this->rootPackage = Src\Resource\Local\Composer::createComposer(dirname(__DIR__, 3))->getPackage(); + } + + /** + * @test + */ + public function buildGeneratesArtifact(): void + { + $writtenDirectory = $this->buildResult->getWrittenDirectory(); + $step = new Tests\Fixtures\DummyStep(); + $step->addProcessedFile( + new Src\Resource\Local\ProcessedFile( + Src\Helper\FilesystemHelper::createFileObject($writtenDirectory, 'foo.json'), + Src\Helper\FilesystemHelper::createFileObject($writtenDirectory, 'baz.json'), + ), + ); + + $this->buildResult->getInstructions()->addTemplateVariable('foo', 'baz'); + $this->buildResult->applyStep($step); + + $actual = $this->subject->build($this->artifactFile, $this->buildResult, $this->rootPackage); + + $expected = new Src\Builder\Artifact\Artifact( + new Src\Builder\Artifact\BuildArtifact( + Src\Builder\ArtifactGenerator::VERSION, + 'build-artifact.json', + $actual->artifact->date, + ), + new Src\Builder\Artifact\TemplateArtifact( + 'test', + $actual->template->hash, + new Src\Builder\Artifact\PackageArtifact( + 'foo/baz', + '1.0.0', + null, + null, + null, + ), + [ + 'name' => 'dummy', + 'url' => 'https://www.example.com', + ], + ), + new Src\Builder\Artifact\GeneratorArtifact( + new Src\Builder\Artifact\PackageArtifact( + $this->rootPackage->getName(), + $this->rootPackage->getPrettyVersion(), + $this->rootPackage->getSourceReference(), + $this->rootPackage->getSourceUrl(), + $this->rootPackage->getDistUrl(), + ), + 'composer', + ), + new Src\Builder\Artifact\ResultArtifact( + [ + 'foo' => 'baz', + ], + [ + [ + 'type' => 'dummy', + 'applied' => true, + ], + ], + [ + [ + 'source' => 'foo.json', + 'target' => 'baz.json', + ], + ], + ), + ); + + self::assertEquals($expected, $actual); + } +} diff --git a/tests/src/Builder/ArtifactReaderTest.php b/tests/src/Builder/ArtifactReaderTest.php new file mode 100644 index 00000000..cc6552d8 --- /dev/null +++ b/tests/src/Builder/ArtifactReaderTest.php @@ -0,0 +1,128 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Tests\Builder; + +use CPSIT\ProjectBuilder as Src; +use CPSIT\ProjectBuilder\Tests; + +use function dirname; + +/** + * ArtifactReaderTest. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + */ +final class ArtifactReaderTest extends Tests\ContainerAwareTestCase +{ + private string $artifactFile; + private Tests\Fixtures\DummyVersion $version; + private Src\Builder\ArtifactReader $subject; + + protected function setUp(): void + { + $this->artifactFile = dirname(__DIR__).'/Fixtures/Files/build-artifact.json'; + $this->version = self::$container->get(Tests\Fixtures\DummyVersion::class); + $this->subject = self::$container->get(Src\Builder\ArtifactReader::class); + } + + /** + * @test + */ + public function fromFileThrowsExceptionIfFileIsNotReadable(): void + { + $this->expectExceptionObject(Src\Exception\InvalidArtifactException::forFile('foo')); + + $this->subject->fromFile('foo'); + } + + /** + * @test + */ + public function fromFileThrowsExceptionIfJsonIsInvalid(): void + { + $file = dirname(__DIR__).'/Fixtures/Files/invalid-json.json'; + + $this->expectExceptionObject(Src\Exception\InvalidArtifactException::forFile($file)); + + $this->subject->fromFile($file); + } + + /** + * @test + */ + public function fromFileThrowsExceptionIfJsonIsUnsupported(): void + { + $file = dirname(__DIR__).'/Fixtures/Files/invalid-artifact.json'; + + $this->expectExceptionObject(Src\Exception\InvalidArtifactException::forFile($file)); + + $this->subject->fromFile($file); + } + + /** + * @test + */ + public function fromFileThrowsExceptionIfArtifactVersionIsInvalid(): void + { + $file = dirname(__DIR__).'/Fixtures/Files/invalid-artifact-version.json'; + + $this->expectExceptionObject(Src\Exception\InvalidArtifactException::forInvalidVersion()); + + $this->subject->fromFile($file); + } + + /** + * @test + */ + public function fromFileThrowsExceptionIfMigratedArtifactIsInvalid(): void + { + $this->version->remapArguments = [ + 'artifact', + null, + 'foo', + ]; + + $this->expectException(Src\Exception\InvalidArtifactException::class); + $this->expectExceptionCode(1677601857); + + $this->subject->fromFile($this->artifactFile); + } + + /** + * @test + */ + public function fromFilePerformsMigrations(): void + { + $this->version->remapArguments = [ + 'generator.executor', + null, + 'docker', + ]; + + $actual = $this->subject->fromFile($this->artifactFile); + + self::assertSame('docker', $actual->generator->executor); + } +} diff --git a/tests/src/Builder/BuildResultTest.php b/tests/src/Builder/BuildResultTest.php index 07a92639..3b9e2412 100644 --- a/tests/src/Builder/BuildResultTest.php +++ b/tests/src/Builder/BuildResultTest.php @@ -23,7 +23,6 @@ namespace CPSIT\ProjectBuilder\Tests\Builder; -use Composer\Package; use CPSIT\ProjectBuilder as Src; use CPSIT\ProjectBuilder\Tests; use Symfony\Component\Finder; @@ -48,7 +47,10 @@ protected function setUp(): void self::$container->get('app.config'), 'foo', ); - $this->subject = new Src\Builder\BuildResult($this->instructions); + $this->subject = new Src\Builder\BuildResult( + $this->instructions, + self::$container->get(Src\Builder\ArtifactGenerator::class), + ); } /** @@ -71,17 +73,27 @@ public function isMirroredReturnsMirrorState(): void /** * @test */ - public function getBuildArtifactReturnsBuildArtifact(): void + public function getArtifactFileReturnsArtifactFile(): void { - self::assertNull($this->subject->getBuildArtifact()); + self::assertNull($this->subject->getArtifactFile()); - $buildArtifact = new Src\Builder\Artifact\BuildArtifact( - 'foo.json', - $this->subject, - new Package\RootPackage('foo/baz', '1.0.0', '1.0.0'), - ); + $artifactFile = Src\Helper\FilesystemHelper::createFileObject('/foo', 'baz'); + + self::assertSame($artifactFile, $this->subject->setArtifactFile($artifactFile)->getArtifactFile()); + } + + /** + * @test + */ + public function getArtifactReturnsArtifact(): void + { + self::assertNull($this->subject->getArtifact()); + + $artifactFile = Src\Helper\FilesystemHelper::createFileObject('/foo', 'baz'); + + $this->subject->setArtifactFile($artifactFile); - self::assertSame($buildArtifact, $this->subject->setBuildArtifact($buildArtifact)->getBuildArtifact()); + self::assertInstanceOf(Src\Builder\Artifact\Artifact::class, $this->subject->getArtifact()); } /** diff --git a/tests/src/Builder/Generator/Step/CleanUpStepTest.php b/tests/src/Builder/Generator/Step/CleanUpStepTest.php index e6349c77..3d8dbb53 100644 --- a/tests/src/Builder/Generator/Step/CleanUpStepTest.php +++ b/tests/src/Builder/Generator/Step/CleanUpStepTest.php @@ -49,6 +49,7 @@ protected function setUp(): void self::$config, Src\Helper\FilesystemHelper::getNewTemporaryDirectory(), ), + self::$container->get(Src\Builder\ArtifactGenerator::class), ); } diff --git a/tests/src/Builder/Generator/Step/CollectBuildInstructionsStepTest.php b/tests/src/Builder/Generator/Step/CollectBuildInstructionsStepTest.php index 30872868..0dcdd5af 100644 --- a/tests/src/Builder/Generator/Step/CollectBuildInstructionsStepTest.php +++ b/tests/src/Builder/Generator/Step/CollectBuildInstructionsStepTest.php @@ -66,6 +66,7 @@ public function runAppliesNullAsDefaultValueOnSkippedProperties(): void $buildResult = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions($config, 'foo'), + self::$container->get(Src\Builder\ArtifactGenerator::class), ); $this->subject->run($buildResult); @@ -107,6 +108,7 @@ public function runAppliesNullAsDefaultValueOnSkippedSubProperties(): void $buildResult = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions($config, 'foo'), + self::$container->get(Src\Builder\ArtifactGenerator::class), ); $this->subject->run($buildResult); diff --git a/tests/src/Builder/Generator/Step/DumpBuildArtifactStepTest.php b/tests/src/Builder/Generator/Step/DumpBuildArtifactStepTest.php index 90b86fdd..3483dfae 100644 --- a/tests/src/Builder/Generator/Step/DumpBuildArtifactStepTest.php +++ b/tests/src/Builder/Generator/Step/DumpBuildArtifactStepTest.php @@ -27,7 +27,9 @@ use CPSIT\ProjectBuilder as Src; use CPSIT\ProjectBuilder\Tests; use Symfony\Component\Filesystem; +use Symfony\Component\Finder; +use function dirname; use function json_encode; /** @@ -40,8 +42,10 @@ final class DumpBuildArtifactStepTest extends Tests\ContainerAwareTestCase { private Filesystem\Filesystem $filesystem; private Src\Builder\Generator\Step\DumpBuildArtifactStep $subject; + private Src\Builder\ArtifactGenerator $artifactGenerator; private Src\Builder\BuildResult $buildResult; - private Src\Builder\Artifact\BuildArtifact $buildArtifact; + private Finder\SplFileInfo $artifactFile; + private Package\RootPackageInterface $rootPackage; protected function setUp(): void { @@ -50,48 +54,53 @@ protected function setUp(): void $this->filesystem, self::$container->get(Src\Builder\Writer\JsonFileWriter::class), ); + $this->artifactGenerator = self::$container->get(Src\Builder\ArtifactGenerator::class); $this->buildResult = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions(self::$config, 'foo'), + $this->artifactGenerator, ); - $this->buildArtifact = new Src\Builder\Artifact\BuildArtifact( - 'foo.json', - $this->buildResult, - new Package\RootPackage('foo/baz', '1.0.0', '1.0.0'), + $this->artifactFile = Src\Helper\FilesystemHelper::createFileObject( + $this->buildResult->getWrittenDirectory(), + '.build/build-artifact.json', ); + $this->rootPackage = Src\Resource\Local\Composer::createComposer(dirname(__DIR__, 5))->getPackage(); } /** * @test */ - public function runDoesNothingIfBuildArtifactWasNotGenerated(): void + public function runDoesNothingIfArtifactWasNotGenerated(): void { self::assertTrue($this->subject->run($this->buildResult)); self::assertFalse($this->buildResult->isStepApplied($this->subject)); - self::assertFileDoesNotExist($this->buildArtifact->getFile()->getPathname()); + self::assertFileDoesNotExist($this->artifactFile->getPathname()); } /** * @test */ - public function runDumpsBuildArtifact(): void + public function runDumpsArtifact(): void { - $this->buildResult->setBuildArtifact($this->buildArtifact); + $this->buildResult->setArtifactFile($this->artifactFile); self::assertTrue($this->subject->run($this->buildResult)); self::assertTrue($this->buildResult->isStepApplied($this->subject)); - self::assertFileExists($this->buildArtifact->getFile()->getPathname()); + self::assertFileExists($this->artifactFile->getPathname()); + + $artifact = $this->artifactGenerator->build($this->artifactFile, $this->buildResult, $this->rootPackage); + self::assertJsonStringEqualsJsonFile( - $this->buildArtifact->getFile()->getPathname(), - json_encode($this->buildArtifact, JSON_THROW_ON_ERROR), + $this->artifactFile->getPathname(), + json_encode($artifact, JSON_THROW_ON_ERROR), ); } /** * @test */ - public function revertDoesNothingIfBuildArtifactWasNotGenerated(): void + public function revertDoesNothingIfArtifactWasNotGenerated(): void { - $artifactPath = $this->buildArtifact->getFile()->getPathname(); + $artifactPath = $this->artifactFile->getPathname(); $this->filesystem->dumpFile($artifactPath, 'test'); @@ -107,11 +116,11 @@ public function revertDoesNothingIfBuildArtifactWasNotGenerated(): void /** * @test */ - public function revertRemovesDumpedBuildArtifact(): void + public function revertRemovesDumpedArtifact(): void { - $artifactPath = $this->buildArtifact->getFile()->getPathname(); + $artifactPath = $this->artifactFile->getPathname(); - $this->buildResult->setBuildArtifact($this->buildArtifact); + $this->buildResult->setArtifactFile($this->artifactFile); self::assertFileDoesNotExist($artifactPath); @@ -136,6 +145,6 @@ protected function tearDown(): void { parent::tearDown(); - $this->filesystem->remove($this->buildArtifact->getFile()->getPathname()); + $this->filesystem->remove($this->artifactFile->getPathname()); } } diff --git a/tests/src/Builder/Generator/Step/GenerateBuildArtifactStepTest.php b/tests/src/Builder/Generator/Step/GenerateBuildArtifactStepTest.php index 9da614e8..656150ee 100644 --- a/tests/src/Builder/Generator/Step/GenerateBuildArtifactStepTest.php +++ b/tests/src/Builder/Generator/Step/GenerateBuildArtifactStepTest.php @@ -27,6 +27,7 @@ use CPSIT\ProjectBuilder\Tests; use Generator; use Symfony\Component\Filesystem; +use Symfony\Component\Finder; /** * GenerateBuildArtifactStepTest. @@ -39,7 +40,7 @@ final class GenerateBuildArtifactStepTest extends Tests\ContainerAwareTestCase private Src\Builder\Generator\Step\GenerateBuildArtifactStep $subject; private Filesystem\Filesystem $filesystem; private Src\Builder\BuildResult $buildResult; - private string $artifactPath; + private Finder\SplFileInfo $artifactFile; protected function setUp(): void { @@ -47,8 +48,9 @@ protected function setUp(): void $this->filesystem = self::$container->get(Filesystem\Filesystem::class); $this->buildResult = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions(self::$config, 'foo'), + self::$container->get(Src\Builder\ArtifactGenerator::class), ); - $this->artifactPath = Filesystem\Path::join( + $this->artifactFile = Src\Helper\FilesystemHelper::createFileObject( $this->buildResult->getWrittenDirectory(), '.build/build-artifact.json', ); @@ -57,19 +59,19 @@ protected function setUp(): void /** * @test * - * @dataProvider runAsksForConfirmationIfBuildArtifactPathAlreadyExistsDataProvider + * @dataProvider runAsksForConfirmationIfArtifactPathAlreadyExistsDataProvider */ - public function runAsksForConfirmationIfBuildArtifactPathAlreadyExists(bool $continue, bool $expected): void + public function runAsksForConfirmationIfArtifactPathAlreadyExists(bool $continue, bool $expected): void { self::$io->setUserInputs([$continue ? 'yes' : 'no']); - $this->filesystem->dumpFile($this->artifactPath, 'test'); + $this->filesystem->dumpFile($this->artifactFile->getPathname(), 'test'); - self::assertFileExists($this->artifactPath); + self::assertFileExists($this->artifactFile->getPathname()); self::assertSame($expected, $this->subject->run($this->buildResult)); self::assertSame(!$expected, $this->subject->isStopped()); self::assertFalse($this->buildResult->isStepApplied($this->subject)); - self::assertNull($this->buildResult->getBuildArtifact()); + self::assertNull($this->buildResult->getArtifact()); self::assertStringContainsString( 'The build artifact cannot be generated because the resulting file already exists.', self::$io->getOutput(), @@ -79,17 +81,17 @@ public function runAsksForConfirmationIfBuildArtifactPathAlreadyExists(bool $con /** * @test */ - public function runGeneratesBuildArtifact(): void + public function runGeneratesArtifact(): void { self::assertTrue($this->subject->run($this->buildResult)); - self::assertInstanceOf(Src\Builder\Artifact\BuildArtifact::class, $this->buildResult->getBuildArtifact()); + self::assertInstanceOf(Src\Builder\Artifact\Artifact::class, $this->buildResult->getArtifact()); self::assertTrue($this->buildResult->isStepApplied($this->subject)); } /** * @return Generator */ - public function runAsksForConfirmationIfBuildArtifactPathAlreadyExistsDataProvider(): Generator + public function runAsksForConfirmationIfArtifactPathAlreadyExistsDataProvider(): Generator { yield 'continue' => [true, true]; yield 'do not continue' => [false, false]; @@ -99,6 +101,6 @@ protected function tearDown(): void { parent::tearDown(); - $this->filesystem->remove($this->artifactPath); + $this->filesystem->remove($this->artifactFile->getPathname()); } } diff --git a/tests/src/Builder/Generator/Step/InstallComposerDependenciesStepTest.php b/tests/src/Builder/Generator/Step/InstallComposerDependenciesStepTest.php index 2ee60a16..354bb878 100644 --- a/tests/src/Builder/Generator/Step/InstallComposerDependenciesStepTest.php +++ b/tests/src/Builder/Generator/Step/InstallComposerDependenciesStepTest.php @@ -49,6 +49,7 @@ protected function setUp(): void $this->subject = self::$container->get(Src\Builder\Generator\Step\InstallComposerDependenciesStep::class); $this->buildResult = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions(self::$config, 'foo'), + self::$container->get(Src\Builder\ArtifactGenerator::class), ); } diff --git a/tests/src/Builder/Generator/Step/ProcessSharedSourceFilesStepTest.php b/tests/src/Builder/Generator/Step/ProcessSharedSourceFilesStepTest.php index d5a6e392..71c9cd22 100644 --- a/tests/src/Builder/Generator/Step/ProcessSharedSourceFilesStepTest.php +++ b/tests/src/Builder/Generator/Step/ProcessSharedSourceFilesStepTest.php @@ -45,6 +45,7 @@ protected function setUp(): void $this->subject->setConfig($step); $this->result = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions(self::$config, 'foo'), + self::$container->get(Src\Builder\ArtifactGenerator::class), ); } diff --git a/tests/src/Builder/Generator/Step/ProcessSourceFilesStepTest.php b/tests/src/Builder/Generator/Step/ProcessSourceFilesStepTest.php index ece7c0bc..5b5e3f90 100644 --- a/tests/src/Builder/Generator/Step/ProcessSourceFilesStepTest.php +++ b/tests/src/Builder/Generator/Step/ProcessSourceFilesStepTest.php @@ -48,6 +48,7 @@ protected function setUp(): void $this->subject->setConfig($step); $this->result = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions(self::$config, 'foo'), + self::$container->get(Src\Builder\ArtifactGenerator::class), ); } @@ -93,6 +94,7 @@ public function runCanProcessTheSameSourceFileWithMultipleConditions( $result = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions($config, 'foo'), + self::$container->get(Src\Builder\ArtifactGenerator::class), ); $this->subject->setConfig($step); diff --git a/tests/src/Builder/Generator/Step/ShowNextStepsStepTest.php b/tests/src/Builder/Generator/Step/ShowNextStepsStepTest.php index fb63bacd..7bff1e83 100644 --- a/tests/src/Builder/Generator/Step/ShowNextStepsStepTest.php +++ b/tests/src/Builder/Generator/Step/ShowNextStepsStepTest.php @@ -44,6 +44,7 @@ protected function setUp(): void $this->subject = self::$container->get(Src\Builder\Generator\Step\ShowNextStepsStep::class); $this->result = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions(self::$config, 'foo'), + self::$container->get(Src\Builder\ArtifactGenerator::class), ); } diff --git a/tests/src/Event/BuildStepProcessedEventTest.php b/tests/src/Event/BuildStepProcessedEventTest.php index ca4e5af5..4e126d7b 100644 --- a/tests/src/Event/BuildStepProcessedEventTest.php +++ b/tests/src/Event/BuildStepProcessedEventTest.php @@ -43,6 +43,7 @@ protected function setUp(): void $this->step = new Tests\Fixtures\DummyStep(); $this->buildResult = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions(self::$config, 'foo'), + self::$container->get(Src\Builder\ArtifactGenerator::class), ); $this->subject = new Src\Event\BuildStepProcessedEvent( $this->step, diff --git a/tests/src/Event/BuildStepRevertedEventTest.php b/tests/src/Event/BuildStepRevertedEventTest.php index 9e6e4df6..c9eb7537 100644 --- a/tests/src/Event/BuildStepRevertedEventTest.php +++ b/tests/src/Event/BuildStepRevertedEventTest.php @@ -43,6 +43,7 @@ protected function setUp(): void $this->step = new Tests\Fixtures\DummyStep(); $this->buildResult = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions(self::$config, 'foo'), + self::$container->get(Src\Builder\ArtifactGenerator::class), ); $this->subject = new Src\Event\BuildStepRevertedEvent( $this->step, diff --git a/tests/src/Event/ProjectBuildFinishedEventTest.php b/tests/src/Event/ProjectBuildFinishedEventTest.php index b355ed50..9a0bcf2f 100644 --- a/tests/src/Event/ProjectBuildFinishedEventTest.php +++ b/tests/src/Event/ProjectBuildFinishedEventTest.php @@ -41,6 +41,7 @@ protected function setUp(): void { $this->buildResult = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions(self::$config, 'foo'), + self::$container->get(Src\Builder\ArtifactGenerator::class), ); $this->subject = new Src\Event\ProjectBuildFinishedEvent( $this->buildResult, diff --git a/tests/src/Exception/InvalidArtifactExceptionTest.php b/tests/src/Exception/InvalidArtifactExceptionTest.php new file mode 100644 index 00000000..7e181558 --- /dev/null +++ b/tests/src/Exception/InvalidArtifactExceptionTest.php @@ -0,0 +1,105 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Tests\Exception; + +use CPSIT\ProjectBuilder as Src; +use Opis\JsonSchema; +use PHPUnit\Framework\TestCase; + +/** + * InvalidArtifactExceptionTest. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + */ +final class InvalidArtifactExceptionTest extends TestCase +{ + /** + * @test + */ + public function forFileReturnExceptionForFile(): void + { + $actual = Src\Exception\InvalidArtifactException::forFile('foo'); + + self::assertSame('The artifact file "foo" is invalid and cannot be processed.', $actual->getMessage()); + self::assertSame(1677141460, $actual->getCode()); + } + + /** + * @test + */ + public function forPathReturnExceptionForPath(): void + { + $actual = Src\Exception\InvalidArtifactException::forPath('foo.baz'); + + self::assertSame('Invalid value at "foo.baz" in artifact.', $actual->getMessage()); + self::assertSame(1677140440, $actual->getCode()); + } + + /** + * @test + */ + public function forInvalidVersionReturnExceptionForInvalidVersion(): void + { + $actual = Src\Exception\InvalidArtifactException::forInvalidVersion(); + + self::assertSame('Unable to detect a valid artifact version.', $actual->getMessage()); + self::assertSame(1677141758, $actual->getCode()); + } + + /** + * @test + */ + public function forValidationErrorsReturnsExceptionForGivenValidationErrors(): void + { + $validationErrors = $this->generateValidationErrors(); + + $actual = Src\Exception\InvalidArtifactException::forValidationErrors($validationErrors); + + self::assertSame( + 'The artifact does not match the build artifact schema.'.PHP_EOL. + ' * Error at property path "/": The data (null) must match the type: object', + $actual->getMessage(), + ); + self::assertSame(1677601857, $actual->getCode()); + } + + private function generateValidationErrors(): JsonSchema\Errors\ValidationError + { + $schemaFile = dirname(__DIR__, 3).'/resources/build-artifact.schema.json'; + $schemaReference = 'file://'.$schemaFile; + + $resolver = new JsonSchema\Resolvers\SchemaResolver(); + $resolver->registerFile($schemaReference, $schemaFile); + + $validator = new JsonSchema\Validator(); + $validator->setResolver($resolver); + + $validationErrors = $validator->validate(null, $schemaReference)->error(); + + self::assertInstanceOf(JsonSchema\Errors\ValidationError::class, $validationErrors); + + return $validationErrors; + } +} diff --git a/tests/src/Fixtures/DummyVersion.php b/tests/src/Fixtures/DummyVersion.php new file mode 100644 index 00000000..a100f7c1 --- /dev/null +++ b/tests/src/Fixtures/DummyVersion.php @@ -0,0 +1,61 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace CPSIT\ProjectBuilder\Tests\Fixtures; + +use CPSIT\ProjectBuilder\Builder; + +/** + * DummyVersion. + * + * @author Elias Häußler + * @license GPL-3.0-or-later + * + * @internal + */ +final class DummyVersion extends Builder\Artifact\Migration\BaseVersion +{ + /** + * @var array{}|array{0: non-empty-string, 1?: non-empty-string|null, 2?: mixed} + */ + public array $remapArguments = []; + + public function migrate(array $artifact): array + { + if ([] !== $this->remapArguments) { + $this->remapValue($artifact, ...$this->remapArguments); + } + + return $artifact; + } + + public static function getSourceVersion(): int + { + return 1; + } + + public static function getTargetVersion(): int + { + return 2; + } +} diff --git a/tests/src/Fixtures/Files/build-artifact.json b/tests/src/Fixtures/Files/build-artifact.json new file mode 100644 index 00000000..79bef3bc --- /dev/null +++ b/tests/src/Fixtures/Files/build-artifact.json @@ -0,0 +1,79 @@ +{ + "artifact": { + "version": 1, + "file": "foo.json", + "date": 1679501401 + }, + "template": { + "identifier": "cpsit/project-builder-template-json", + "hash": "faa1988f89bdbabe1852e60338b83588e6c593c8", + "package": { + "name": "cpsit/project-builder-test-template", + "version": "1.0.0.0", + "sourceReference": null, + "sourceUrl": null, + "distUrl": null + }, + "provider": { + "name": "Packagist.org", + "url": "https://packagist.org" + } + }, + "generator": { + "package": { + "name": "cpsit/project-builder", + "version": "1.7.3.0", + "sourceReference": null, + "sourceUrl": null, + "distUrl": null + }, + "executor": "composer" + }, + "result": { + "properties": { + "bar": { + "name": "Hello World" + } + }, + "steps": [ + { + "type": "collectBuildInstructions", + "applied": true + }, + { + "type": "processSourceFiles", + "applied": true + }, + { + "type": "processSharedSourceFiles", + "applied": true + }, + { + "type": "generateBuildArtifact", + "applied": true + }, + { + "type": "mirrorProcessedFiles", + "applied": true + } + ], + "processedFiles": [ + { + "source": "dummy.json.twig", + "target": "dummy.json" + }, + { + "source": "dummy-4.json", + "target": "overrides/dummy-4.json" + }, + { + "source": "shared-dummy.json.twig", + "target": "shared-dummy.json" + }, + { + "source": "shared-dummy-4.json", + "target": "overrides/shared-dummy-4.json" + } + ] + } +} diff --git a/tests/src/Fixtures/Files/invalid-artifact-version.json b/tests/src/Fixtures/Files/invalid-artifact-version.json new file mode 100644 index 00000000..1b4d69c1 --- /dev/null +++ b/tests/src/Fixtures/Files/invalid-artifact-version.json @@ -0,0 +1,5 @@ +{ + "artifact": { + "version": "foo" + } +} diff --git a/tests/src/Fixtures/Files/invalid-artifact.json b/tests/src/Fixtures/Files/invalid-artifact.json new file mode 100644 index 00000000..bef6eaa1 --- /dev/null +++ b/tests/src/Fixtures/Files/invalid-artifact.json @@ -0,0 +1,4 @@ +[ + "foo", + "baz" +] diff --git a/tests/src/Fixtures/Files/invalid-json.json b/tests/src/Fixtures/Files/invalid-json.json new file mode 100644 index 00000000..257cc564 --- /dev/null +++ b/tests/src/Fixtures/Files/invalid-json.json @@ -0,0 +1 @@ +foo diff --git a/tests/src/Helper/ArrayHelperTest.php b/tests/src/Helper/ArrayHelperTest.php index 74e6612e..b3c43352 100644 --- a/tests/src/Helper/ArrayHelperTest.php +++ b/tests/src/Helper/ArrayHelperTest.php @@ -98,4 +98,48 @@ public function setValueByPathSetsValueAtGivenPath(): void $subject, ); } + + /** + * @test + */ + public function removeByPathDoesNothingIfGivenPathDoesNotExist(): void + { + $subject = [ + 'foo' => [ + 'bar' => 'hello world!', + ], + ]; + + Src\Helper\ArrayHelper::removeByPath($subject, 'foo.hello.world'); + + self::assertSame( + [ + 'foo' => [ + 'bar' => 'hello world!', + ], + ], + $subject, + ); + } + + /** + * @test + */ + public function removeByPathRemovesGivenPathInSubject(): void + { + $subject = [ + 'foo' => [ + 'bar' => 'hello world!', + ], + ]; + + Src\Helper\ArrayHelper::removeByPath($subject, 'foo.bar'); + + self::assertSame( + [ + 'foo' => [], + ], + $subject, + ); + } } From 9f9e7a057564514393230f5678da5fd0613a8fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=A4u=C3=9Fler?= Date: Wed, 22 Mar 2023 19:13:04 +0100 Subject: [PATCH 5/9] [BUGFIX] Move artifact generation to dumpBuildArtifact step --- src/Builder/BuildResult.php | 18 ------------------ src/Builder/Generator/Generator.php | 4 ++-- .../Generator/Step/DumpBuildArtifactStep.php | 9 +++++++-- tests/src/Builder/ArtifactGeneratorTest.php | 1 - tests/src/Builder/BuildResultTest.php | 15 --------------- .../Builder/Generator/Step/CleanUpStepTest.php | 1 - .../Step/CollectBuildInstructionsStepTest.php | 2 -- .../Step/DumpBuildArtifactStepTest.php | 4 ++-- .../Step/GenerateBuildArtifactStepTest.php | 5 ++--- .../InstallComposerDependenciesStepTest.php | 1 - .../Step/ProcessSharedSourceFilesStepTest.php | 1 - .../Step/ProcessSourceFilesStepTest.php | 2 -- .../Generator/Step/ShowNextStepsStepTest.php | 1 - .../src/Event/BuildStepProcessedEventTest.php | 1 - tests/src/Event/BuildStepRevertedEventTest.php | 1 - .../Event/ProjectBuildFinishedEventTest.php | 1 - 16 files changed, 13 insertions(+), 54 deletions(-) diff --git a/src/Builder/BuildResult.php b/src/Builder/BuildResult.php index a04c3a1e..6a9296ca 100644 --- a/src/Builder/BuildResult.php +++ b/src/Builder/BuildResult.php @@ -23,7 +23,6 @@ namespace CPSIT\ProjectBuilder\Builder; -use CPSIT\ProjectBuilder\Helper; use CPSIT\ProjectBuilder\Resource; use Symfony\Component\Filesystem; use Symfony\Component\Finder; @@ -48,7 +47,6 @@ final class BuildResult public function __construct( private readonly BuildInstructions $instructions, - private readonly ArtifactGenerator $artifactGenerator, ) { } @@ -84,15 +82,6 @@ public function setArtifactFile(?Finder\SplFileInfo $artifactFile): self return $this; } - public function getArtifact(): ?Artifact\Artifact - { - if (null !== $this->artifactFile) { - return $this->generateArtifact($this->artifactFile); - } - - return null; - } - /** * @return array */ @@ -149,11 +138,4 @@ public function getWrittenDirectory(): string return $this->instructions->getTemporaryDirectory(); } - - private function generateArtifact(Finder\SplFileInfo $artifactFile): Artifact\Artifact - { - $composer = Resource\Local\Composer::createComposer(Helper\FilesystemHelper::getProjectRootPath()); - - return $this->artifactGenerator->build($artifactFile, $this, $composer->getPackage()); - } } diff --git a/src/Builder/Generator/Generator.php b/src/Builder/Generator/Generator.php index dc6998cf..6f2b5f71 100644 --- a/src/Builder/Generator/Generator.php +++ b/src/Builder/Generator/Generator.php @@ -65,7 +65,7 @@ public function run(string $targetDirectory): Builder\BuildResult } $instructions = new Builder\BuildInstructions($this->config, $targetDirectory); - $result = new Builder\BuildResult($instructions, $this->artifactGenerator); + $result = new Builder\BuildResult($instructions); $this->eventDispatcher->dispatch(new Event\ProjectBuildStartedEvent($instructions)); @@ -106,7 +106,7 @@ public function run(string $targetDirectory): Builder\BuildResult public function dumpArtifact(Builder\BuildResult $result): void { - $step = new Builder\Generator\Step\DumpBuildArtifactStep($this->filesystem, $this->writer); + $step = new Builder\Generator\Step\DumpBuildArtifactStep($this->filesystem, $this->writer, $this->artifactGenerator); $step->run($result); } diff --git a/src/Builder/Generator/Step/DumpBuildArtifactStep.php b/src/Builder/Generator/Step/DumpBuildArtifactStep.php index 0e60387f..51d4b38c 100644 --- a/src/Builder/Generator/Step/DumpBuildArtifactStep.php +++ b/src/Builder/Generator/Step/DumpBuildArtifactStep.php @@ -24,6 +24,8 @@ namespace CPSIT\ProjectBuilder\Builder\Generator\Step; use CPSIT\ProjectBuilder\Builder; +use CPSIT\ProjectBuilder\Helper; +use CPSIT\ProjectBuilder\Resource; use Symfony\Component\Filesystem; /** @@ -41,6 +43,7 @@ final class DumpBuildArtifactStep extends AbstractStep public function __construct( private readonly Filesystem\Filesystem $filesystem, private readonly Builder\Writer\JsonFileWriter $writer, + private readonly Builder\ArtifactGenerator $artifactGenerator, ) { parent::__construct(); } @@ -48,14 +51,16 @@ public function __construct( public function run(Builder\BuildResult $buildResult): bool { $artifactFile = $buildResult->getArtifactFile(); - $artifact = $buildResult->getArtifact(); - if (null === $artifactFile || null === $artifact) { + if (null === $artifactFile) { return true; } $buildResult->applyStep($this); + $composer = Resource\Local\Composer::createComposer(Helper\FilesystemHelper::getProjectRootPath()); + $artifact = $this->artifactGenerator->build($artifactFile, $buildResult, $composer->getPackage()); + return $this->writer->write($artifactFile, $artifact); } diff --git a/tests/src/Builder/ArtifactGeneratorTest.php b/tests/src/Builder/ArtifactGeneratorTest.php index aabd7b43..f2fc3244 100644 --- a/tests/src/Builder/ArtifactGeneratorTest.php +++ b/tests/src/Builder/ArtifactGeneratorTest.php @@ -55,7 +55,6 @@ protected function setUp(): void self::$container->get('app.config'), 'foo', ), - $this->subject, ); $this->rootPackage = Src\Resource\Local\Composer::createComposer(dirname(__DIR__, 3))->getPackage(); } diff --git a/tests/src/Builder/BuildResultTest.php b/tests/src/Builder/BuildResultTest.php index 3b9e2412..b8a37750 100644 --- a/tests/src/Builder/BuildResultTest.php +++ b/tests/src/Builder/BuildResultTest.php @@ -49,7 +49,6 @@ protected function setUp(): void ); $this->subject = new Src\Builder\BuildResult( $this->instructions, - self::$container->get(Src\Builder\ArtifactGenerator::class), ); } @@ -82,20 +81,6 @@ public function getArtifactFileReturnsArtifactFile(): void self::assertSame($artifactFile, $this->subject->setArtifactFile($artifactFile)->getArtifactFile()); } - /** - * @test - */ - public function getArtifactReturnsArtifact(): void - { - self::assertNull($this->subject->getArtifact()); - - $artifactFile = Src\Helper\FilesystemHelper::createFileObject('/foo', 'baz'); - - $this->subject->setArtifactFile($artifactFile); - - self::assertInstanceOf(Src\Builder\Artifact\Artifact::class, $this->subject->getArtifact()); - } - /** * @test */ diff --git a/tests/src/Builder/Generator/Step/CleanUpStepTest.php b/tests/src/Builder/Generator/Step/CleanUpStepTest.php index 3d8dbb53..e6349c77 100644 --- a/tests/src/Builder/Generator/Step/CleanUpStepTest.php +++ b/tests/src/Builder/Generator/Step/CleanUpStepTest.php @@ -49,7 +49,6 @@ protected function setUp(): void self::$config, Src\Helper\FilesystemHelper::getNewTemporaryDirectory(), ), - self::$container->get(Src\Builder\ArtifactGenerator::class), ); } diff --git a/tests/src/Builder/Generator/Step/CollectBuildInstructionsStepTest.php b/tests/src/Builder/Generator/Step/CollectBuildInstructionsStepTest.php index 0dcdd5af..30872868 100644 --- a/tests/src/Builder/Generator/Step/CollectBuildInstructionsStepTest.php +++ b/tests/src/Builder/Generator/Step/CollectBuildInstructionsStepTest.php @@ -66,7 +66,6 @@ public function runAppliesNullAsDefaultValueOnSkippedProperties(): void $buildResult = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions($config, 'foo'), - self::$container->get(Src\Builder\ArtifactGenerator::class), ); $this->subject->run($buildResult); @@ -108,7 +107,6 @@ public function runAppliesNullAsDefaultValueOnSkippedSubProperties(): void $buildResult = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions($config, 'foo'), - self::$container->get(Src\Builder\ArtifactGenerator::class), ); $this->subject->run($buildResult); diff --git a/tests/src/Builder/Generator/Step/DumpBuildArtifactStepTest.php b/tests/src/Builder/Generator/Step/DumpBuildArtifactStepTest.php index 3483dfae..14688481 100644 --- a/tests/src/Builder/Generator/Step/DumpBuildArtifactStepTest.php +++ b/tests/src/Builder/Generator/Step/DumpBuildArtifactStepTest.php @@ -50,14 +50,14 @@ final class DumpBuildArtifactStepTest extends Tests\ContainerAwareTestCase protected function setUp(): void { $this->filesystem = self::$container->get(Filesystem\Filesystem::class); + $this->artifactGenerator = self::$container->get(Src\Builder\ArtifactGenerator::class); $this->subject = new Src\Builder\Generator\Step\DumpBuildArtifactStep( $this->filesystem, self::$container->get(Src\Builder\Writer\JsonFileWriter::class), + $this->artifactGenerator, ); - $this->artifactGenerator = self::$container->get(Src\Builder\ArtifactGenerator::class); $this->buildResult = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions(self::$config, 'foo'), - $this->artifactGenerator, ); $this->artifactFile = Src\Helper\FilesystemHelper::createFileObject( $this->buildResult->getWrittenDirectory(), diff --git a/tests/src/Builder/Generator/Step/GenerateBuildArtifactStepTest.php b/tests/src/Builder/Generator/Step/GenerateBuildArtifactStepTest.php index 656150ee..63825841 100644 --- a/tests/src/Builder/Generator/Step/GenerateBuildArtifactStepTest.php +++ b/tests/src/Builder/Generator/Step/GenerateBuildArtifactStepTest.php @@ -48,7 +48,6 @@ protected function setUp(): void $this->filesystem = self::$container->get(Filesystem\Filesystem::class); $this->buildResult = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions(self::$config, 'foo'), - self::$container->get(Src\Builder\ArtifactGenerator::class), ); $this->artifactFile = Src\Helper\FilesystemHelper::createFileObject( $this->buildResult->getWrittenDirectory(), @@ -71,7 +70,7 @@ public function runAsksForConfirmationIfArtifactPathAlreadyExists(bool $continue self::assertSame($expected, $this->subject->run($this->buildResult)); self::assertSame(!$expected, $this->subject->isStopped()); self::assertFalse($this->buildResult->isStepApplied($this->subject)); - self::assertNull($this->buildResult->getArtifact()); + self::assertNull($this->buildResult->getArtifactFile()); self::assertStringContainsString( 'The build artifact cannot be generated because the resulting file already exists.', self::$io->getOutput(), @@ -84,7 +83,7 @@ public function runAsksForConfirmationIfArtifactPathAlreadyExists(bool $continue public function runGeneratesArtifact(): void { self::assertTrue($this->subject->run($this->buildResult)); - self::assertInstanceOf(Src\Builder\Artifact\Artifact::class, $this->buildResult->getArtifact()); + self::assertEquals($this->artifactFile, $this->buildResult->getArtifactFile()); self::assertTrue($this->buildResult->isStepApplied($this->subject)); } diff --git a/tests/src/Builder/Generator/Step/InstallComposerDependenciesStepTest.php b/tests/src/Builder/Generator/Step/InstallComposerDependenciesStepTest.php index 354bb878..2ee60a16 100644 --- a/tests/src/Builder/Generator/Step/InstallComposerDependenciesStepTest.php +++ b/tests/src/Builder/Generator/Step/InstallComposerDependenciesStepTest.php @@ -49,7 +49,6 @@ protected function setUp(): void $this->subject = self::$container->get(Src\Builder\Generator\Step\InstallComposerDependenciesStep::class); $this->buildResult = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions(self::$config, 'foo'), - self::$container->get(Src\Builder\ArtifactGenerator::class), ); } diff --git a/tests/src/Builder/Generator/Step/ProcessSharedSourceFilesStepTest.php b/tests/src/Builder/Generator/Step/ProcessSharedSourceFilesStepTest.php index 71c9cd22..d5a6e392 100644 --- a/tests/src/Builder/Generator/Step/ProcessSharedSourceFilesStepTest.php +++ b/tests/src/Builder/Generator/Step/ProcessSharedSourceFilesStepTest.php @@ -45,7 +45,6 @@ protected function setUp(): void $this->subject->setConfig($step); $this->result = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions(self::$config, 'foo'), - self::$container->get(Src\Builder\ArtifactGenerator::class), ); } diff --git a/tests/src/Builder/Generator/Step/ProcessSourceFilesStepTest.php b/tests/src/Builder/Generator/Step/ProcessSourceFilesStepTest.php index 5b5e3f90..ece7c0bc 100644 --- a/tests/src/Builder/Generator/Step/ProcessSourceFilesStepTest.php +++ b/tests/src/Builder/Generator/Step/ProcessSourceFilesStepTest.php @@ -48,7 +48,6 @@ protected function setUp(): void $this->subject->setConfig($step); $this->result = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions(self::$config, 'foo'), - self::$container->get(Src\Builder\ArtifactGenerator::class), ); } @@ -94,7 +93,6 @@ public function runCanProcessTheSameSourceFileWithMultipleConditions( $result = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions($config, 'foo'), - self::$container->get(Src\Builder\ArtifactGenerator::class), ); $this->subject->setConfig($step); diff --git a/tests/src/Builder/Generator/Step/ShowNextStepsStepTest.php b/tests/src/Builder/Generator/Step/ShowNextStepsStepTest.php index 7bff1e83..fb63bacd 100644 --- a/tests/src/Builder/Generator/Step/ShowNextStepsStepTest.php +++ b/tests/src/Builder/Generator/Step/ShowNextStepsStepTest.php @@ -44,7 +44,6 @@ protected function setUp(): void $this->subject = self::$container->get(Src\Builder\Generator\Step\ShowNextStepsStep::class); $this->result = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions(self::$config, 'foo'), - self::$container->get(Src\Builder\ArtifactGenerator::class), ); } diff --git a/tests/src/Event/BuildStepProcessedEventTest.php b/tests/src/Event/BuildStepProcessedEventTest.php index 4e126d7b..ca4e5af5 100644 --- a/tests/src/Event/BuildStepProcessedEventTest.php +++ b/tests/src/Event/BuildStepProcessedEventTest.php @@ -43,7 +43,6 @@ protected function setUp(): void $this->step = new Tests\Fixtures\DummyStep(); $this->buildResult = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions(self::$config, 'foo'), - self::$container->get(Src\Builder\ArtifactGenerator::class), ); $this->subject = new Src\Event\BuildStepProcessedEvent( $this->step, diff --git a/tests/src/Event/BuildStepRevertedEventTest.php b/tests/src/Event/BuildStepRevertedEventTest.php index c9eb7537..9e6e4df6 100644 --- a/tests/src/Event/BuildStepRevertedEventTest.php +++ b/tests/src/Event/BuildStepRevertedEventTest.php @@ -43,7 +43,6 @@ protected function setUp(): void $this->step = new Tests\Fixtures\DummyStep(); $this->buildResult = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions(self::$config, 'foo'), - self::$container->get(Src\Builder\ArtifactGenerator::class), ); $this->subject = new Src\Event\BuildStepRevertedEvent( $this->step, diff --git a/tests/src/Event/ProjectBuildFinishedEventTest.php b/tests/src/Event/ProjectBuildFinishedEventTest.php index 9a0bcf2f..b355ed50 100644 --- a/tests/src/Event/ProjectBuildFinishedEventTest.php +++ b/tests/src/Event/ProjectBuildFinishedEventTest.php @@ -41,7 +41,6 @@ protected function setUp(): void { $this->buildResult = new Src\Builder\BuildResult( new Src\Builder\BuildInstructions(self::$config, 'foo'), - self::$container->get(Src\Builder\ArtifactGenerator::class), ); $this->subject = new Src\Event\ProjectBuildFinishedEvent( $this->buildResult, From 1c76f441a596668950e7f45b42e1b3eb65d3bbb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=A4u=C3=9Fler?= Date: Thu, 23 Mar 2023 16:43:15 +0100 Subject: [PATCH 6/9] [TASK] Use unique class names for artifact versions This implementation is similar to the one of Doctrine migrations. --- .../{Version1.php => Version1679497137.php} | 4 ++-- src/Builder/ArtifactReader.php | 22 +++++++++---------- ...ion1Test.php => Version1679497137Test.php} | 8 +++---- 3 files changed, 17 insertions(+), 17 deletions(-) rename src/Builder/Artifact/Migration/{Version1.php => Version1679497137.php} (95%) rename tests/src/Builder/Artifact/Migration/{Version1Test.php => Version1679497137Test.php} (91%) diff --git a/src/Builder/Artifact/Migration/Version1.php b/src/Builder/Artifact/Migration/Version1679497137.php similarity index 95% rename from src/Builder/Artifact/Migration/Version1.php rename to src/Builder/Artifact/Migration/Version1679497137.php index 23c1ac87..b7e8210a 100644 --- a/src/Builder/Artifact/Migration/Version1.php +++ b/src/Builder/Artifact/Migration/Version1679497137.php @@ -24,12 +24,12 @@ namespace CPSIT\ProjectBuilder\Builder\Artifact\Migration; /** - * Version1. + * Version1679497137. * * @author Elias Häußler * @license GPL-3.0-or-later */ -final class Version1 extends BaseVersion +final class Version1679497137 extends BaseVersion { public function migrate(array $artifact): array { diff --git a/src/Builder/ArtifactReader.php b/src/Builder/ArtifactReader.php index a1e7be39..bd537bf0 100644 --- a/src/Builder/ArtifactReader.php +++ b/src/Builder/ArtifactReader.php @@ -30,10 +30,13 @@ use CuyZ\Valinor; use DateTimeInterface; use Opis\JsonSchema; +use ReflectionObject; use Symfony\Component\Filesystem; use function array_filter; +use function array_values; use function file_get_contents; +use function implode; use function is_array; use function is_numeric; use function json_decode; @@ -181,22 +184,19 @@ private function createMapper(): Valinor\Mapper\TreeMapper private function orderVersions(iterable $versions): array { $prefixedVersions = []; - $orderedVersions = []; foreach ($versions as $version) { - $migrationPath = $version::getSourceVersion().'_'.$version::getTargetVersion(); - $prefixedVersions[$migrationPath] ??= []; - $prefixedVersions[$migrationPath][] = $version; + $reflectionObject = new ReflectionObject($version); + $versionIdentifier = implode('_', [ + $version::getSourceVersion(), + $version::getTargetVersion(), + $reflectionObject->getShortName(), + ]); + $prefixedVersions[$versionIdentifier] = $version; } ksort($prefixedVersions); - foreach ($prefixedVersions as $prefixedVersionList) { - foreach ($prefixedVersionList as $version) { - $orderedVersions[] = $version; - } - } - - return $orderedVersions; + return array_values($prefixedVersions); } } diff --git a/tests/src/Builder/Artifact/Migration/Version1Test.php b/tests/src/Builder/Artifact/Migration/Version1679497137Test.php similarity index 91% rename from tests/src/Builder/Artifact/Migration/Version1Test.php rename to tests/src/Builder/Artifact/Migration/Version1679497137Test.php index 97ef9f2d..c1828b53 100644 --- a/tests/src/Builder/Artifact/Migration/Version1Test.php +++ b/tests/src/Builder/Artifact/Migration/Version1679497137Test.php @@ -27,14 +27,14 @@ use PHPUnit\Framework\TestCase; /** - * Version1Test. + * Version1679497137Test. * * @author Elias Häußler * @license GPL-3.0-or-later */ -final class Version1Test extends TestCase +final class Version1679497137Test extends TestCase { - private Src\Builder\Artifact\Migration\Version1 $subject; + private Src\Builder\Artifact\Migration\Version1679497137 $subject; /** * @var array @@ -43,7 +43,7 @@ final class Version1Test extends TestCase protected function setUp(): void { - $this->subject = new Src\Builder\Artifact\Migration\Version1(); + $this->subject = new Src\Builder\Artifact\Migration\Version1679497137(); $this->artifact = [ 'artifact' => [ 'file' => 'foo', From 36d394b924548881226a0cc4303a1d736a1e483e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=A4u=C3=9Fler?= Date: Fri, 7 Jul 2023 15:23:23 +0200 Subject: [PATCH 7/9] [TASK] Rename "artifact migration version" to "artifact migration" --- config/services.php | 4 +- config/services.yaml | 2 +- .../{BaseVersion.php => BaseMigration.php} | 4 +- .../Migration/{Version.php => Migration.php} | 4 +- ...1679497137.php => Migration1679497137.php} | 4 +- src/Builder/ArtifactReader.php | 40 +++++++++---------- tests/config/services.yaml | 4 +- ...eVersionTest.php => BaseMigrationTest.php} | 8 ++-- ...37Test.php => Migration1679497137Test.php} | 8 ++-- tests/src/Builder/ArtifactReaderTest.php | 8 ++-- .../{DummyVersion.php => DummyMigration.php} | 4 +- 11 files changed, 45 insertions(+), 45 deletions(-) rename src/Builder/Artifact/Migration/{BaseVersion.php => BaseMigration.php} (96%) rename src/Builder/Artifact/Migration/{Version.php => Migration.php} (97%) rename src/Builder/Artifact/Migration/{Version1679497137.php => Migration1679497137.php} (95%) rename tests/src/Builder/Artifact/Migration/{BaseVersionTest.php => BaseMigrationTest.php} (94%) rename tests/src/Builder/Artifact/Migration/{Version1679497137Test.php => Migration1679497137Test.php} (86%) rename tests/src/Fixtures/{DummyVersion.php => DummyMigration.php} (94%) diff --git a/config/services.php b/config/services.php index 5109ea12..ee793e51 100644 --- a/config/services.php +++ b/config/services.php @@ -43,8 +43,8 @@ DependencyInjection\Loader\Configurator\ContainerConfigurator $configurator, DependencyInjection\ContainerBuilder $container, ): void { - $container->registerForAutoconfiguration(Builder\Artifact\Migration\Version::class) - ->addTag('artifact.version_migration') + $container->registerForAutoconfiguration(Builder\Artifact\Migration\Migration::class) + ->addTag('artifact.migration') ; $container->registerForAutoconfiguration(Builder\Writer\WriterInterface::class) ->addTag('builder.writer') diff --git a/config/services.yaml b/config/services.yaml index d1c9fe92..1f132d53 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -23,7 +23,7 @@ services: CPSIT\ProjectBuilder\Builder\ArtifactReader: arguments: - $versions: !tagged_iterator artifact.version_migration + $migrations: !tagged_iterator artifact.migration CPSIT\ProjectBuilder\Builder\Config\Config: alias: 'app.config' diff --git a/src/Builder/Artifact/Migration/BaseVersion.php b/src/Builder/Artifact/Migration/BaseMigration.php similarity index 96% rename from src/Builder/Artifact/Migration/BaseVersion.php rename to src/Builder/Artifact/Migration/BaseMigration.php index 1215096d..989d2efe 100644 --- a/src/Builder/Artifact/Migration/BaseVersion.php +++ b/src/Builder/Artifact/Migration/BaseMigration.php @@ -28,12 +28,12 @@ use function is_callable; /** - * BaseVersion. + * BaseMigration. * * @author Elias Häußler * @license GPL-3.0-or-later */ -abstract class BaseVersion implements Version +abstract class BaseMigration implements Migration { /** * @param array $artifact diff --git a/src/Builder/Artifact/Migration/Version.php b/src/Builder/Artifact/Migration/Migration.php similarity index 97% rename from src/Builder/Artifact/Migration/Version.php rename to src/Builder/Artifact/Migration/Migration.php index 2986fca9..d1f6473b 100644 --- a/src/Builder/Artifact/Migration/Version.php +++ b/src/Builder/Artifact/Migration/Migration.php @@ -24,14 +24,14 @@ namespace CPSIT\ProjectBuilder\Builder\Artifact\Migration; /** - * Version. + * Migration. * * @author Elias Häußler * @license GPL-3.0-or-later * * @internal */ -interface Version +interface Migration { /** * @param array $artifact diff --git a/src/Builder/Artifact/Migration/Version1679497137.php b/src/Builder/Artifact/Migration/Migration1679497137.php similarity index 95% rename from src/Builder/Artifact/Migration/Version1679497137.php rename to src/Builder/Artifact/Migration/Migration1679497137.php index b7e8210a..652d7990 100644 --- a/src/Builder/Artifact/Migration/Version1679497137.php +++ b/src/Builder/Artifact/Migration/Migration1679497137.php @@ -24,12 +24,12 @@ namespace CPSIT\ProjectBuilder\Builder\Artifact\Migration; /** - * Version1679497137. + * Migration1679497137. * * @author Elias Häußler * @license GPL-3.0-or-later */ -final class Version1679497137 extends BaseVersion +final class Migration1679497137 extends BaseMigration { public function migrate(array $artifact): array { diff --git a/src/Builder/ArtifactReader.php b/src/Builder/ArtifactReader.php index bd537bf0..7c8658d9 100644 --- a/src/Builder/ArtifactReader.php +++ b/src/Builder/ArtifactReader.php @@ -54,20 +54,20 @@ final class ArtifactReader private readonly Valinor\Mapper\TreeMapper $mapper; /** - * @var list + * @var list */ - private readonly array $versions; + private readonly array $migrations; /** - * @param iterable $versions + * @param iterable $migrations */ public function __construct( - iterable $versions, + iterable $migrations, private readonly Filesystem\Filesystem $filesystem, private readonly Json\SchemaValidator $schemaValidator, ) { $this->mapper = $this->createMapper(); - $this->versions = $this->orderVersions($versions); + $this->migrations = $this->orderMigrations($migrations); } /** @@ -141,9 +141,9 @@ private function performMigrations(array $artifact): array $migrationPath = range($artifactVersion, ArtifactGenerator::VERSION); foreach ($migrationPath as $sourceVersion) { - foreach ($this->versions as $version) { - if ($sourceVersion === $version::getSourceVersion()) { - $artifact = $version->migrate($artifact); + foreach ($this->migrations as $migration) { + if ($sourceVersion === $migration::getSourceVersion()) { + $artifact = $migration->migrate($artifact); } } } @@ -177,26 +177,26 @@ private function createMapper(): Valinor\Mapper\TreeMapper } /** - * @param iterable $versions + * @param iterable $migrations * - * @return list + * @return list */ - private function orderVersions(iterable $versions): array + private function orderMigrations(iterable $migrations): array { - $prefixedVersions = []; + $prefixedMigrations = []; - foreach ($versions as $version) { - $reflectionObject = new ReflectionObject($version); - $versionIdentifier = implode('_', [ - $version::getSourceVersion(), - $version::getTargetVersion(), + foreach ($migrations as $migration) { + $reflectionObject = new ReflectionObject($migration); + $migrationIdentifier = implode('_', [ + $migration::getSourceVersion(), + $migration::getTargetVersion(), $reflectionObject->getShortName(), ]); - $prefixedVersions[$versionIdentifier] = $version; + $prefixedMigrations[$migrationIdentifier] = $migration; } - ksort($prefixedVersions); + ksort($prefixedMigrations); - return array_values($prefixedVersions); + return array_values($prefixedMigrations); } } diff --git a/tests/config/services.yaml b/tests/config/services.yaml index a2768108..c8220309 100644 --- a/tests/config/services.yaml +++ b/tests/config/services.yaml @@ -17,5 +17,5 @@ services: CPSIT\ProjectBuilder\Tests\Fixtures\DummyTemplateRenderingEventListener: tags: ['event.listener'] - CPSIT\ProjectBuilder\Tests\Fixtures\DummyVersion: - tags: ['artifact.version_migration'] + CPSIT\ProjectBuilder\Tests\Fixtures\DummyMigration: + tags: ['artifact.migration'] diff --git a/tests/src/Builder/Artifact/Migration/BaseVersionTest.php b/tests/src/Builder/Artifact/Migration/BaseMigrationTest.php similarity index 94% rename from tests/src/Builder/Artifact/Migration/BaseVersionTest.php rename to tests/src/Builder/Artifact/Migration/BaseMigrationTest.php index 44f6c31c..a661cd13 100644 --- a/tests/src/Builder/Artifact/Migration/BaseVersionTest.php +++ b/tests/src/Builder/Artifact/Migration/BaseMigrationTest.php @@ -30,14 +30,14 @@ use function strrev; /** - * BaseVersionTest. + * BaseMigrationTest. * * @author Elias Häußler * @license GPL-3.0-or-later */ -final class BaseVersionTest extends Framework\TestCase +final class BaseMigrationTest extends Framework\TestCase { - private Tests\Fixtures\DummyVersion $subject; + private Tests\Fixtures\DummyMigration $subject; /** * @var array @@ -46,7 +46,7 @@ final class BaseVersionTest extends Framework\TestCase protected function setUp(): void { - $this->subject = new Tests\Fixtures\DummyVersion(); + $this->subject = new Tests\Fixtures\DummyMigration(); $this->artifact = [ 'foo' => [ 'baz' => 'hello world', diff --git a/tests/src/Builder/Artifact/Migration/Version1679497137Test.php b/tests/src/Builder/Artifact/Migration/Migration1679497137Test.php similarity index 86% rename from tests/src/Builder/Artifact/Migration/Version1679497137Test.php rename to tests/src/Builder/Artifact/Migration/Migration1679497137Test.php index debb1fae..6906400e 100644 --- a/tests/src/Builder/Artifact/Migration/Version1679497137Test.php +++ b/tests/src/Builder/Artifact/Migration/Migration1679497137Test.php @@ -27,14 +27,14 @@ use PHPUnit\Framework; /** - * Version1679497137Test. + * Migration1679497137Test. * * @author Elias Häußler * @license GPL-3.0-or-later */ -final class Version1679497137Test extends Framework\TestCase +final class Migration1679497137Test extends Framework\TestCase { - private Src\Builder\Artifact\Migration\Version1679497137 $subject; + private Src\Builder\Artifact\Migration\Migration1679497137 $subject; /** * @var array @@ -43,7 +43,7 @@ final class Version1679497137Test extends Framework\TestCase protected function setUp(): void { - $this->subject = new Src\Builder\Artifact\Migration\Version1679497137(); + $this->subject = new Src\Builder\Artifact\Migration\Migration1679497137(); $this->artifact = [ 'artifact' => [ 'file' => 'foo', diff --git a/tests/src/Builder/ArtifactReaderTest.php b/tests/src/Builder/ArtifactReaderTest.php index 68ae5c3a..3431f366 100644 --- a/tests/src/Builder/ArtifactReaderTest.php +++ b/tests/src/Builder/ArtifactReaderTest.php @@ -38,13 +38,13 @@ final class ArtifactReaderTest extends Tests\ContainerAwareTestCase { private string $artifactFile; - private Tests\Fixtures\DummyVersion $version; + private Tests\Fixtures\DummyMigration $migration; private Src\Builder\ArtifactReader $subject; protected function setUp(): void { $this->artifactFile = dirname(__DIR__).'/Fixtures/Files/build-artifact.json'; - $this->version = self::$container->get(Tests\Fixtures\DummyVersion::class); + $this->migration = self::$container->get(Tests\Fixtures\DummyMigration::class); $this->subject = self::$container->get(Src\Builder\ArtifactReader::class); } @@ -89,7 +89,7 @@ public function fromFileThrowsExceptionIfArtifactVersionIsInvalid(): void #[Framework\Attributes\Test] public function fromFileThrowsExceptionIfMigratedArtifactIsInvalid(): void { - $this->version->remapArguments = [ + $this->migration->remapArguments = [ 'artifact', null, 'foo', @@ -104,7 +104,7 @@ public function fromFileThrowsExceptionIfMigratedArtifactIsInvalid(): void #[Framework\Attributes\Test] public function fromFilePerformsMigrations(): void { - $this->version->remapArguments = [ + $this->migration->remapArguments = [ 'generator.executor', null, 'docker', diff --git a/tests/src/Fixtures/DummyVersion.php b/tests/src/Fixtures/DummyMigration.php similarity index 94% rename from tests/src/Fixtures/DummyVersion.php rename to tests/src/Fixtures/DummyMigration.php index a100f7c1..faffea39 100644 --- a/tests/src/Fixtures/DummyVersion.php +++ b/tests/src/Fixtures/DummyMigration.php @@ -26,14 +26,14 @@ use CPSIT\ProjectBuilder\Builder; /** - * DummyVersion. + * DummyMigration. * * @author Elias Häußler * @license GPL-3.0-or-later * * @internal */ -final class DummyVersion extends Builder\Artifact\Migration\BaseVersion +final class DummyMigration extends Builder\Artifact\Migration\BaseMigration { /** * @var array{}|array{0: non-empty-string, 1?: non-empty-string|null, 2?: mixed} From a44f6a6e1d2d125ebda2c30de7c66a3ee2d2179a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=A4u=C3=9Fler?= Date: Fri, 7 Jul 2023 15:30:46 +0200 Subject: [PATCH 8/9] [TASK] Don't validate generated artifact --- src/Builder/ArtifactGenerator.php | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/src/Builder/ArtifactGenerator.php b/src/Builder/ArtifactGenerator.php index 59d6ea25..535fa392 100644 --- a/src/Builder/ArtifactGenerator.php +++ b/src/Builder/ArtifactGenerator.php @@ -24,16 +24,11 @@ namespace CPSIT\ProjectBuilder\Builder; use Composer\Package; -use CPSIT\ProjectBuilder\Exception; -use CPSIT\ProjectBuilder\Json; -use CPSIT\ProjectBuilder\Paths; use CPSIT\ProjectBuilder\Resource; use Symfony\Component\Finder; use function array_map; use function getenv; -use function json_decode; -use function json_encode; /** * ArtifactGenerator. @@ -47,37 +42,18 @@ final class ArtifactGenerator { public const VERSION = 1; - public function __construct( - private readonly Json\SchemaValidator $schemaValidator, - ) { - } - - /** - * @throws Exception\InvalidArtifactException - */ public function build( Finder\SplFileInfo $file, BuildResult $buildResult, Package\RootPackageInterface $rootPackage, int $version = self::VERSION, ): Artifact\Artifact { - // Generate artifact - $artifact = new Artifact\Artifact( + return new Artifact\Artifact( $this->generateBuildArtifact($file, $version), $this->generateTemplateArtifact($buildResult), $this->generateGeneratorArtifact($rootPackage), $this->generateResultArtifact($buildResult), ); - - // Validate generated artifact - $json = json_decode((string) json_encode($artifact)); - $validationResult = $this->schemaValidator->validate($json, Paths::BUILD_ARTIFACT_SCHEMA); - - if (!$validationResult->isValid()) { - throw Exception\InvalidArtifactException::forValidationErrors($validationResult->error()); - } - - return $artifact; } private function generateBuildArtifact(Finder\SplFileInfo $file, int $version): Artifact\BuildArtifact From 7a22509c4555a5e157b24d1239869d6c33bbe041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=A4u=C3=9Fler?= Date: Fri, 7 Jul 2023 15:34:44 +0200 Subject: [PATCH 9/9] [BUGFIX] Streamline used version in generated package artifacts --- src/Builder/ArtifactGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Builder/ArtifactGenerator.php b/src/Builder/ArtifactGenerator.php index 535fa392..681ffa5f 100644 --- a/src/Builder/ArtifactGenerator.php +++ b/src/Builder/ArtifactGenerator.php @@ -136,7 +136,7 @@ private function generatePackageArtifact(Package\PackageInterface $package): Art { return new Artifact\PackageArtifact( $package->getName(), - $package->getVersion(), + $package->getPrettyVersion(), $package->getSourceReference(), $package->getSourceUrl(), $package->getDistUrl(),