fix(domains): ignore hostless URLs in deployment and port overrides

This commit is contained in:
Andras Bacsai 2026-09-10 12:33:50 +02:00
parent 36befcfecd
commit a0a8c86752
5 changed files with 57 additions and 9 deletions

View file

@ -9,3 +9,6 @@ ## Alpine x-transition + tw-animate-css exit animations flash at the end
## Displayed defaults must not become stored overrides
- When an edit form shows an inherited or computed default, trace an unchanged save and a related-field edit through persistence.
- Preserve the inherited state when the displayed value still equals the computed default; store an override only when the user selects a different value.
## Prove regressions against the unchanged baseline
- For a bug fix, run the same regression test before and after the production change. Use a stash when requested so the failure and success come from the exact same test.

View file

@ -2403,15 +2403,20 @@ private function set_coolify_variables()
$fqdn = $this->preview->fqdn;
}
if (isset($fqdn)) {
$domains = str($fqdn)->explode(',')->map(fn (string $domain) => trim($domain))->filter();
$url = $domains->map(fn (string $domain) => Url::fromString($domain)->withPort(null)->__toString())->implode(',');
$fqdn = $domains->map(fn (string $domain) => Url::fromString($domain)->getHost())->implode(',');
if ((int) $this->application->compose_parsing_version >= 3) {
$this->coolify_variables .= 'COOLIFY_URL='.escapeShellValue($url).' ';
$this->coolify_variables .= 'COOLIFY_FQDN='.escapeShellValue($fqdn).' ';
} else {
$this->coolify_variables .= 'COOLIFY_URL='.escapeShellValue($fqdn).' ';
$this->coolify_variables .= 'COOLIFY_FQDN='.escapeShellValue($url).' ';
$domains = str($fqdn)->explode(',')
->map(fn (string $domain) => trim($domain))
->filter()
->filter(fn (string $domain) => isValidDomainUrl($domain));
if ($domains->isNotEmpty()) {
$url = $domains->map(fn (string $domain) => Url::fromString($domain)->withPort(null)->__toString())->implode(',');
$fqdn = $domains->map(fn (string $domain) => Url::fromString($domain)->getHost())->implode(',');
if ((int) $this->application->compose_parsing_version >= 3) {
$this->coolify_variables .= 'COOLIFY_URL='.escapeShellValue($url).' ';
$this->coolify_variables .= 'COOLIFY_FQDN='.escapeShellValue($fqdn).' ';
} else {
$this->coolify_variables .= 'COOLIFY_URL='.escapeShellValue($fqdn).' ';
$this->coolify_variables .= 'COOLIFY_FQDN='.escapeShellValue($url).' ';
}
}
}
if (isset($this->application->git_branch)) {

View file

@ -34,6 +34,7 @@ public static function normalize(?string $fqdn, ?array $existing): array
$normalizedDomains = collect(explode(',', $fqdn))
->map(fn (string $domain): string => trim($domain))
->filter()
->filter(fn (string $domain): bool => isValidDomainUrl($domain))
->map(function (string $domain) use ($existingOverrides): array {
$portlessDomain = self::withoutPort($domain);
$parts = DomainUrlParts::split($domain);

View file

@ -44,6 +44,7 @@ function coolifyVariablesForFqdn(string $fqdn, string $composeParsingVersion = '
// The created hook resets this, so it has to be set afterwards.
$application->compose_parsing_version = $composeParsingVersion;
$application->save();
Application::withoutGlobalScopes()->whereKey($application->id)->update(['fqdn' => $fqdn]);
$job = new TestableCoolifyUrlDeploymentJob;
$reflection = new ReflectionClass(ApplicationDeploymentJob::class);
@ -92,3 +93,19 @@ function coolifyVariablesForFqdn(string $fqdn, string $composeParsingVersion = '
->toContain("COOLIFY_URL='https://a.example.com'")
->toContain("COOLIFY_FQDN='a.example.com'");
});
it('ignores a hostless stored domain while setting deployment variables', function () {
$variables = coolifyVariablesForFqdn('https://,https://a.example.com');
expect($variables)
->toContain("COOLIFY_URL='https://a.example.com'")
->toContain("COOLIFY_FQDN='a.example.com'");
});
it('does not set domain variables for a hostless stored fqdn', function () {
$variables = coolifyVariablesForFqdn('https://');
expect($variables)
->not->toContain('COOLIFY_URL=')
->not->toContain('COOLIFY_FQDN=');
});

View file

@ -34,3 +34,25 @@
'www redirect' => ['https://example.com', 'https://www.example.com'],
'non-www redirect' => ['https://www.example.com', 'https://example.com'],
]);
it('drops hostless domains and their port overrides', function () {
$result = DomainPortOverrides::normalize(
'https://,https://example.com',
[
'https://' => 3000,
'https://example.com' => 8080,
],
);
expect($result)->toBe([
'fqdn' => 'https://example.com',
'overrides' => ['https://example.com' => 8080],
]);
});
it('clears an fqdn that contains only a hostless domain', function () {
expect(DomainPortOverrides::normalize('https://', ['https://' => 3000]))->toBe([
'fqdn' => null,
'overrides' => null,
]);
});