feat/ajout-de-fonctionnalites (#1)
All checks were successful
Auto Tag Develop / tag (push) Successful in 6s

Reviewed-on: #1
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #1.
This commit is contained in:
2026-04-06 14:23:20 +00:00
committed by Autin
parent f80578c26a
commit 8f585b4be8
52 changed files with 6536 additions and 434 deletions

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace App\Service;
use App\Entity\Environment;
use Symfony\Component\Process\Process;
final class DeployService
{
/**
* @return array{success: bool, output: string, exitCode: int}
*/
public function deploy(Environment $environment, string $tag): array
{
$scriptPath = $environment->getDeployScriptPath();
if (null === $scriptPath || !file_exists($scriptPath)) {
return [
'success' => false,
'output' => sprintf('Deploy script not found: %s', $scriptPath ?? 'null'),
'exitCode' => 1,
];
}
$process = new Process(
['bash', $scriptPath, $tag],
dirname($scriptPath),
);
$process->setTimeout(300);
$process->run();
return [
'success' => $process->isSuccessful(),
'output' => $process->getOutput() . $process->getErrorOutput(),
'exitCode' => $process->getExitCode() ?? 1,
];
}
}

View File

@@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
namespace App\Service;
use Symfony\Component\Process\Process;
final class DockerService
{
/**
* @return array{status: string, image: string, version: string, startedAt: string}
*/
public function getContainerStatus(string $containerName): array
{
$process = new Process([
'docker', 'inspect',
'--format', '{{.State.Status}}||{{.Config.Image}}||{{.State.StartedAt}}',
$containerName,
]);
$process->setTimeout(10);
$process->run();
if (!$process->isSuccessful()) {
return [
'status' => 'not_found',
'image' => '',
'version' => '',
'startedAt' => '',
];
}
$parts = explode('||', trim($process->getOutput()));
if (\count($parts) < 3) {
return [
'status' => 'not_found',
'image' => '',
'version' => '',
'startedAt' => '',
];
}
$image = $parts[1];
$version = 'latest';
if (str_contains($image, ':')) {
$version = substr($image, strrpos($image, ':') + 1);
}
return [
'status' => $parts[0],
'image' => $image,
'version' => $version,
'startedAt' => $parts[2],
];
}
/**
* @return array{cpuPercent: float, memoryUsage: string, memoryLimit: string, memoryPercent: float}
*/
public function getContainerStats(string $containerName): array
{
$process = new Process([
'docker', 'stats', '--no-stream',
'--format', '{{.CPUPerc}}||{{.MemUsage}}||{{.MemPerc}}',
$containerName,
]);
$process->setTimeout(10);
$process->run();
if (!$process->isSuccessful()) {
return [
'cpuPercent' => 0.0,
'memoryUsage' => '',
'memoryLimit' => '',
'memoryPercent' => 0.0,
];
}
$parts = explode('||', trim($process->getOutput()));
if (\count($parts) < 3) {
return [
'cpuPercent' => 0.0,
'memoryUsage' => '',
'memoryLimit' => '',
'memoryPercent' => 0.0,
];
}
$memParts = explode(' / ', $parts[1]);
return [
'cpuPercent' => (float) rtrim($parts[0], '%'),
'memoryUsage' => $memParts[0] ?? '',
'memoryLimit' => $memParts[1] ?? '',
'memoryPercent' => (float) rtrim($parts[2], '%'),
];
}
}

View File

@@ -0,0 +1,93 @@
<?php
declare(strict_types=1);
namespace App\Service;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Contracts\HttpClient\HttpClientInterface;
final readonly class GiteaRegistryService
{
public function __construct(
private HttpClientInterface $httpClient,
#[Autowire('%env(GITEA_API_URL)%')]
private string $giteaApiUrl,
#[Autowire('%env(GITEA_API_TOKEN)%')]
private string $giteaApiToken,
) {}
/**
* List available tags for a container image.
*
* @param string $registryImage e.g. "gitea.malio.fr/malio-dev/sirh"
*
* @return list<string>
*/
public function listTags(string $registryImage): array
{
$parts = explode('/', $registryImage);
if (\count($parts) < 3) {
throw new \InvalidArgumentException(sprintf('Invalid registry image format: "%s". Expected "registry/owner/package".', $registryImage));
}
$owner = $parts[1];
$package = implode('/', \array_slice($parts, 2));
$bearerToken = $this->getBearerToken($owner, $package);
$url = sprintf('%s/v2/%s/%s/tags/list', $this->giteaApiUrl, $owner, $package);
$response = $this->httpClient->request('GET', $url, [
'headers' => [
'Authorization' => sprintf('Bearer %s', $bearerToken),
],
'timeout' => 10,
]);
$data = $response->toArray();
$tags = $data['tags'] ?? [];
usort($tags, function (string $a, string $b): int {
$aIsVersion = str_starts_with($a, 'v');
$bIsVersion = str_starts_with($b, 'v');
if ($aIsVersion && $bIsVersion) {
return version_compare(ltrim($b, 'v'), ltrim($a, 'v'));
}
if ($aIsVersion) {
return -1;
}
if ($bIsVersion) {
return 1;
}
return strcmp($a, $b);
});
return $tags;
}
private function getBearerToken(string $owner, string $package): string
{
$tokenUrl = sprintf(
'%s/v2/token?service=container_registry&scope=repository:%s/%s:pull',
$this->giteaApiUrl,
$owner,
$package,
);
$response = $this->httpClient->request('GET', $tokenUrl, [
'auth_basic' => [$this->giteaApiToken, ''],
'timeout' => 10,
]);
$data = $response->toArray();
return $data['token'] ?? throw new \RuntimeException('Failed to obtain bearer token from Gitea registry.');
}
}