mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-14 08:18:08 +00:00
fix(compose): normalize service-name keys for domains and env vars (#11040)
This commit is contained in:
1 parent
7b18777f06
commit
0b843bb07c
18 files changed
+1443
-220
No files matched your search
@@ -1343,19 +1343,21 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if ($this->pull_request_id === 0) {
|
||||
// Generate SERVICE_ variables first for dockercompose
|
||||
if ($this->build_pack === 'dockercompose') {
|
||||
$domains = collect(json_decode($this->application->docker_compose_domains)) ?? collect([]);
|
||||
$domains = collect(json_decode($this->application->docker_compose_domains ?: '[]', true) ?: []);
|
||||
|
||||
// Generate SERVICE_FQDN & SERVICE_URL for dockercompose
|
||||
// Env keys always use underscore-normalized names so hyphen/dot storage keys stay valid.
|
||||
foreach ($domains as $forServiceName => $domain) {
|
||||
$parsedDomain = data_get($domain, 'domain');
|
||||
$parsedDomain = composeDomainEntryString($domain);
|
||||
if (filled($parsedDomain)) {
|
||||
$parsedDomain = str($parsedDomain)->explode(',')->first();
|
||||
$coolifyUrl = Url::fromString($parsedDomain);
|
||||
$coolifyScheme = $coolifyUrl->getScheme();
|
||||
$coolifyFqdn = $coolifyUrl->getHost();
|
||||
$coolifyUrl = $coolifyUrl->withScheme($coolifyScheme)->withHost($coolifyFqdn)->withPort(null);
|
||||
$envs->push('SERVICE_URL_'.str($forServiceName)->upper().'='.$coolifyUrl->__toString());
|
||||
$envs->push('SERVICE_FQDN_'.str($forServiceName)->upper().'='.$coolifyFqdn);
|
||||
$serviceEnvKey = str(normalizeComposeServiceName((string) $forServiceName))->upper();
|
||||
$envs->push('SERVICE_URL_'.$serviceEnvKey.'='.$coolifyUrl->__toString());
|
||||
$envs->push('SERVICE_FQDN_'.$serviceEnvKey.'='.$coolifyFqdn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1413,19 +1415,20 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
} else {
|
||||
// Generate SERVICE_ variables first for dockercompose preview
|
||||
if ($this->build_pack === 'dockercompose') {
|
||||
$domains = collect(json_decode(data_get($this->preview, 'docker_compose_domains'))) ?? collect([]);
|
||||
$domains = collect(json_decode(data_get($this->preview, 'docker_compose_domains') ?: '[]', true) ?: []);
|
||||
|
||||
// Generate SERVICE_FQDN & SERVICE_URL for dockercompose
|
||||
foreach ($domains as $forServiceName => $domain) {
|
||||
$parsedDomain = data_get($domain, 'domain');
|
||||
$parsedDomain = composeDomainEntryString($domain);
|
||||
if (filled($parsedDomain)) {
|
||||
$parsedDomain = str($parsedDomain)->explode(',')->first();
|
||||
$coolifyUrl = Url::fromString($parsedDomain);
|
||||
$coolifyScheme = $coolifyUrl->getScheme();
|
||||
$coolifyFqdn = $coolifyUrl->getHost();
|
||||
$coolifyUrl = $coolifyUrl->withScheme($coolifyScheme)->withHost($coolifyFqdn)->withPort(null);
|
||||
$envs->push('SERVICE_URL_'.str($forServiceName)->replace('-', '_')->replace('.', '_')->upper().'='.$coolifyUrl->__toString());
|
||||
$envs->push('SERVICE_FQDN_'.str($forServiceName)->replace('-', '_')->replace('.', '_')->upper().'='.$coolifyFqdn);
|
||||
$serviceEnvKey = str(normalizeComposeServiceName((string) $forServiceName))->upper();
|
||||
$envs->push('SERVICE_URL_'.$serviceEnvKey.'='.$coolifyUrl->__toString());
|
||||
$envs->push('SERVICE_FQDN_'.$serviceEnvKey.'='.$coolifyFqdn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1664,17 +1667,18 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
|
||||
// Generate SERVICE_FQDN & SERVICE_URL for non-PR deployments
|
||||
$domains = collect(json_decode($this->application->docker_compose_domains)) ?? collect([]);
|
||||
$domains = collect(json_decode($this->application->docker_compose_domains ?: '[]', true) ?: []);
|
||||
foreach ($domains as $forServiceName => $domain) {
|
||||
$parsedDomain = data_get($domain, 'domain');
|
||||
$parsedDomain = composeDomainEntryString($domain);
|
||||
if (filled($parsedDomain)) {
|
||||
$parsedDomain = str($parsedDomain)->explode(',')->first();
|
||||
$coolifyUrl = Url::fromString($parsedDomain);
|
||||
$coolifyScheme = $coolifyUrl->getScheme();
|
||||
$coolifyFqdn = $coolifyUrl->getHost();
|
||||
$coolifyUrl = $coolifyUrl->withScheme($coolifyScheme)->withHost($coolifyFqdn)->withPort(null);
|
||||
$envs_dict['SERVICE_URL_'.str($forServiceName)->replace('-', '_')->replace('.', '_')->upper()] = escapeBashEnvValue($coolifyUrl->__toString());
|
||||
$envs_dict['SERVICE_FQDN_'.str($forServiceName)->replace('-', '_')->replace('.', '_')->upper()] = escapeBashEnvValue($coolifyFqdn);
|
||||
$serviceEnvKey = str(normalizeComposeServiceName((string) $forServiceName))->upper();
|
||||
$envs_dict['SERVICE_URL_'.$serviceEnvKey] = escapeBashEnvValue($coolifyUrl->__toString());
|
||||
$envs_dict['SERVICE_FQDN_'.$serviceEnvKey] = escapeBashEnvValue($coolifyFqdn);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1686,17 +1690,18 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
|
||||
// Generate SERVICE_FQDN & SERVICE_URL for preview deployments with PR-specific domains
|
||||
$domains = collect(json_decode(data_get($this->preview, 'docker_compose_domains'))) ?? collect([]);
|
||||
$domains = collect(json_decode(data_get($this->preview, 'docker_compose_domains') ?: '[]', true) ?: []);
|
||||
foreach ($domains as $forServiceName => $domain) {
|
||||
$parsedDomain = data_get($domain, 'domain');
|
||||
$parsedDomain = composeDomainEntryString($domain);
|
||||
if (filled($parsedDomain)) {
|
||||
$parsedDomain = str($parsedDomain)->explode(',')->first();
|
||||
$coolifyUrl = Url::fromString($parsedDomain);
|
||||
$coolifyScheme = $coolifyUrl->getScheme();
|
||||
$coolifyFqdn = $coolifyUrl->getHost();
|
||||
$coolifyUrl = $coolifyUrl->withScheme($coolifyScheme)->withHost($coolifyFqdn)->withPort(null);
|
||||
$envs_dict['SERVICE_URL_'.str($forServiceName)->replace('-', '_')->replace('.', '_')->upper()] = escapeBashEnvValue($coolifyUrl->__toString());
|
||||
$envs_dict['SERVICE_FQDN_'.str($forServiceName)->replace('-', '_')->replace('.', '_')->upper()] = escapeBashEnvValue($coolifyFqdn);
|
||||
$serviceEnvKey = str(normalizeComposeServiceName((string) $forServiceName))->upper();
|
||||
$envs_dict['SERVICE_URL_'.$serviceEnvKey] = escapeBashEnvValue($coolifyUrl->__toString());
|
||||
$envs_dict['SERVICE_FQDN_'.$serviceEnvKey] = escapeBashEnvValue($coolifyFqdn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,12 +301,7 @@ class General extends Component
|
||||
}
|
||||
$this->parsedServiceDomains = $this->application->docker_compose_domains ? json_decode($this->application->docker_compose_domains, true) : [];
|
||||
// Convert service names with dots and dashes to use underscores for HTML form binding
|
||||
$sanitizedDomains = [];
|
||||
foreach ($this->parsedServiceDomains as $serviceName => $domain) {
|
||||
$sanitizedKey = str($serviceName)->replace('-', '_')->replace('.', '_')->toString();
|
||||
$sanitizedDomains[$sanitizedKey] = $domain;
|
||||
}
|
||||
$this->parsedServiceDomains = $sanitizedDomains;
|
||||
$this->parsedServiceDomains = $this->sanitizeParsedServiceDomainsForForm($this->parsedServiceDomains);
|
||||
|
||||
$this->customLabels = $this->application->parseContainerLabels();
|
||||
if (! $this->customLabels && $this->application->destination->server->proxyType() !== 'NONE' && $this->application->settings->is_container_label_readonly_enabled === true) {
|
||||
@@ -520,12 +515,7 @@ class General extends Component
|
||||
|
||||
$this->parsedServiceDomains = $this->application->docker_compose_domains ? json_decode($this->application->docker_compose_domains, true) : [];
|
||||
// Convert service names with dots and dashes to use underscores for HTML form binding
|
||||
$sanitizedDomains = [];
|
||||
foreach ($this->parsedServiceDomains as $serviceName => $domain) {
|
||||
$sanitizedKey = str($serviceName)->replace('-', '_')->replace('.', '_')->toString();
|
||||
$sanitizedDomains[$sanitizedKey] = $domain;
|
||||
}
|
||||
$this->parsedServiceDomains = $sanitizedDomains;
|
||||
$this->parsedServiceDomains = $this->sanitizeParsedServiceDomainsForForm($this->parsedServiceDomains);
|
||||
|
||||
$showToast && $this->dispatch('success', 'Docker compose file loaded.');
|
||||
$this->dispatch('compose_loaded');
|
||||
@@ -551,22 +541,14 @@ class General extends Component
|
||||
|
||||
$uuid = new_public_id();
|
||||
$domain = generateUrl(server: $this->application->destination->server, random: $uuid);
|
||||
$sanitizedKey = str($serviceName)->replace('-', '_')->replace('.', '_')->toString();
|
||||
$sanitizedKey = normalizeComposeServiceName($serviceName);
|
||||
$this->parsedServiceDomains[$sanitizedKey]['domain'] = $domain;
|
||||
|
||||
// Convert back to original service names for storage
|
||||
$originalDomains = [];
|
||||
$composeServiceNames = collect(data_get($this->parsedServices, 'services', []))->keys()->map(fn ($name) => (string) $name)->values()->all();
|
||||
foreach ($this->parsedServiceDomains as $key => $value) {
|
||||
// Find the original service name by checking parsed services
|
||||
$originalServiceName = $key;
|
||||
if (isset($this->parsedServices['services'])) {
|
||||
foreach ($this->parsedServices['services'] as $originalName => $service) {
|
||||
if (str($originalName)->replace('-', '_')->replace('.', '_')->toString() === $key) {
|
||||
$originalServiceName = $originalName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$originalServiceName = findComposeServiceName((string) $key, $composeServiceNames) ?? (string) $key;
|
||||
$originalDomains[$originalServiceName] = $value;
|
||||
}
|
||||
|
||||
@@ -858,9 +840,10 @@ class General extends Component
|
||||
foreach ($this->parsedServiceDomains as $serviceName => $service) {
|
||||
$this->parsedServiceDomains[$serviceName]['domain'] = ValidationPatterns::normalizeApplicationDomains(data_get($service, 'domain'));
|
||||
}
|
||||
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
|
||||
$originalDomains = $this->composeDomainsForStorage();
|
||||
$this->application->docker_compose_domains = json_encode($originalDomains);
|
||||
if ($this->application->isDirty('docker_compose_domains')) {
|
||||
foreach ($this->parsedServiceDomains as $service) {
|
||||
foreach ($originalDomains as $service) {
|
||||
$domain = data_get($service, 'domain');
|
||||
if ($domain) {
|
||||
if (! validateDNSEntry($domain, $this->application->destination->server)) {
|
||||
@@ -985,4 +968,58 @@ class General extends Component
|
||||
'{workdir}/.env'
|
||||
);
|
||||
}
|
||||
|
||||
private function composeDomainsForStorage(): array
|
||||
{
|
||||
return rekeyComposeDomainsToServiceNames(
|
||||
$this->parsedServiceDomains,
|
||||
collect(data_get($this->parsedServices, 'services', []))->keys(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapse domain map keys to underscore form keys for Livewire/HTML binding.
|
||||
* When twin keys exist, prefer a filled domain over a blank one.
|
||||
*
|
||||
* @param array<string, mixed> $domains
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function sanitizeParsedServiceDomainsForForm(array $domains): array
|
||||
{
|
||||
$sanitizedDomains = [];
|
||||
|
||||
foreach ($domains as $serviceName => $domain) {
|
||||
$sanitizedKey = normalizeComposeServiceName((string) $serviceName);
|
||||
if (! array_key_exists($sanitizedKey, $sanitizedDomains)) {
|
||||
$sanitizedDomains[$sanitizedKey] = $domain;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$existing = $sanitizedDomains[$sanitizedKey];
|
||||
if (is_object($existing)) {
|
||||
$existing = (array) $existing;
|
||||
}
|
||||
if (is_object($domain)) {
|
||||
$domain = (array) $domain;
|
||||
}
|
||||
if (! is_array($existing)) {
|
||||
$existing = ['domain' => $existing];
|
||||
}
|
||||
if (! is_array($domain)) {
|
||||
$domain = ['domain' => $domain];
|
||||
}
|
||||
|
||||
$merged = array_merge($existing, $domain);
|
||||
$merged['domain'] = preferComposeDomainValue(
|
||||
$existing['domain'] ?? null,
|
||||
false,
|
||||
$domain['domain'] ?? null,
|
||||
false,
|
||||
);
|
||||
$sanitizedDomains[$sanitizedKey] = $merged;
|
||||
}
|
||||
|
||||
return $sanitizedDomains;
|
||||
}
|
||||
}
|
||||
@@ -39,13 +39,7 @@ class PreviewsCompose extends Component
|
||||
]);
|
||||
|
||||
$this->domain = ValidationPatterns::normalizeApplicationDomains($this->domain);
|
||||
|
||||
$docker_compose_domains = data_get($this->preview, 'docker_compose_domains');
|
||||
$docker_compose_domains = json_decode($docker_compose_domains, true) ?: [];
|
||||
$docker_compose_domains[$this->serviceName] = $docker_compose_domains[$this->serviceName] ?? [];
|
||||
$docker_compose_domains[$this->serviceName]['domain'] = $this->domain;
|
||||
$this->preview->docker_compose_domains = json_encode($docker_compose_domains);
|
||||
$this->preview->save();
|
||||
$this->persistPreviewDomain($this->domain);
|
||||
$this->dispatch('update_links');
|
||||
$this->dispatch('success', 'Domain saved.');
|
||||
} catch (\Throwable $e) {
|
||||
@@ -58,12 +52,8 @@ class PreviewsCompose extends Component
|
||||
try {
|
||||
$this->authorize('update', $this->preview->application);
|
||||
|
||||
$domains = collect(json_decode($this->preview->application->docker_compose_domains, true) ?: []);
|
||||
$domain = $domains->first(function ($_, $key) {
|
||||
return $key === $this->serviceName;
|
||||
});
|
||||
|
||||
$domain_string = data_get($domain, 'domain');
|
||||
$applicationDomains = json_decode($this->preview->application->docker_compose_domains ?: '[]', true) ?: [];
|
||||
$domain_string = getComposeServiceDomainString($applicationDomains, (string) $this->serviceName);
|
||||
|
||||
// If no domain is set in the main application, generate a default domain
|
||||
if (empty($domain_string)) {
|
||||
@@ -111,14 +101,8 @@ class PreviewsCompose extends Component
|
||||
$preview_fqdn = implode(',', $preview_fqdns);
|
||||
}
|
||||
|
||||
// Save the generated domain
|
||||
$this->domain = $preview_fqdn;
|
||||
$docker_compose_domains = data_get($this->preview, 'docker_compose_domains');
|
||||
$docker_compose_domains = json_decode($docker_compose_domains, true) ?: [];
|
||||
$docker_compose_domains[$this->serviceName] = $docker_compose_domains[$this->serviceName] ?? [];
|
||||
$docker_compose_domains[$this->serviceName]['domain'] = $this->domain;
|
||||
$this->preview->docker_compose_domains = json_encode($docker_compose_domains);
|
||||
$this->preview->save();
|
||||
$this->persistPreviewDomain($this->domain);
|
||||
|
||||
$this->dispatch('update_links');
|
||||
$this->dispatch('success', 'Domain generated.');
|
||||
@@ -126,4 +110,56 @@ class PreviewsCompose extends Component
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
private function persistPreviewDomain(?string $domain): void
|
||||
{
|
||||
$docker_compose_domains = json_decode(data_get($this->preview, 'docker_compose_domains') ?: '[]', true) ?: [];
|
||||
$serviceNames = $this->previewServiceNames($docker_compose_domains);
|
||||
$storageKey = findComposeServiceName((string) $this->serviceName, $serviceNames)
|
||||
?? (string) $this->serviceName;
|
||||
|
||||
$docker_compose_domains = putComposeServiceDomain(
|
||||
$docker_compose_domains,
|
||||
$storageKey,
|
||||
$domain,
|
||||
$serviceNames,
|
||||
);
|
||||
$docker_compose_domains = rekeyComposeDomainsToServiceNames($docker_compose_domains, $serviceNames);
|
||||
|
||||
$this->serviceName = $storageKey;
|
||||
$this->preview->docker_compose_domains = json_encode($docker_compose_domains);
|
||||
$this->preview->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $previewDomains
|
||||
* @return list<string>
|
||||
*/
|
||||
private function previewServiceNames(array $previewDomains): array
|
||||
{
|
||||
$parsedServices = $this->preview->application->parse(pull_request_id: $this->preview->pull_request_id);
|
||||
$fromCompose = collect(data_get($parsedServices, 'services', []))
|
||||
->keys()
|
||||
->map(function ($serviceName) {
|
||||
return str((string) $serviceName)
|
||||
->replaceLast('-pr-'.$this->preview->pull_request_id, '')
|
||||
->toString();
|
||||
})
|
||||
->all();
|
||||
|
||||
$domainKeys = collect(array_keys($previewDomains))
|
||||
->merge(array_keys(json_decode($this->preview->application->docker_compose_domains ?: '[]', true) ?: []))
|
||||
->map(fn ($name) => (string) $name);
|
||||
$unmapped = $domainKeys
|
||||
->reject(fn (string $key) => findComposeServiceName($key, $fromCompose) !== null)
|
||||
->all();
|
||||
|
||||
return collect($fromCompose)
|
||||
->merge(preferredComposeServiceNamesFromDomainKeys(
|
||||
$fromCompose === [] ? $domainKeys->all() : $unmapped
|
||||
))
|
||||
->unique()
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
}
|
||||
+26
-28
@@ -2082,34 +2082,7 @@ class Application extends BaseModel
|
||||
$this->save();
|
||||
$parsedServices = $this->parse();
|
||||
if ($this->docker_compose_domains) {
|
||||
$decoded = json_decode($this->docker_compose_domains, true);
|
||||
$json = collect(is_array($decoded) ? $decoded : []);
|
||||
$normalized = collect();
|
||||
foreach ($json as $key => $value) {
|
||||
$normalizedKey = (string) str($key)->replace('-', '_')->replace('.', '_');
|
||||
$normalized->put($normalizedKey, $value);
|
||||
}
|
||||
$json = $normalized;
|
||||
$services = collect(data_get($parsedServices, 'services', []));
|
||||
foreach ($services as $name => $service) {
|
||||
if (str($name)->contains('-') || str($name)->contains('.')) {
|
||||
$replacedName = str($name)->replace('-', '_')->replace('.', '_');
|
||||
$services->put((string) $replacedName, $service);
|
||||
$services->forget((string) $name);
|
||||
}
|
||||
}
|
||||
$names = collect($services)->keys()->toArray();
|
||||
$jsonNames = $json->keys()->toArray();
|
||||
$diff = array_diff($jsonNames, $names);
|
||||
$json = $json->filter(function ($value, $key) use ($diff) {
|
||||
return ! in_array($key, $diff);
|
||||
});
|
||||
if ($json) {
|
||||
$this->docker_compose_domains = json_encode($json);
|
||||
} else {
|
||||
$this->docker_compose_domains = null;
|
||||
}
|
||||
$this->save();
|
||||
$this->reconcileDockerComposeDomains($parsedServices);
|
||||
}
|
||||
|
||||
return [
|
||||
@@ -2126,6 +2099,31 @@ class Application extends BaseModel
|
||||
}
|
||||
}
|
||||
|
||||
private function reconcileDockerComposeDomains(mixed $parsedServices): void
|
||||
{
|
||||
$services = collect(data_get($parsedServices, 'services', []));
|
||||
$serviceNames = $services->keys()->map(fn ($name) => (string) $name)->all();
|
||||
|
||||
if ($serviceNames === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
$decoded = json_decode($this->docker_compose_domains, true);
|
||||
$rekeyed = rekeyComposeDomainsToServiceNames(
|
||||
is_array($decoded) ? $decoded : [],
|
||||
$serviceNames,
|
||||
);
|
||||
|
||||
$domains = collect($rekeyed)->filter(
|
||||
fn ($value, $key) => findComposeServiceName((string) $key, $serviceNames) !== null
|
||||
);
|
||||
|
||||
$this->docker_compose_domains = $domains->isNotEmpty()
|
||||
? json_encode($domains->all())
|
||||
: null;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
public function parseContainerLabels(?ApplicationPreview $preview = null)
|
||||
{
|
||||
$customLabels = data_get($this, 'custom_labels');
|
||||
|
||||
@@ -124,34 +124,55 @@ class ApplicationPreview extends BaseModel
|
||||
|
||||
public function generate_preview_fqdn_compose()
|
||||
{
|
||||
$services = collect(json_decode($this->application->docker_compose_domains)) ?? collect();
|
||||
$docker_compose_domains = data_get($this, 'docker_compose_domains');
|
||||
$docker_compose_domains = json_decode($docker_compose_domains, true) ?? [];
|
||||
$applicationDomains = json_decode($this->application->docker_compose_domains ?: '[]', true) ?: [];
|
||||
$previewDomains = json_decode(data_get($this, 'docker_compose_domains') ?: '[]', true) ?: [];
|
||||
|
||||
// Get all services from the parsed compose file to ensure all services have entries
|
||||
$parsedServices = $this->application->parse(pull_request_id: $this->pull_request_id);
|
||||
if (isset($parsedServices['services'])) {
|
||||
foreach ($parsedServices['services'] as $serviceName => $service) {
|
||||
if (! isDatabaseImage(data_get($service, 'image'))) {
|
||||
// Remove PR suffix from service name to get original service name
|
||||
$originalServiceName = str($serviceName)->replaceLast('-pr-'.$this->pull_request_id, '')->toString();
|
||||
$composeServiceNames = $this->composeServiceNamesForPreview();
|
||||
// Canonical compose names first; collapse leftover domain-map twins when parse is empty/missing.
|
||||
$domainKeys = collect(array_keys($applicationDomains))
|
||||
->merge(array_keys($previewDomains))
|
||||
->map(fn ($name) => (string) $name);
|
||||
$unmappedDomainKeys = $domainKeys
|
||||
->reject(fn (string $key) => findComposeServiceName($key, $composeServiceNames) !== null)
|
||||
->all();
|
||||
$knownServiceNames = collect($composeServiceNames)
|
||||
->merge(preferredComposeServiceNamesFromDomainKeys(
|
||||
$composeServiceNames === [] ? $domainKeys->all() : $unmappedDomainKeys
|
||||
))
|
||||
->unique()
|
||||
->values()
|
||||
->all();
|
||||
|
||||
// Ensure all services have an entry, even if empty
|
||||
if (! $services->has($originalServiceName)) {
|
||||
$services->put($originalServiceName, ['domain' => '']);
|
||||
}
|
||||
}
|
||||
$applicationDomains = rekeyComposeDomainsToServiceNames($applicationDomains, $knownServiceNames);
|
||||
$previewDomains = rekeyComposeDomainsToServiceNames($previewDomains, $knownServiceNames);
|
||||
|
||||
// Ensure every non-database compose service has a domain slot (empty if unset).
|
||||
foreach ($composeServiceNames as $serviceName) {
|
||||
if (! array_key_exists($serviceName, $applicationDomains)) {
|
||||
$applicationDomains[$serviceName] = ['domain' => ''];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($services as $service_name => $service_config) {
|
||||
$domain_string = data_get($service_config, 'domain');
|
||||
$serviceNames = collect(array_keys($applicationDomains))
|
||||
->merge($composeServiceNames)
|
||||
->map(fn ($name) => (string) $name)
|
||||
->unique()
|
||||
->values()
|
||||
->all();
|
||||
|
||||
$docker_compose_domains = [];
|
||||
foreach ($serviceNames as $service_name) {
|
||||
$domain_string = getComposeServiceDomainString($applicationDomains, $service_name);
|
||||
|
||||
// If domain string is empty or null, don't auto-generate domain
|
||||
// Only generate domains when main app already has domains set
|
||||
if (empty($domain_string)) {
|
||||
// Ensure service has an empty domain entry for form binding
|
||||
$docker_compose_domains[$service_name]['domain'] = '';
|
||||
$docker_compose_domains = putComposeServiceDomain(
|
||||
$docker_compose_domains,
|
||||
$service_name,
|
||||
'',
|
||||
$serviceNames,
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -180,19 +201,22 @@ class ApplicationPreview extends BaseModel
|
||||
$preview_domains[] = $preview_fqdn;
|
||||
}
|
||||
|
||||
if (! empty($preview_domains)) {
|
||||
$docker_compose_domains[$service_name]['domain'] = implode(',', $preview_domains);
|
||||
} else {
|
||||
// Ensure service has an empty domain entry for form binding
|
||||
$docker_compose_domains[$service_name]['domain'] = '';
|
||||
}
|
||||
$docker_compose_domains = putComposeServiceDomain(
|
||||
$docker_compose_domains,
|
||||
$service_name,
|
||||
! empty($preview_domains) ? implode(',', $preview_domains) : '',
|
||||
$serviceNames,
|
||||
);
|
||||
}
|
||||
|
||||
// Drop any leftover twin keys that were not rewritten above.
|
||||
$docker_compose_domains = rekeyComposeDomainsToServiceNames($docker_compose_domains, $serviceNames);
|
||||
|
||||
$this->docker_compose_domains = json_encode($docker_compose_domains);
|
||||
|
||||
// Populate fqdn from generated domains so webhook notifications can read it
|
||||
$allDomains = collect($docker_compose_domains)
|
||||
->pluck('domain')
|
||||
->map(fn ($entry) => composeDomainEntryString($entry))
|
||||
->filter(fn ($d) => ! empty($d))
|
||||
->flatMap(fn ($d) => explode(',', $d))
|
||||
->implode(',');
|
||||
@@ -201,4 +225,31 @@ class ApplicationPreview extends BaseModel
|
||||
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Original compose service names for this preview (PR suffix stripped), excluding database images.
|
||||
*
|
||||
* @return list<string>
|
||||
*/
|
||||
private function composeServiceNamesForPreview(): array
|
||||
{
|
||||
$parsedServices = $this->application->parse(pull_request_id: $this->pull_request_id);
|
||||
$services = data_get($parsedServices, 'services', []);
|
||||
if (! is_iterable($services)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$names = [];
|
||||
foreach ($services as $serviceName => $service) {
|
||||
if (isDatabaseImage(data_get($service, 'image'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$names[] = str((string) $serviceName)
|
||||
->replaceLast('-pr-'.$this->pull_request_id, '')
|
||||
->toString();
|
||||
}
|
||||
|
||||
return array_values(array_unique($names));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user