diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index c4a71d599..109c72884 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -471,7 +471,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue } else { $composeFile = $this->application->parse(pull_request_id: $this->pull_request_id, preview_id: data_get($this->preview, 'id')); $this->save_environment_variables(); - if (! is_null($this->env_filename)) { + if (filled($this->env_filename)) { $services = collect(data_get($composeFile, 'services', [])); $services = $services->map(function ($service, $name) { $service['env_file'] = [$this->env_filename]; @@ -480,7 +480,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue }); $composeFile['services'] = $services->toArray(); } - if (is_null($composeFile)) { + if (empty($composeFile)) { $this->application_deployment_queue->addLogEntry('Failed to parse docker-compose file.'); $this->fail('Failed to parse docker-compose file.'); @@ -887,10 +887,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue private function save_environment_variables() { $envs = collect([]); - $local_branch = $this->branch; - if ($this->pull_request_id !== 0) { - $local_branch = "pull/{$this->pull_request_id}/head"; - } $sort = $this->application->settings->is_env_sorting_enabled; if ($sort) { $sorted_environment_variables = $this->application->environment_variables->sortBy('key'); @@ -899,6 +895,14 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue $sorted_environment_variables = $this->application->environment_variables->sortBy('id'); $sorted_environment_variables_preview = $this->application->environment_variables_preview->sortBy('id'); } + if ($this->build_pack === 'dockercompose') { + $sorted_environment_variables = $sorted_environment_variables->filter(function ($env) { + return ! str($env->key)->startsWith('SERVICE_FQDN_') && ! str($env->key)->startsWith('SERVICE_URL_'); + }); + $sorted_environment_variables_preview = $sorted_environment_variables_preview->filter(function ($env) { + return ! str($env->key)->startsWith('SERVICE_FQDN_') && ! str($env->key)->startsWith('SERVICE_URL_'); + }); + } $ports = $this->application->main_port(); $coolify_envs = $this->generate_coolify_env_variables(); $coolify_envs->each(function ($item, $key) use ($envs) { @@ -920,6 +924,22 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue if ($this->application->environment_variables->where('key', 'HOST')->isEmpty()) { $envs->push('HOST=0.0.0.0'); } + + if ($this->build_pack === 'dockercompose') { + $domains = collect(json_decode($this->application->docker_compose_domains)) ?? collect([]); + + // Generate SERVICE_FQDN & SERVICE_URL for dockercompose + foreach ($domains as $forServiceName => $domain) { + $parsedDomain = data_get($domain, 'domain'); + if (filled($parsedDomain)) { + $parsedDomain = str($parsedDomain)->explode(',')->first(); + $coolifyUrl = str($parsedDomain)->after('://')->before(':')->prepend(str($parsedDomain)->before('://')->append('://')); + $coolifyFqdn = str($parsedDomain)->replace('http://', '')->replace('https://', '')->before(':'); + $envs->push('SERVICE_URL_'.str($forServiceName)->upper().'='.$coolifyUrl->value()); + $envs->push('SERVICE_FQDN_'.str($forServiceName)->upper().'='.$coolifyFqdn->value()); + } + } + } } else { $this->env_filename = ".env-pr-$this->pull_request_id"; foreach ($sorted_environment_variables_preview as $env) { @@ -936,6 +956,21 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue $envs->push('HOST=0.0.0.0'); } + if ($this->build_pack === 'dockercompose') { + $domains = collect(json_decode(data_get($this->preview, 'docker_compose_domains'))) ?? collect([]); + + // Generate SERVICE_FQDN & SERVICE_URL for dockercompose + foreach ($domains as $forServiceName => $domain) { + $parsedDomain = data_get($domain, 'domain'); + if (filled($parsedDomain)) { + $parsedDomain = str($parsedDomain)->explode(',')->first(); + $coolifyUrl = str($parsedDomain)->after('://')->before(':')->prepend(str($parsedDomain)->before('://')->append('://')); + $coolifyFqdn = str($parsedDomain)->replace('http://', '')->replace('https://', '')->before(':'); + $envs->push('SERVICE_URL_'.str($forServiceName)->upper().'='.$coolifyUrl->value()); + $envs->push('SERVICE_FQDN_'.str($forServiceName)->upper().'='.$coolifyFqdn->value()); + } + } + } } if ($envs->isEmpty()) { $this->env_filename = null; @@ -1702,10 +1737,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue { $this->create_workdir(); $ports = $this->application->main_port(); - $onlyPort = null; - if (count($ports) > 0) { - $onlyPort = $ports[0]; - } $persistent_storages = $this->generate_local_persistent_volumes(); $persistent_file_volumes = $this->application->fileStorages()->get(); $volume_names = $this->generate_local_persistent_volumes_only_volume_names(); @@ -2240,9 +2271,10 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); $this->application_deployment_queue->addLogEntry('Building docker image completed.'); } - private function graceful_shutdown_container(string $containerName, int $timeout = 30) + private function graceful_shutdown_container(string $containerName) { try { + $timeout = isDev() ? 1 : 30; $this->execute_remote_command( ["docker stop --time=$timeout $containerName", 'hidden' => true, 'ignore_errors' => true], ["docker rm -f $containerName", 'hidden' => true, 'ignore_errors' => true] diff --git a/app/Models/ApplicationPreview.php b/app/Models/ApplicationPreview.php index bf2bf05bf..c635f146a 100644 --- a/app/Models/ApplicationPreview.php +++ b/app/Models/ApplicationPreview.php @@ -52,24 +52,38 @@ class ApplicationPreview extends BaseModel public function generate_preview_fqdn_compose() { - $domains = collect(json_decode($this->application->docker_compose_domains)) ?? collect(); - foreach ($domains as $service_name => $domain) { - $domain = data_get($domain, 'domain'); - $url = Url::fromString($domain); - $template = $this->application->preview_url_template; - $host = $url->getHost(); - $schema = $url->getScheme(); - $random = new Cuid2; - $preview_fqdn = str_replace('{{random}}', $random, $template); - $preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn); - $preview_fqdn = str_replace('{{pr_id}}', $this->pull_request_id, $preview_fqdn); - $preview_fqdn = "$schema://$preview_fqdn"; - $docker_compose_domains = data_get($this, 'docker_compose_domains'); - $docker_compose_domains = json_decode($docker_compose_domains, true); - $docker_compose_domains[$service_name]['domain'] = $preview_fqdn; - $docker_compose_domains = json_encode($docker_compose_domains); - $this->docker_compose_domains = $docker_compose_domains; - $this->save(); + $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) ?? []; + + foreach ($services as $service_name => $service_config) { + $domain_string = data_get($service_config, 'domain'); + $service_domains = str($domain_string)->explode(',')->map(fn ($d) => trim($d)); + + $preview_domains = []; + foreach ($service_domains as $domain) { + if (empty($domain)) { + continue; + } + + $url = Url::fromString($domain); + $template = $this->application->preview_url_template; + $host = $url->getHost(); + $schema = $url->getScheme(); + $random = new Cuid2; + $preview_fqdn = str_replace('{{random}}', $random, $template); + $preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn); + $preview_fqdn = str_replace('{{pr_id}}', $this->pull_request_id, $preview_fqdn); + $preview_fqdn = "$schema://$preview_fqdn"; + $preview_domains[] = $preview_fqdn; + } + + if (! empty($preview_domains)) { + $docker_compose_domains[$service_name]['domain'] = implode(',', $preview_domains); + } } + + $this->docker_compose_domains = json_encode($docker_compose_domains); + $this->save(); } } diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index b8497e2d5..e62185cc1 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -3147,6 +3147,9 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int continue; } if ($command->value() === 'FQDN') { + if ($isApplication && $resource->build_pack === 'dockercompose') { + continue; + } $fqdnFor = $key->after('SERVICE_FQDN_')->lower()->value(); if (str($fqdnFor)->contains('_')) { $fqdnFor = str($fqdnFor)->before('_'); @@ -3162,6 +3165,9 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int 'is_preview' => false, ]); } elseif ($command->value() === 'URL') { + if ($isApplication && $resource->build_pack === 'dockercompose') { + continue; + } $fqdnFor = $key->after('SERVICE_URL_')->lower()->value(); if (str($fqdnFor)->contains('_')) { $fqdnFor = str($fqdnFor)->before('_'); @@ -3651,9 +3657,28 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int } if ($isApplication) { - $domains = collect(json_decode($resource->docker_compose_domains)) ?? collect([]); + if ($isPullRequest) { + $preview = $resource->previews()->find($preview_id); + $domains = collect(json_decode(data_get($preview, 'docker_compose_domains'))) ?? collect([]); + } else { + $domains = collect(json_decode($resource->docker_compose_domains)) ?? collect([]); + } $fqdns = data_get($domains, "$serviceName.domain"); - if ($fqdns) { + // Generate SERVICE_FQDN & SERVICE_URL for dockercompose + if ($resource->build_pack === 'dockercompose') { + foreach ($domains as $forServiceName => $domain) { + $parsedDomain = data_get($domain, 'domain'); + if (filled($parsedDomain)) { + $parsedDomain = str($parsedDomain)->explode(',')->first(); + $coolifyUrl = str($parsedDomain)->after('://')->before(':')->prepend(str($parsedDomain)->before('://')->append('://')); + $coolifyFqdn = str($parsedDomain)->replace('http://', '')->replace('https://', '')->before(':'); + $coolifyEnvironments->put('SERVICE_URL_'.str($forServiceName)->upper(), $coolifyUrl->value()); + $coolifyEnvironments->put('SERVICE_FQDN_'.str($forServiceName)->upper(), $coolifyFqdn->value()); + } + } + } + // If the domain is set, we need to generate the FQDNs for the preview + if (filled($fqdns)) { $fqdns = str($fqdns)->explode(','); if ($isPullRequest) { $preview = $resource->previews()->find($preview_id); @@ -3685,7 +3710,6 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int } } } - $defaultLabels = defaultLabels( id: $resource->id, name: $containerName, @@ -3695,6 +3719,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int type: 'application', environment: $resource->environment->name, ); + } elseif ($isService) { if ($savedService->serviceType()) { $fqdns = generateServiceSpecificFqdns($savedService);