110 lines
3.8 KiB
PHP
110 lines
3.8 KiB
PHP
<?php
|
|
|
|
namespace App\Actions\Environments;
|
|
|
|
use App\Enums\BuildArtifactStatus;
|
|
use App\Enums\BuildStrategy;
|
|
use App\Enums\RegistryType;
|
|
use App\Enums\ServiceCategory;
|
|
use App\Models\BuildArtifact;
|
|
use App\Models\Environment;
|
|
use App\Services\Registries\ImageReference;
|
|
use App\Services\Registries\RegistryResolver;
|
|
use RuntimeException;
|
|
|
|
class PlanBuildArtifact
|
|
{
|
|
public function __construct(
|
|
private readonly RegistryResolver $registryResolver,
|
|
private readonly ImageReference $imageReference,
|
|
) {}
|
|
|
|
public function execute(Environment $environment, string $commitSha): BuildArtifact
|
|
{
|
|
$environment->loadMissing(['application.organisation.registries', 'services.replicas']);
|
|
|
|
$existingArtifact = $environment->buildArtifacts()
|
|
->where('commit_sha', $commitSha)
|
|
->whereIn('status', [BuildArtifactStatus::PENDING, BuildArtifactStatus::BUILDING, BuildArtifactStatus::AVAILABLE])
|
|
->latest()
|
|
->first();
|
|
|
|
if ($existingArtifact) {
|
|
return $existingArtifact;
|
|
}
|
|
|
|
$targetServerCount = $this->targetServerCount($environment);
|
|
$registry = $this->registryResolver->buildRegistryFor($environment->application->organisation);
|
|
$registryType = $this->registryType($registry);
|
|
|
|
if ($targetServerCount > 1 && ! $registry) {
|
|
$blocker = $this->registryResolver->managedRegistryBlockerFor($environment->application->organisation);
|
|
|
|
throw new RuntimeException($blocker ?: 'A registry is required before building artifacts for multi-server deployments.');
|
|
}
|
|
|
|
$builder = $environment->application->organisation->services()
|
|
->where('category', ServiceCategory::BUILDER)
|
|
->first();
|
|
$buildServerId = null;
|
|
|
|
if ($registryType === RegistryType::MANAGED) {
|
|
$buildServerId = (int) $registry->control_server_id;
|
|
|
|
if ($buildServerId <= 0) {
|
|
throw new RuntimeException('A control/build server is required for managed registry builds.');
|
|
}
|
|
}
|
|
|
|
$strategy = match (true) {
|
|
$registryType === RegistryType::MANAGED => BuildStrategy::DEDICATED_BUILDER,
|
|
$registry !== null => BuildStrategy::EXTERNAL_REGISTRY,
|
|
$builder !== null => BuildStrategy::DEDICATED_BUILDER,
|
|
default => BuildStrategy::TARGET_SERVER,
|
|
};
|
|
|
|
$imageTag = $this->imageReference->tagFor($environment, $commitSha, $registry);
|
|
|
|
return $environment->buildArtifacts()->create([
|
|
'commit_sha' => $commitSha,
|
|
'image_tag' => $imageTag,
|
|
'registry_ref' => $registry ? $this->imageReference->registryReference($registry, $imageTag) : null,
|
|
'built_by_service_id' => $builder?->id,
|
|
'status' => BuildArtifactStatus::PENDING,
|
|
'metadata' => [
|
|
'build_strategy' => $strategy->value,
|
|
'registry_type' => $registryType?->value,
|
|
'target_server_count' => $targetServerCount,
|
|
'build_server_id' => $buildServerId,
|
|
],
|
|
]);
|
|
}
|
|
|
|
private function targetServerCount(Environment $environment): int
|
|
{
|
|
$replicaServerCount = $environment->services
|
|
->flatMap(fn ($service) => $service->replicas->pluck('server_id')->filter())
|
|
->unique()
|
|
->count();
|
|
|
|
if ($replicaServerCount > 0) {
|
|
return $replicaServerCount;
|
|
}
|
|
|
|
return $environment->services->sum('desired_replicas') > 1 ? 2 : 1;
|
|
}
|
|
|
|
private function registryType(mixed $registry): ?RegistryType
|
|
{
|
|
if (! $registry) {
|
|
return null;
|
|
}
|
|
|
|
if ($registry->type instanceof RegistryType) {
|
|
return $registry->type;
|
|
}
|
|
|
|
return RegistryType::tryFrom((string) $registry->type);
|
|
}
|
|
}
|