refactor: use Illuminate Pipelines

This commit is contained in:
Oliver Davies 2023-04-25 00:16:21 +01:00
parent 830c37c681
commit d19fcbdede
7 changed files with 343 additions and 211 deletions

View file

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace App\Action;
use Symfony\Component\Yaml\Yaml;
final class CreateFinalConfigurationData
{
public function handle(string $configFile, \Closure $next) {
$configurationData = array_merge(
Yaml::parseFile(filename: __DIR__ . '/../../resources/build.defaults.yaml'),
Yaml::parseFile(filename: $configFile),
);
if (isset($configurationData['docker-compose'])) {
$configurationData['dockerCompose'] = $configurationData['docker-compose'];
$configurationData['docker-compose'] = null;
}
$configurationData['managedText'] = 'Do not edit this file. It is automatically generated by https://www.oliverdavies.uk/build-configs.';
return $next($configurationData);
}
}

View file

@ -0,0 +1,134 @@
<?php
declare(strict_types=1);
namespace App\Action;
use App\DataTransferObject\TemplateFile;
use App\Enum\Language;
use App\Enum\WebServer;
use Illuminate\Support\Arr;
final class CreateListOfFilesToGenerate
{
public function handle(array $configurationData, \Closure $next) {
/** @var Collection<int, TemplateFile> */
$filesToGenerate = collect([
new TemplateFile(data: 'common/.dockerignore', name: '.dockerignore'),
new TemplateFile(data: 'common/.hadolint.yaml', name: '.hadolint.yaml'),
new TemplateFile(data: 'env.example', name: '.env.example'),
]);
$extraDatabases = Arr::get($configurationData, 'database.extra_databases', []);
if (count($extraDatabases) > 0) {
$filesToGenerate[] = new TemplateFile(
data: 'extra-databases.sql',
name: 'extra-databases.sql',
path: 'tools/docker/images/database/root/docker-entrypoint-initdb.d',
);
}
if (false !== Arr::get($configurationData, "justfile", true)) {
$filesToGenerate[] = new TemplateFile(data: 'justfile', name: 'justfile');
}
if (isset($configurationData['dockerCompose']) && $configurationData['dockerCompose'] !== null) {
$filesToGenerate[] = new TemplateFile(data: 'docker-compose.yaml', name: 'docker-compose.yaml');
}
if (static::isPhp(Arr::get($configurationData, 'language'))) {
$filesToGenerate[] = new TemplateFile(data: 'php/Dockerfile', name: 'Dockerfile');
$filesToGenerate[] = new TemplateFile(data: 'php/phpcs.xml', name: 'phpcs.xml.dist');
$filesToGenerate[] = new TemplateFile(data: 'php/phpunit.xml', name: 'phpunit.xml.dist');
$filesToGenerate[] = new TemplateFile(
data: 'php/docker-entrypoint-php',
name: 'docker-entrypoint-php',
path: 'tools/docker/images/php/root/usr/local/bin',
);
if (Arr::has(array: $configurationData, keys: 'php.phpstan')) {
$filesToGenerate[] = new TemplateFile(data: 'php/phpstan.neon', name: 'phpstan.neon.dist');
}
}
if (static::isNode(Arr::get($configurationData, 'language'))) {
$filesToGenerate[] = new TemplateFile(data: 'node/.yarnrc', name: '.yarnrc');
$filesToGenerate[] = new TemplateFile(data: 'node/Dockerfile', name: 'Dockerfile');
}
if (static::isCaddy(Arr::get($configurationData, 'web.type'))) {
$filesToGenerate[] = new TemplateFile(
data: 'web/caddy/Caddyfile',
name: 'Caddyfile',
path: 'tools/docker/images/web/root/etc/caddy',
);
}
if (static::isNginx(Arr::get($configurationData, 'web.type'))) {
$filesToGenerate[] = new TemplateFile(
data: 'web/nginx/default.conf',
name: 'default.conf',
path: 'tools/docker/images/web/root/etc/nginx/conf.d',
);
}
if ('drupal-project' === Arr::get($configurationData, 'type')) {
// Add a Drupal version of phpunit.xml.dist.
$filesToGenerate[] = new TemplateFile(data: 'drupal-project/phpunit.xml.dist', name: 'phpunit.xml.dist');
}
if (Arr::get($configurationData, 'experimental.createGitHubActionsConfiguration', false) === true) {
$filesToGenerate[] = new TemplateFile(
data: 'ci/github-actions/ci.yml',
name: 'ci.yml',
path: '.github/workflows',
);
}
if (Arr::get($configurationData, 'experimental.runGitHooksBeforePush', false) === true) {
$filesToGenerate[] = new TemplateFile(
data: 'git-hooks/pre-push',
name: 'pre-push',
path: '.githooks',
);
}
return $next([$configurationData, $filesToGenerate]);
}
private static function isCaddy(?string $webServer): bool
{
if (is_null($webServer)) {
return false;
}
return strtoupper($webServer) === WebServer::CADDY->name;
}
private static function isNginx(?string $webServer): bool
{
if (is_null($webServer)) {
return false;
}
return strtoupper($webServer) === WebServer::NGINX->name;
}
private static function isNode(?string $language): bool
{
if (is_null($language)) {
return false;
}
return strtoupper($language) === Language::NODE->name;
}
private static function isPhp(?string $language): bool
{
if (is_null($language)) {
return false;
}
return strtoupper($language) === Language::PHP->name;
}
}

