mirror of
https://github.com/coollabsio/coolify.git
synced 2026-10-28 17:44:36 +08:00
fix(previews): keep custom preview domains on redeploy
Closes #6536 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
79cf62323a
commit
7654e8d3a9
4 files changed
+141
-6
No files matched your search
@@ -292,9 +292,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
if ($this->preview) {
|
||||
if ($this->application->build_pack === 'dockercompose') {
|
||||
$this->preview->generate_preview_fqdn_compose();
|
||||
$this->preview->generate_preview_fqdn_compose(keepExistingDomains: true);
|
||||
} else {
|
||||
$this->preview->generate_preview_fqdn();
|
||||
$this->preview->generate_preview_fqdn(keepExistingDomains: true);
|
||||
}
|
||||
}
|
||||
if ($this->application->isGithubAppSource()) {
|
||||
|
||||
@@ -147,7 +147,7 @@ class Previews extends Component
|
||||
]);
|
||||
$this->auditPreviewEvent('ui.application.preview_created', $pull_request_id);
|
||||
}
|
||||
$found->generate_preview_fqdn_compose();
|
||||
$found->generate_preview_fqdn_compose(keepExistingDomains: ! $found->wasRecentlyCreated);
|
||||
$this->application->refresh();
|
||||
$this->syncDockerTags();
|
||||
} else {
|
||||
@@ -165,7 +165,7 @@ class Previews extends Component
|
||||
if ($found && $this->application->build_pack === 'dockerimage' && str($docker_registry_image_tag)->isNotEmpty()) {
|
||||
$this->saveDockerTagWithAudit($found, $docker_registry_image_tag);
|
||||
}
|
||||
$found->generate_preview_fqdn(generateWithoutApplicationDomain: true);
|
||||
$found->generate_preview_fqdn(generateWithoutApplicationDomain: true, keepExistingDomains: true);
|
||||
$this->application->refresh();
|
||||
$this->syncDockerTags();
|
||||
$this->dispatch('update_links');
|
||||
|
||||
@@ -196,8 +196,23 @@ class ApplicationPreview extends BaseModel
|
||||
return $this->morphMany(LocalPersistentVolume::class, 'resource');
|
||||
}
|
||||
|
||||
public function generate_preview_fqdn(bool $generateWithoutApplicationDomain = false)
|
||||
/**
|
||||
* Set the preview domains from the preview URL template.
|
||||
*
|
||||
* With $keepExistingDomains, domains already stored on the preview (for example custom
|
||||
* domains set in the UI) are kept, and only an empty preview gets generated domains.
|
||||
*/
|
||||
public function generate_preview_fqdn(bool $generateWithoutApplicationDomain = false, bool $keepExistingDomains = false)
|
||||
{
|
||||
if ($keepExistingDomains && filled($this->fqdn)) {
|
||||
$normalized = DomainPortOverrides::normalize($this->fqdn, $this->domain_port_overrides);
|
||||
$this->fqdn = $normalized['fqdn'];
|
||||
$this->domain_port_overrides = $normalized['overrides'];
|
||||
$this->save();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
$applicationFqdn = $this->application->fqdn;
|
||||
if (! $applicationFqdn && $generateWithoutApplicationDomain) {
|
||||
$applicationFqdn = generateUrl(
|
||||
@@ -221,7 +236,13 @@ class ApplicationPreview extends BaseModel
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function generate_preview_fqdn_compose(bool $generateWithoutApplicationDomain = false)
|
||||
/**
|
||||
* Set the preview domain of every compose service from the preview URL template.
|
||||
*
|
||||
* With $keepExistingDomains, a service that already has domains on the preview keeps them,
|
||||
* and only services without a preview domain get generated domains.
|
||||
*/
|
||||
public function generate_preview_fqdn_compose(bool $generateWithoutApplicationDomain = false, bool $keepExistingDomains = false)
|
||||
{
|
||||
$applicationDomains = json_decode($this->application->docker_compose_domains ?: '[]', true) ?: [];
|
||||
$previewDomains = json_decode(data_get($this, 'docker_compose_domains') ?: '[]', true) ?: [];
|
||||
@@ -262,6 +283,21 @@ class ApplicationPreview extends BaseModel
|
||||
$docker_compose_domains = [];
|
||||
$previewPortOverrides = [];
|
||||
foreach ($serviceNames as $service_name) {
|
||||
$existingPreviewDomains = $keepExistingDomains
|
||||
? DomainPortOverrides::normalize(getComposeServiceDomainString($previewDomains, $service_name), $this->domain_port_overrides)
|
||||
: ['fqdn' => null, 'overrides' => null];
|
||||
if (filled($existingPreviewDomains['fqdn'])) {
|
||||
$docker_compose_domains = putComposeServiceDomain(
|
||||
$docker_compose_domains,
|
||||
$service_name,
|
||||
$existingPreviewDomains['fqdn'],
|
||||
$serviceNames,
|
||||
);
|
||||
$previewPortOverrides = array_merge($previewPortOverrides, $existingPreviewDomains['overrides'] ?? []);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$domain_string = getComposeServiceDomainString($applicationDomains, $service_name);
|
||||
|
||||
if (empty($domain_string)) {
|
||||
|
||||
@@ -1113,3 +1113,102 @@ it('does not queue file sync jobs when polling dns checks for compose previews',
|
||||
->and(Queue::pushed(ServerFilesFromServerJob::class))->toHaveCount(1)
|
||||
->and($component->get('composeServices'))->toBe(['web', 'api']);
|
||||
});
|
||||
|
||||
it('keeps custom preview domains and port overrides on redeploy', function () {
|
||||
$this->application->update([
|
||||
'fqdn' => 'https://app.example.com',
|
||||
'preview_url_template' => '{{pr_id}}.{{domain}}',
|
||||
]);
|
||||
$preview = createPreviewForPortTests($this->application, 6536, [
|
||||
'fqdn' => 'https://custom-one.example.com,https://custom-two.example.com',
|
||||
'domain_port_overrides' => ['https://custom-two.example.com' => 9000],
|
||||
]);
|
||||
|
||||
// The deployment job keeps the stored preview domains.
|
||||
$preview->fresh()->generate_preview_fqdn(keepExistingDomains: true);
|
||||
|
||||
expect($preview->fresh()->fqdn)->toBe('https://custom-one.example.com,https://custom-two.example.com')
|
||||
->and($preview->fresh()->domain_port_overrides)->toBe(['https://custom-two.example.com' => 9000]);
|
||||
|
||||
// Without the flag (the "Generate domain" action), the template replaces them.
|
||||
$preview->fresh()->generate_preview_fqdn();
|
||||
|
||||
expect($preview->fresh()->fqdn)->toBe('https://6536.app.example.com')
|
||||
->and($preview->fresh()->domain_port_overrides)->toBeNull();
|
||||
});
|
||||
|
||||
it('moves a port from a kept legacy preview domain into the overrides', function () {
|
||||
$this->application->update(['fqdn' => 'https://app.example.com']);
|
||||
$preview = createPreviewForPortTests($this->application, 6537, [
|
||||
'fqdn' => 'https://custom.example.com:3000',
|
||||
]);
|
||||
|
||||
$preview->generate_preview_fqdn(keepExistingDomains: true);
|
||||
|
||||
expect($preview->fresh()->fqdn)->toBe('https://custom.example.com')
|
||||
->and($preview->fresh()->domain_port_overrides)->toBe(['https://custom.example.com' => 3000]);
|
||||
});
|
||||
|
||||
it('generates a domain on redeploy when the preview has none', function () {
|
||||
$this->application->update([
|
||||
'fqdn' => 'https://app.example.com',
|
||||
'preview_url_template' => '{{pr_id}}.{{domain}}',
|
||||
]);
|
||||
$preview = createPreviewForPortTests($this->application, 6538);
|
||||
|
||||
$preview->generate_preview_fqdn(keepExistingDomains: true);
|
||||
|
||||
expect($preview->fresh()->fqdn)->toBe('https://6538.app.example.com');
|
||||
});
|
||||
|
||||
it('keeps custom compose preview domains on redeploy and fills empty services', function () {
|
||||
$this->application->update([
|
||||
'build_pack' => 'dockercompose',
|
||||
'preview_url_template' => '{{pr_id}}.{{domain}}',
|
||||
'docker_compose_raw' => "services:\n frontend:\n image: nginx:alpine\n backend:\n image: nginx:alpine\n",
|
||||
'docker_compose_domains' => json_encode([
|
||||
'frontend' => ['domain' => 'https://app.example.com'],
|
||||
'backend' => ['domain' => 'https://api.example.com:3001'],
|
||||
]),
|
||||
]);
|
||||
$preview = createPreviewForPortTests($this->application->fresh(), 6539, [
|
||||
'docker_compose_domains' => json_encode([
|
||||
'frontend' => ['domain' => 'https://custom.example.com'],
|
||||
'backend' => ['domain' => ''],
|
||||
]),
|
||||
'domain_port_overrides' => ['https://custom.example.com' => 8080],
|
||||
]);
|
||||
|
||||
$preview->generate_preview_fqdn_compose(keepExistingDomains: true);
|
||||
$preview->refresh();
|
||||
|
||||
expect(json_decode($preview->docker_compose_domains, true))->toBe([
|
||||
'frontend' => ['domain' => 'https://custom.example.com'],
|
||||
'backend' => ['domain' => 'https://6539.api.example.com'],
|
||||
])
|
||||
->and($preview->fqdn)->toBe('https://custom.example.com,https://6539.api.example.com')
|
||||
->and($preview->domain_port_overrides)->toBe([
|
||||
'https://custom.example.com' => 8080,
|
||||
'https://6539.api.example.com' => 3001,
|
||||
]);
|
||||
|
||||
$preview->generate_preview_fqdn_compose();
|
||||
|
||||
expect(json_decode($preview->fresh()->docker_compose_domains, true)['frontend']['domain'])->toBe('https://6539.app.example.com');
|
||||
});
|
||||
|
||||
it('replaces a custom preview domain when the generate action runs', function () {
|
||||
$this->application->update([
|
||||
'fqdn' => 'https://app.example.com',
|
||||
'preview_url_template' => '{{pr_id}}.{{domain}}',
|
||||
]);
|
||||
$preview = createPreviewForPortTests($this->application, 6540, [
|
||||
'fqdn' => 'https://custom.example.com',
|
||||
]);
|
||||
|
||||
Livewire::test(PreviewDomains::class, ['preview' => $preview])
|
||||
->call('generateDomain')
|
||||
->assertHasNoErrors();
|
||||
|
||||
expect($preview->fresh()->fqdn)->toBe('https://6540.app.example.com');
|
||||
});
|
||||
Reference in new issue
Block a user