presentations/building-build-configs/sections/internals.rst
Oliver Davies 6891a7517a Re-organise
Signed-off-by: Oliver Davies <oliver@oliverdavies.uk>
2025-09-25 21:52:45 +01:00

300 lines
7 KiB
ReStructuredText

.. raw:: pdf
PageBreak titlePage
.. class:: centredtitle
Build Configs internals
.. raw:: pdf
PageBreak standardPage
.. code-block::
src/
Action/
CreateFinalConfigurationData.php
CreateListOfFilesToGenerate.php
GenerateConfigurationFiles.php
ValidateConfigurationData.php
Command/
GenerateCommand.php
InitCommand.php
DataTransferObject/
ConfigDto.php
TemplateFile.php
Enum/
Language.php
ProjectType.php
WebServer.php
.. code-block:: php
:linenos:
:startinline: true
protected function configure(): void
$this
->addOption(
name: 'config-file',
shortcut: ['c'],
mode: InputOption::VALUE_REQUIRED,
description: 'The path to the project\'s build.yaml file',
default: 'build.yaml',
)
->addOption(
name: 'output-dir',
shortcut: ['o'],
mode: InputOption::VALUE_REQUIRED,
description: 'The directory to create files in',
default: '.',
);
}
.. code-block:: php
:linenos:
:startinline: true
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$configFile = $input->getOption(name: 'config-file');
$outputDir = $input->getOption(name: 'output-dir');
}
.. raw:: pdf
PageBreak
.. code-block:: php
:linenos:
:startinline: true
protected function execute(InputInterface $input, OutputInterface $output): int
{
// ...
$pipelines = [
new CreateFinalConfigurationData(),
new ValidateConfigurationData(),
new CreateListOfFilesToGenerate(),
new GenerateConfigurationFiles(
$this->filesystem,
$this->twig,
$outputDir,
),
];
}
.. code-block:: php
:linenos:
:startinline: true
protected function execute(InputInterface $input, OutputInterface $output): int
{
// ...
/**
* @var Collection<int,TemplateFile> $generatedFiles
* @var ConfigDto $configurationData
*/
[$configurationData, $generatedFiles] = (new Pipeline())
->send($configFile)
->through($pipelines)
->thenReturn();
$io->info("Building configuration for {$configurationData->name}.");
$io->write('Generated files:');
$io->listing(static::getListOfFiles(filesToGenerate: $generatedFiles)->toArray());
return Command::SUCCESS;
}
.. code-block:: php
:linenos:
:startinline: true
// CreateFinalConfigurationData.php
public function handle(string $configFile, \Closure $next) {
{
$configurationData = Yaml::parseFile(filename: $configFile);
$configurationData = array_replace_recursive(
Yaml::parseFile(filename: __DIR__ . '/../../resources/build.defaults.yaml'),
$configurationData,
);
// ...
return $next($configurationData);
}
.. raw:: pdf
PageBreak
.. code-block:: php
:linenos:
:startinline: true
// ValidateConfigurationData.php
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()]);
$configurationDataDto = $serializer->deserialize(
json_encode($configurationData),
ConfigDto::class,
'json',
);
// ...
}
.. raw:: pdf
PageBreak
.. code-block:: php
:linenos:
:startinline: true
// ValidateConfigurationData.php
public function handle(array $configurationData, \Closure $next)
{
// ...
$validator = Validation::createValidatorBuilder()
->enableAnnotationMapping()
->getValidator();
$violations = $validator->validate($configurationDataDto);
if (0 < $violations->count()) {
throw new \RuntimeException('Configuration is invalid.');
}
return $next([$configurationData, $configurationDataDto]);
}
.. code-block:: php
:linenos:
:startinline: true
// ConfigDto.php
#[Assert\Collection(
allowExtraFields: false,
fields: ['docroot' => new Assert\Choice([null, 'web', 'docroot'])],
)]
public array $drupal;
#[Assert\Collection([
'ignore' => new Assert\Optional([
new Assert\All([
new Assert\Type('string'),
]),
]),
])]
public array $git;
#[Assert\Choice(choices: ['javascript', 'php', 'typescript'])]
public string $language;
#[Assert\NotBlank]
#[Assert\Type('string')]
public string $name;
#[Assert\Type('string')]
public string $projectRoot;
#[Assert\Choice(choices: [
'drupal',
'fractal',
'php-library',
'symfony',
])]
public string $type;
.. code-block:: php
:startinline: true
:linenos:
// CreateListOfFilesToGenerate.php
public function handle(array $configurationDataAndDto, \Closure $next)
{
/**
* @var ConfigDto $configDto,
* @var array<string,mixed> $configurationData
*/
[$configurationData, $configDto] = $configurationDataAndDto;
/** @var Collection<int, TemplateFile> */
$filesToGenerate = collect();
// ...
}
.. code-block:: php
:startinline: true
:linenos:
// CreateListOfFilesToGenerate.php
public function handle(array $configurationDataAndDto, \Closure $next)
{
// ...
if (!isset($configDto->php['phpunit']) || $configDto->php['phpunit'] !== false) {
$filesToGenerate->push(
new TemplateFile(
data: 'drupal/phpunit.xml.dist',
name: 'phpunit.xml.dist',
)
);
}
// ...
return $next([$configurationData, $configDto, $filesToGenerate]);
}
.. code-block:: php
:linenos:
:startinline: true
// GenerateConfigurationFiles.php
public function handle(array $filesToGenerateAndConfigurationData, \Closure $next)
{
// ...
$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));
});
return $next([$configurationDataDto, $filesToGenerate]);
}