Skip to content

Commit

Permalink
[FEATURE] Introduce build artifacts for created projects
Browse files Browse the repository at this point in the history
Resolves: #12
  • Loading branch information
eliashaeussler committed Jan 23, 2023
1 parent a72501b commit ec86f0c
Show file tree
Hide file tree
Showing 13 changed files with 484 additions and 6 deletions.
4 changes: 0 additions & 4 deletions config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,6 @@

$services = $configurator->services();

// Disable clean-up step since it's an internal step that should
// not be able to be referenced from template configuration.
$services->remove(Builder\Generator\Step\CleanUpStep::class);

// Add external services
$services->set(ExpressionLanguage\ExpressionLanguage::class);
$services->set(Filesystem\Filesystem::class);
Expand Down
3 changes: 3 additions & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ services:
resource: '../src/*'
exclude:
- '../src/Builder/Config/*'
- '../src/Builder/Generator/Step/CleanUpStep.php'
- '../src/Builder/Generator/Step/DumpBuildArtifactStep.php'
- '../src/Builder/BuildArtifact.php'
- '../src/Builder/BuildInstructions.php'
- '../src/Builder/BuildResult.php'
- '../src/Console/*'
Expand Down
19 changes: 19 additions & 0 deletions resources/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,25 @@
}
}
},
{
"properties": {
"type": {
"const": "generateBuildArtifact"
},
"options": {
"type": "object",
"properties": {
"artifactPath": {
"type": "string",
"title": "Path to build artifact in generated project",
"description": "Must be a JSON file, relative to the project root",
"default": ".build/build-artifact.json",
"pattern": "\\.json$"
}
}
}
}
},
{
"properties": {
"type": {
Expand Down
157 changes: 157 additions & 0 deletions src/Builder/BuildArtifact.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Composer package "cpsit/project-builder".
*
* Copyright (C) 2023 Elias Häußler <e.haeussler@familie-redlich.de>
*
* 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 <https://www.gnu.org/licenses/>.
*/

namespace CPSIT\ProjectBuilder\Builder;

use Composer\Package;
use CPSIT\ProjectBuilder\Resource;
use JsonSerializable;
use Symfony\Component\Finder;

use function array_map;
use function time;

/**
* BuildArtifact.
*
* @author Elias Häußler <e.haeussler@familie-redlich.de>
* @license GPL-3.0-or-later
*
* @internal
*
* @phpstan-type TBuildArtifact array{ artifact: TArtifact, template: TTemplateArtifact, generator: TGeneratorArtifact, result: TResultArtifact}
* @phpstan-type TArtifact array{version: int, path: string, date: int}
* @phpstan-type TPackageArtifact array{name: string, version: string, sourceReference: string|null, sourceUrl: string|null, distUrl: string|null}
* @phpstan-type TTemplateArtifact array{identifier: string, hash: string, package: TPackageArtifact, provider: array{name: string, url: string}}
* @phpstan-type TGeneratorArtifact array{package: TPackageArtifact}
* @phpstan-type TResultArtifact array{properties: array<string, mixed>, steps: list<array{type: string, applied: bool}>, processedFiles: list<array{source: string, target: string}>}
*/
final class BuildArtifact implements JsonSerializable
{
private const VERSION = 1;

public function __construct(
private Finder\SplFileInfo $path,
private BuildResult $buildResult,
private Package\RootPackageInterface $rootPackage,
) {
}

public function getPath(): Finder\SplFileInfo
{
return $this->path;
}

/**
* @phpstan-return TBuildArtifact
*/
public function dump(): array
{
return [
'artifact' => [
'version' => self::VERSION,
'path' => $this->path->getRelativePathname(),
'date' => time(),
],
'template' => $this->buildTemplateArtifact(),
'generator' => $this->buildGeneratorArtifact(),
'result' => $this->buildResultArtifact(),
];
}

/**
* @phpstan-return TTemplateArtifact
*/
private function buildTemplateArtifact(): array
{
$config = $this->buildResult->getInstructions()->getConfig();
$package = $config->getSource()->getPackage();
$provider = $config->getSource()->getProvider();

return [
'identifier' => $config->getIdentifier(),
'hash' => $config->buildHash(),
'package' => $this->decoratePackage($package),
'provider' => [
'name' => $provider::getName(),
'url' => $provider->getUrl(),
],
];
}

/**
* @phpstan-return TGeneratorArtifact
*/
private function buildGeneratorArtifact(): array
{
return [
'package' => $this->decoratePackage($this->rootPackage),
];
}

/**
* @phpstan-return TResultArtifact
*/
private function buildResultArtifact(): array
{
return [
'properties' => $this->buildResult->getInstructions()->getTemplateVariables(),
'steps' => array_map(
fn (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(),
),
];
}

/**
* @phpstan-return TPackageArtifact
*/
private function decoratePackage(Package\PackageInterface $package): array
{
return [
'name' => $package->getName(),
'version' => $package->getVersion(),
'sourceReference' => $package->getSourceReference(),
'sourceUrl' => $package->getSourceUrl(),
'distUrl' => $package->getDistUrl(),
];
}

/**
* @phpstan-return TBuildArtifact
*/
public function jsonSerialize(): array
{
return $this->dump();
}
}
13 changes: 13 additions & 0 deletions src/Builder/BuildResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
final class BuildResult
{
private bool $mirrored = false;
private ?BuildArtifact $buildArtifact = null;

/**
* @var array<string, Generator\Step\StepInterface>
Expand Down Expand Up @@ -65,6 +66,18 @@ public function setMirrored(bool $mirrored): self
return $this;
}

public function getBuildArtifact(): ?BuildArtifact
{
return $this->buildArtifact;
}

public function setBuildArtifact(BuildArtifact $buildArtifact): self
{
$this->buildArtifact = $buildArtifact;

return $this;
}

/**
* @return array<string, Generator\Step\StepInterface>
*/
Expand Down
36 changes: 36 additions & 0 deletions src/Builder/Config/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
namespace CPSIT\ProjectBuilder\Builder\Config;

use CPSIT\ProjectBuilder\Exception;
use CPSIT\ProjectBuilder\Template;

use function serialize;
use function sha1;

/**
* Config.
Expand All @@ -34,6 +38,7 @@
final class Config
{
private ?string $declaringFile = null;
private ?Template\TemplateSource $source = null;

/**
* @param non-empty-list<ValueObject\Step> $steps
Expand Down Expand Up @@ -88,4 +93,35 @@ public function setDeclaringFile(?string $declaringFile): self

return $this;
}

public function getSource(): Template\TemplateSource
{
if (null === $this->source) {
throw Exception\InvalidConfigurationException::forUnknownSource($this->identifier);
}

return $this->source;
}

public function setSource(Template\TemplateSource $source): self
{
$this->source = $source;

return $this;
}

/**
* @internal
*/
public function buildHash(): string
{
return sha1(
serialize([
'identifier' => $this->identifier,
'name' => $this->name,
'steps' => $this->steps,
'properties' => $this->properties,
]),
);
}
}
6 changes: 6 additions & 0 deletions src/Builder/Config/ValueObject/StepOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ final class StepOptions
public function __construct(
private array $fileConditions = [],
private ?string $templateFile = null,
private ?string $artifactPath = null,
) {
}

Expand All @@ -52,4 +53,9 @@ public function getTemplateFile(): ?string
{
return $this->templateFile;
}

public function getArtifactPath(): ?string
{
return $this->artifactPath;
}
}
12 changes: 10 additions & 2 deletions src/Builder/Generator/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public function __construct(
private Builder\Generator\Step\StepFactory $stepFactory,
private Filesystem\Filesystem $filesystem,
private EventDispatcher\EventDispatcherInterface $eventDispatcher,
private Builder\Writer\JsonFileWriter $writer,
) {
}

Expand Down Expand Up @@ -104,8 +105,15 @@ public function run(string $targetDirectory): Builder\BuildResult

public function cleanUp(Builder\BuildResult $result): void
{
$step = new Builder\Generator\Step\CleanUpStep($this->filesystem);
$step->run($result);
/** @var list<Builder\Generator\Step\StepInterface> $steps */
$steps = [
new Builder\Generator\Step\CleanUpStep($this->filesystem),
new Builder\Generator\Step\DumpBuildArtifactStep($this->filesystem, $this->writer),
];

foreach ($steps as $step) {
$step->run($result);
}
}

private function handleStepFailure(
Expand Down
Loading

0 comments on commit ec86f0c

Please sign in to comment.