Neue Services und Klassen hinzugefügt: Projekt-Entitäten, Repository und Konfigurationsverwaltung
This commit is contained in:
21
src/Attributes/Mapping/DataFromArray.php
Normal file
21
src/Attributes/Mapping/DataFromArray.php
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* DataFromArray.php 2026-03-16 thomas
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 Thomas Schneider <thomas@inter-mundos.de>
|
||||||
|
* Alle Rechte vorbehalten.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Attributes\Mapping;
|
||||||
|
|
||||||
|
use Attribute;
|
||||||
|
|
||||||
|
#[Attribute(Attribute::TARGET_PROPERTY)]
|
||||||
|
final readonly class DataFromArray
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public string|null $key,
|
||||||
|
){}
|
||||||
|
}
|
||||||
0
src/Entity/.gitignore
vendored
0
src/Entity/.gitignore
vendored
74
src/Entity/Project.php
Normal file
74
src/Entity/Project.php
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* Project.php 2026-03-20 thomas
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 Thomas Schneider <thomas@inter-mundos.de>
|
||||||
|
* Alle Rechte vorbehalten.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Entity;
|
||||||
|
|
||||||
|
use App\Attributes\Mapping\DataFromArray;
|
||||||
|
use App\Service\Deployment\DeploymentInterface;
|
||||||
|
use ReflectionClass;
|
||||||
|
use ReflectionException;
|
||||||
|
|
||||||
|
class Project
|
||||||
|
{
|
||||||
|
#[DataFromArray(key: 'name')]
|
||||||
|
public readonly string $name;
|
||||||
|
|
||||||
|
#[DataFromArray(key: 'deployment')]
|
||||||
|
public readonly array $deployment;
|
||||||
|
|
||||||
|
protected DeploymentInterface $deploymentService;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
array $data,
|
||||||
|
public string $projectDir,
|
||||||
|
public string $deployedVersion,
|
||||||
|
\Closure $deploymentServiceResolver,
|
||||||
|
){
|
||||||
|
$this->autoMapData($data);
|
||||||
|
|
||||||
|
$this->deploymentService = ($deploymentServiceResolver)()->init($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function deploy(string $version = '', string $step = ''): void
|
||||||
|
{
|
||||||
|
$this->deploymentService->handle($this, $version, $step);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatically maps data from the provided array to object properties
|
||||||
|
* based on attributes defined in the class.
|
||||||
|
*
|
||||||
|
* @param array $data The input data array containing key-value pairs to map.
|
||||||
|
*/
|
||||||
|
protected function autoMapData(array $data): void
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$class = new ReflectionClass($this);
|
||||||
|
|
||||||
|
foreach($class->getProperties() as $property)
|
||||||
|
{
|
||||||
|
foreach($property->getAttributes() as $attribute)
|
||||||
|
{
|
||||||
|
if($attribute->getName() === DataFromArray::class)
|
||||||
|
{
|
||||||
|
$attributeArguments = $attribute->getArguments();
|
||||||
|
$this->{$property->getName()} = $data[$attributeArguments['key']];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(ReflectionException $e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
0
src/Repository/.gitignore
vendored
0
src/Repository/.gitignore
vendored
57
src/Repository/ProjectRepository.php
Normal file
57
src/Repository/ProjectRepository.php
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* ProjectRepository.php 2026-03-15 thomas
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 Thomas Schneider <thomas@inter-mundos.de>
|
||||||
|
* Alle Rechte vorbehalten.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Entity\Project;
|
||||||
|
use App\Service\Deployment\DeploymentServiceResolver;
|
||||||
|
use App\Service\ProjectConfigDirService;
|
||||||
|
use Generator;
|
||||||
|
use Symfony\Component\DependencyInjection\Attribute\AutowireServiceClosure;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
|
class ProjectRepository
|
||||||
|
{
|
||||||
|
/** @var Project[] */
|
||||||
|
private array $projects;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
protected ProjectConfigDirService $configDirService,
|
||||||
|
#[AutowireServiceClosure(DeploymentServiceResolver::class)] \Closure $deploymentServiceResolver,
|
||||||
|
){
|
||||||
|
foreach($this->configDirService->getProjectDirContents() as $projectFile)
|
||||||
|
{
|
||||||
|
$deployedVersion = $this->configDirService->getDeployedVersion($projectFile->getPath());
|
||||||
|
|
||||||
|
$this->projects[$projectFile->getRelativePath()] = new Project(
|
||||||
|
data: Yaml::parseFile($projectFile->getRealPath()),
|
||||||
|
projectDir: $projectFile->getPath(),
|
||||||
|
deployedVersion: $deployedVersion,
|
||||||
|
deploymentServiceResolver: $deploymentServiceResolver,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getAll(): Generator
|
||||||
|
{
|
||||||
|
yield from $this->projects;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function findById($id): Project|null
|
||||||
|
{
|
||||||
|
return $this->projects[$id] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function exists(string $id): bool
|
||||||
|
{
|
||||||
|
return array_any($this->projects, fn($project) => $project['id'] === $id);
|
||||||
|
}
|
||||||
|
}
|
||||||
125
src/Service/ProjectConfigDirService.php
Normal file
125
src/Service/ProjectConfigDirService.php
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* ProjectConfigDirService.php 2026-03-15 thomas
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 Thomas Schneider <thomas@inter-mundos.de>
|
||||||
|
* Alle Rechte vorbehalten.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Service;
|
||||||
|
|
||||||
|
use App\Traits\LoggerTrait;
|
||||||
|
use Symfony\Component\Filesystem\Exception\IOExceptionInterface;
|
||||||
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
|
use Symfony\Component\Filesystem\Path;
|
||||||
|
use Symfony\Component\Finder\Exception\DirectoryNotFoundException;
|
||||||
|
use Symfony\Component\Finder\Finder;
|
||||||
|
use Symfony\Component\Finder\SplFileInfo;
|
||||||
|
|
||||||
|
class ProjectConfigDirService
|
||||||
|
{
|
||||||
|
use LoggerTrait;
|
||||||
|
|
||||||
|
const string FILE_NAME_DEPLOYED_VERSION = '.deployed-version';
|
||||||
|
const string FILE_NAME_DEPLOYMENT_STEP = '.deployment-step';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
protected string $dataDir,
|
||||||
|
){}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return SplFileInfo[]
|
||||||
|
*/
|
||||||
|
public function getProjectDirContents(): array
|
||||||
|
{
|
||||||
|
return $this->loadProjectDirContents();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getDeployedVersion(string $projectDir): string
|
||||||
|
{
|
||||||
|
$filesystem = new Filesystem();
|
||||||
|
if(!$filesystem->exists($projectDir . DIRECTORY_SEPARATOR . self::FILE_NAME_DEPLOYED_VERSION))
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$version = $filesystem->readFile($projectDir . DIRECTORY_SEPARATOR . self::FILE_NAME_DEPLOYED_VERSION);
|
||||||
|
|
||||||
|
return trim($version);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function setDeployedVersion(string $projectDir, string $version): void
|
||||||
|
{
|
||||||
|
$filesystem = new Filesystem();
|
||||||
|
$filesystem->dumpFile($projectDir . DIRECTORY_SEPARATOR . self::FILE_NAME_DEPLOYED_VERSION, $version);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getDeploymentStep(string $projectDir): string
|
||||||
|
{
|
||||||
|
$filesystem = new Filesystem();
|
||||||
|
if(!$filesystem->exists($projectDir . DIRECTORY_SEPARATOR . self::FILE_NAME_DEPLOYMENT_STEP))
|
||||||
|
{
|
||||||
|
return 'not-started';
|
||||||
|
}
|
||||||
|
|
||||||
|
$stage = $filesystem->readFile($projectDir . DIRECTORY_SEPARATOR . self::FILE_NAME_DEPLOYMENT_STEP);
|
||||||
|
|
||||||
|
return trim($stage);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function setDeploymentStep(string $projectDir, string $step): void
|
||||||
|
{
|
||||||
|
$filesystem = new Filesystem();
|
||||||
|
$filesystem->dumpFile($projectDir . DIRECTORY_SEPARATOR . self::FILE_NAME_DEPLOYMENT_STEP, $step);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return SplFileInfo[]
|
||||||
|
*/
|
||||||
|
protected function loadProjectDirContents(): array
|
||||||
|
{
|
||||||
|
$finder = new Finder();
|
||||||
|
$content = [];
|
||||||
|
|
||||||
|
// find all files in the current directory
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$finder->files()->in($this->dataDir)->depth(1)->name('project.yaml');
|
||||||
|
|
||||||
|
foreach($finder as $file)
|
||||||
|
{
|
||||||
|
$content[] = $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
catch(DirectoryNotFoundException $e)
|
||||||
|
{
|
||||||
|
$this->logger->error('Project directory not found: ' . $this->dataDir . '. creating ...');
|
||||||
|
|
||||||
|
$filesystem = new Filesystem();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$filesystem->mkdir(Path::normalize($this->dataDir));
|
||||||
|
$this->loadProjectDirContents();
|
||||||
|
}
|
||||||
|
catch(IOExceptionInterface $exception)
|
||||||
|
{
|
||||||
|
echo "An error occurred while creating your directory at ".$exception->getPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(\Exception $e)
|
||||||
|
{
|
||||||
|
throw new \RuntimeException('Failed to load project directory contents', 500, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/Traits/LoggerTrait.php
Normal file
23
src/Traits/LoggerTrait.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* LoggerTrait.php 2026-03-12 thomas
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 Thomas Schneider <thomas@inter-mundos.de>
|
||||||
|
* Alle Rechte vorbehalten.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Traits;
|
||||||
|
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Contracts\Service\Attribute\Required;
|
||||||
|
|
||||||
|
trait LoggerTrait
|
||||||
|
{
|
||||||
|
protected LoggerInterface $logger;
|
||||||
|
|
||||||
|
#[Required]
|
||||||
|
public function setLogger(LoggerInterface $logger): void
|
||||||
|
{
|
||||||
|
$this->logger = $logger;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user