Add tests for automatic path-routed release target preparation and application target handling failures in ReleaseManager
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1295,6 +1295,20 @@ class release_manager
|
||||
$statuses[] = 'failed';
|
||||
continue;
|
||||
}
|
||||
$target = $this->prepareChannelSyncApplicationTarget($target, $actorUserId);
|
||||
if (($target['_release_auto_prepared_application'] ?? false) === true) {
|
||||
$this->recordOperationStep(
|
||||
$operationId,
|
||||
$app . '_target_prepared',
|
||||
strtoupper($app) . ' Coolify application target',
|
||||
'passed',
|
||||
sprintf('%s target was prepared to create a path-routed Coolify application.', strtoupper($app)),
|
||||
null,
|
||||
null,
|
||||
['target_id' => (int)$target['id'], 'app' => $app]
|
||||
);
|
||||
$statuses[] = 'passed';
|
||||
}
|
||||
|
||||
$repository = trim((string)($target['repository'] ?? self::defaultRepositoryForApp($app)));
|
||||
if ($repository === '') {
|
||||
@@ -1761,6 +1775,11 @@ class release_manager
|
||||
throw new RuntimeException('Release deployment target is missing.');
|
||||
}
|
||||
|
||||
return $this->prepareDeploymentTargetAsApplication($targetId, $actorUserId, 'warning');
|
||||
}
|
||||
|
||||
private function prepareDeploymentTargetAsApplication(int $targetId, ?int $actorUserId, string $severity = 'warning'): array
|
||||
{
|
||||
$target = $this->getDeploymentTarget($targetId);
|
||||
$context = self::jsonDecode($target['deploy_context_json'] ?? null);
|
||||
$context = is_array($context) ? $context : [];
|
||||
@@ -1774,9 +1793,10 @@ class release_manager
|
||||
'si',
|
||||
[self::jsonEncode($context), $targetId]
|
||||
);
|
||||
$this->audit((int)$target['channel_id'], null, 'deployment_target_prepared_as_application', $actorUserId, 'warning', [
|
||||
$this->audit((int)$target['channel_id'], null, 'deployment_target_prepared_as_application', $actorUserId, $severity, [
|
||||
'target_id' => $targetId,
|
||||
'replaced_legacy_service_uuid' => $replacedLegacyUuid,
|
||||
'severity' => $severity,
|
||||
]);
|
||||
|
||||
return [
|
||||
@@ -1786,6 +1806,20 @@ class release_manager
|
||||
];
|
||||
}
|
||||
|
||||
private function prepareChannelSyncApplicationTarget(array $target, ?int $actorUserId): array
|
||||
{
|
||||
$context = self::jsonDecode($target['deploy_context_json'] ?? null);
|
||||
$context = is_array($context) ? $context : [];
|
||||
if (!$this->releaseTargetNeedsApplicationAutoCreate($target, $context)) {
|
||||
return $target;
|
||||
}
|
||||
|
||||
$this->prepareDeploymentTargetAsApplication((int)$target['id'], $actorUserId, 'info');
|
||||
$prepared = $this->getDeploymentTarget((int)$target['id']);
|
||||
$prepared['_release_auto_prepared_application'] = true;
|
||||
return $prepared;
|
||||
}
|
||||
|
||||
private function releaseStatusIssueByKey(array $summary, string $issueKey): ?array
|
||||
{
|
||||
foreach (is_array($summary['status_overview']['issues'] ?? null) ? $summary['status_overview']['issues'] : [] as $issue) {
|
||||
@@ -2601,7 +2635,10 @@ class release_manager
|
||||
private static function releaseStatusIssueNeedsApplicationTarget(array $issue): bool
|
||||
{
|
||||
$text = strtolower(trim((string)($issue['message'] ?? '') . ' ' . (string)($issue['next_action'] ?? '')));
|
||||
return str_contains($text, 'stripprefix') || str_contains($text, 'path-routed');
|
||||
return str_contains($text, 'stripprefix')
|
||||
|| str_contains($text, 'path-routed')
|
||||
|| str_contains($text, 'service creation')
|
||||
|| str_contains($text, 'coolify service');
|
||||
}
|
||||
|
||||
private static function releaseStatusEligibleBundles(array $bundles): array
|
||||
@@ -5647,6 +5684,26 @@ class release_manager
|
||||
return 'service';
|
||||
}
|
||||
|
||||
private function releaseTargetNeedsApplicationAutoCreate(array $target, array $context): bool
|
||||
{
|
||||
if (!in_array(strtolower(trim((string)($target['app'] ?? ''))), self::APPS, true)) {
|
||||
return false;
|
||||
}
|
||||
if ($this->nullablePositiveInt($target['coolify_instance_id'] ?? null) === null) {
|
||||
return false;
|
||||
}
|
||||
if (trim((string)($target['coolify_service_uuid'] ?? '')) !== '') {
|
||||
return false;
|
||||
}
|
||||
if ($this->toBool($context['coolify_auto_create'] ?? false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$publicUrl = $this->releaseCoolifyPublicUrl($target, $context);
|
||||
return self::releaseCoolifyPublicUrlNeedsStripPrefixLabels($publicUrl)
|
||||
|| $this->releaseCoolifyResourceType($context, '') === 'application';
|
||||
}
|
||||
|
||||
private function releaseCoolifyBuildPack(array $target, array $context): string
|
||||
{
|
||||
$app = strtolower(trim((string)($target['app'] ?? '')));
|
||||
|
||||
@@ -351,6 +351,50 @@ it('does not treat an existing Coolify service as an application just because a
|
||||
], 'existing-application-uuid'))->toBe('application');
|
||||
});
|
||||
|
||||
it('auto-prepares path-routed release targets for Coolify application creation', function (): void {
|
||||
$manager = new release_manager();
|
||||
$needsApplication = new ReflectionMethod(release_manager::class, 'releaseTargetNeedsApplicationAutoCreate');
|
||||
$needsApplication->setAccessible(true);
|
||||
|
||||
$target = [
|
||||
'id' => 42,
|
||||
'channel_slug' => 'stable',
|
||||
'app' => 'api',
|
||||
'coolify_instance_id' => 7,
|
||||
'coolify_service_uuid' => '',
|
||||
'deploy_context_json' => null,
|
||||
];
|
||||
|
||||
expect($needsApplication->invoke($manager, $target, []))->toBeTrue();
|
||||
expect($needsApplication->invoke($manager, array_replace($target, [
|
||||
'coolify_service_uuid' => 'existing-service',
|
||||
]), []))->toBeFalse();
|
||||
expect($needsApplication->invoke($manager, array_replace($target, [
|
||||
'coolify_instance_id' => null,
|
||||
]), []))->toBeFalse();
|
||||
expect($needsApplication->invoke($manager, $target, [
|
||||
'coolify_auto_create' => true,
|
||||
]))->toBeFalse();
|
||||
});
|
||||
|
||||
it('offers application target preparation for missing Coolify service creation failures', function (): void {
|
||||
$needsApplicationAction = new ReflectionMethod(release_manager::class, 'releaseStatusIssueNeedsApplicationTarget');
|
||||
$needsApplicationAction->setAccessible(true);
|
||||
|
||||
expect($needsApplicationAction->invoke(null, [
|
||||
'message' => 'API deployment failed before activation.',
|
||||
'next_action' => 'Select an existing Coolify service or enable Coolify service creation before deployment.',
|
||||
]))->toBeTrue();
|
||||
expect($needsApplicationAction->invoke(null, [
|
||||
'message' => 'Path-routed release targets require a Coolify application resource with StripPrefix labels.',
|
||||
'next_action' => 'Migrate this target before deploying.',
|
||||
]))->toBeTrue();
|
||||
expect($needsApplicationAction->invoke(null, [
|
||||
'message' => 'Release gate failed.',
|
||||
'next_action' => 'Run live smoke tests before promotion.',
|
||||
]))->toBeFalse();
|
||||
});
|
||||
|
||||
it('keeps release branch services out of the production Coolify environment except beta', function (): void {
|
||||
$manager = new release_manager();
|
||||
$payloadMethod = new ReflectionMethod(release_manager::class, 'releaseCoolifyServicePayload');
|
||||
|
||||
Reference in New Issue
Block a user