Add managed registry provisioning, pruning, and readiness tracking

This commit is contained in:
2026-06-08 20:44:16 +01:00
parent 5b977c1f41
commit 3a851db08f
52 changed files with 2706 additions and 116 deletions

View File

@@ -4,13 +4,21 @@ 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']);
@@ -26,36 +34,48 @@ class PlanBuildArtifact
}
$targetServerCount = $this->targetServerCount($environment);
$registry = $environment->application->organisation->registries()->first();
$registry = $this->registryResolver->buildRegistryFor($environment->application->organisation);
$registryType = $this->registryType($registry);
if ($targetServerCount > 1 && ! $registry) {
throw new RuntimeException('A registry is required before building artifacts for multi-server deployments.');
$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 = str($environment->application->name)
->slug()
->append(':'.substr($commitSha, 0, 12))
->value();
$imageTag = $this->imageReference->tagFor($environment, $commitSha, $registry);
return $environment->buildArtifacts()->create([
'commit_sha' => $commitSha,
'image_tag' => $imageTag,
'registry_ref' => $registry ? rtrim((string) $registry->url, '/').'/'.$imageTag : null,
'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,
],
]);
}
@@ -73,4 +93,17 @@ class PlanBuildArtifact
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);
}
}