View file

@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace App\Action;
use App\DataTransferObject\TemplateFile;
use Illuminate\Support\Collection;
use Symfony\Component\Filesystem\Filesystem;
use Twig\Environment;
final class GenerateConfigurationFiles
{
public function __construct(
private Filesystem $filesystem,
private Environment $twig,
private string $outputDir,
) {
}
public function handle(array $filesToGenerateAndConfigurationData, \Closure $next)
{
/**
* @var Collection<int,TemplateFile> $filesToGenerate
* @var array<string,mixed> $configurationData
*/
[$configurationData, $filesToGenerate] = $filesToGenerateAndConfigurationData;
$filesToGenerate->each(function(TemplateFile $templateFile) use ($configurationData): void {
if ($templateFile->path !== null) {
if (!$this->filesystem->exists($templateFile->path)) {
$this->filesystem->mkdir("{$this->outputDir}/{$templateFile->path}");
}
}
$sourceFile = "{$templateFile->data}.twig";
$outputFile = collect([
$this->outputDir,
$templateFile->path,
$templateFile->name,
])->filter()->implode('/');
$this->filesystem->dumpFile($outputFile, $this->twig->render($sourceFile, $configurationData));
});
// If the Docker entrypoint file is generated, ensure it is executable.
if ($this->filesystem->exists("{$outputDir}/tools/docker/images/php/root/usr/local/bin/docker-entrypoint-php")) {
$filesystem->chmod("{$outputDir}/tools/docker/images/php/root/usr/local/bin/docker-entrypoint-php", 0755);
}
if ($this->filesystem->exists("{$outputDir}/.githooks/pre-push")) {
$this->filesystem->chmod("{$outputDir}/.githooks/pre-push", 0755);
}
return $next([$configurationData, $filesToGenerate]);
}
}

View file

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace App\Action;
use App\DataTransferObject\Config;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Validator\ConstraintViolationInterface;
use Symfony\Component\Validator\Validation;
final class ValidateBuildConfigurationData
{
public function handle(array $configurationData, \Closure $next) {
// Convert the input to a configuration data object.
$normalizer = new ObjectNormalizer(null, new CamelCaseToSnakeCaseNameConverter());
$serializer = new Serializer([$normalizer], [new JsonEncoder()]);
$configurationDataObject = $serializer->deserialize(json_encode($configurationData), Config::class, 'json');
$validator = Validation::createValidatorBuilder()->enableAnnotationMapping()->getValidator();
$violations = $validator->validate($configurationDataObject);
if (0 < $violations->count()) {
$io->error('Configuration is invalid.');
$io->listing(
collect($violations)
->map(fn (ConstraintViolationInterface $v) => "{$v->getPropertyPath()} - {$v->getMessage()}")
->toArray()
);
return;
}
return $next($configurationData);
}
}