From 74ad6d2d81ed5f3a5e8c81e2213ef7b8211e0324 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Thu, 2 Oct 2025 08:49:28 +0200 Subject: [PATCH 1/5] chore(versions): update version numbers for Coolify releases - Incremented the beta version for both the stable and nightly releases of Coolify to reflect the latest changes. - Updated version from 4.0.0-beta.433 to 4.0.0-beta.434 for stable and from 4.0.0-beta.434 to 4.0.0-beta.435 for nightly. --- versions.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/versions.json b/versions.json index b5cf3360a..cb9fb7dc2 100644 --- a/versions.json +++ b/versions.json @@ -1,10 +1,10 @@ { "coolify": { "v4": { - "version": "4.0.0-beta.433" + "version": "4.0.0-beta.434" }, "nightly": { - "version": "4.0.0-beta.434" + "version": "4.0.0-beta.435" }, "helper": { "version": "1.0.11" From da6c7ed7d5c3b9e3d0ad053664e89a8bee9cd9e9 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Thu, 2 Oct 2025 08:49:35 +0200 Subject: [PATCH 2/5] chore(versions): bump Coolify stable version to 4.0.0-beta.434 - Updated the stable version number in constants.php to reflect the latest release. - Ensured consistency with the previous versioning scheme. --- config/constants.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/constants.php b/config/constants.php index 749d6435b..ddda70d19 100644 --- a/config/constants.php +++ b/config/constants.php @@ -2,7 +2,7 @@ return [ 'coolify' => [ - 'version' => '4.0.0-beta.433', + 'version' => '4.0.0-beta.434', 'helper_version' => '1.0.11', 'realtime_version' => '1.0.10', 'self_hosted' => env('SELF_HOSTED', true), From aadde3a83e0eebdc53ee0e6db2c60326935b6a25 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Thu, 2 Oct 2025 13:54:36 +0200 Subject: [PATCH 3/5] feat(deployments): enhance Docker build argument handling for multiline variables - Introduced new helper functions to generate Docker build arguments and environment flags, accommodating multiline variables with proper escaping. - Updated the ApplicationDeploymentJob to utilize these new functions, improving the handling of environment variables during deployment. - Added comprehensive tests to ensure correct behavior for multiline variables and special characters. --- app/Jobs/ApplicationDeploymentJob.php | 32 ++- bootstrap/helpers/docker.php | 61 +++++ .../MultilineEnvironmentVariableTest.php | 208 ++++++++++++++++++ 3 files changed, 292 insertions(+), 9 deletions(-) create mode 100644 tests/Feature/MultilineEnvironmentVariableTest.php diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index b3f9793c7..04b11b9b4 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -2779,12 +2779,23 @@ private function generate_build_env_variables() $secrets_hash = $this->generate_secrets_hash($variables); } - $this->build_args = $variables->map(function ($value, $key) { - $value = escapeshellarg($value); + $env_vars = $this->pull_request_id === 0 + ? $this->application->environment_variables()->where('is_buildtime', true)->get() + : $this->application->environment_variables_preview()->where('is_buildtime', true)->get(); - return "--build-arg {$key}={$value}"; + // Map variables to include is_multiline flag + $vars_with_metadata = $variables->map(function ($value, $key) use ($env_vars) { + $env = $env_vars->firstWhere('key', $key); + + return [ + 'key' => $key, + 'value' => $value, + 'is_multiline' => $env ? $env->is_multiline : false, + ]; }); + $this->build_args = generateDockerBuildArgs($vars_with_metadata); + if ($secrets_hash) { $this->build_args->push("--build-arg COOLIFY_BUILD_SECRETS_HASH={$secrets_hash}"); } @@ -2807,14 +2818,17 @@ private function generate_docker_env_flags_for_secrets() } $secrets_hash = $this->generate_secrets_hash($variables); - $env_flags = $variables - ->map(function ($env) { - $escaped_value = escapeshellarg($env->real_value); - return "-e {$env->key}={$escaped_value}"; - }) - ->implode(' '); + // Map to simple array format for the helper function + $vars_array = $variables->map(function ($env) { + return [ + 'key' => $env->key, + 'value' => $env->real_value, + 'is_multiline' => $env->is_multiline, + ]; + }); + $env_flags = generateDockerEnvFlags($vars_array); $env_flags .= " -e COOLIFY_BUILD_SECRETS_HASH={$secrets_hash}"; return $env_flags; diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php index 1491e4712..af26c97bd 100644 --- a/bootstrap/helpers/docker.php +++ b/bootstrap/helpers/docker.php @@ -1119,3 +1119,64 @@ function escapeDollarSign($value) return str_replace($search, $replace, $value); } + +/** + * Generate Docker build arguments from environment variables collection + * + * @param \Illuminate\Support\Collection|array $variables Collection of variables with 'key', 'value', and optionally 'is_multiline' + * @return \Illuminate\Support\Collection Collection of formatted --build-arg strings + */ +function generateDockerBuildArgs($variables): \Illuminate\Support\Collection +{ + $variables = collect($variables); + + return $variables->map(function ($var) { + $key = is_array($var) ? data_get($var, 'key') : $var->key; + $value = is_array($var) ? data_get($var, 'value') : $var->value; + $isMultiline = is_array($var) ? data_get($var, 'is_multiline', false) : ($var->is_multiline ?? false); + + if ($isMultiline) { + // For multiline variables, strip surrounding quotes and escape for bash + $raw_value = trim($value, "'"); + $escaped_value = str_replace(['\\', '"', '$', '`'], ['\\\\', '\\"', '\\$', '\\`'], $raw_value); + + return "--build-arg {$key}=\"{$escaped_value}\""; + } + + // For regular variables, use escapeshellarg for security + $value = escapeshellarg($value); + + return "--build-arg {$key}={$value}"; + }); +} + +/** + * Generate Docker environment flags from environment variables collection + * + * @param \Illuminate\Support\Collection|array $variables Collection of variables with 'key', 'value', and optionally 'is_multiline' + * @return string Space-separated environment flags + */ +function generateDockerEnvFlags($variables): string +{ + $variables = collect($variables); + + return $variables + ->map(function ($var) { + $key = is_array($var) ? data_get($var, 'key') : $var->key; + $value = is_array($var) ? data_get($var, 'value') : $var->value; + $isMultiline = is_array($var) ? data_get($var, 'is_multiline', false) : ($var->is_multiline ?? false); + + if ($isMultiline) { + // For multiline variables, strip surrounding quotes and escape for bash + $raw_value = trim($value, "'"); + $escaped_value = str_replace(['\\', '"', '$', '`'], ['\\\\', '\\"', '\\$', '\\`'], $raw_value); + + return "-e {$key}=\"{$escaped_value}\""; + } + + $escaped_value = escapeshellarg($value); + + return "-e {$key}={$escaped_value}"; + }) + ->implode(' '); +} diff --git a/tests/Feature/MultilineEnvironmentVariableTest.php b/tests/Feature/MultilineEnvironmentVariableTest.php new file mode 100644 index 000000000..e32a2ce99 --- /dev/null +++ b/tests/Feature/MultilineEnvironmentVariableTest.php @@ -0,0 +1,208 @@ + 'SSH_PRIVATE_KEY', 'value' => "'{$sshKey}'", 'is_multiline' => true], + ['key' => 'REGULAR_VAR', 'value' => 'simple value', 'is_multiline' => false], + ]; + + $buildArgs = generateDockerBuildArgs($variables); + + // SSH key should use double quotes and have proper escaping + $sshArg = $buildArgs->first(); + expect($sshArg)->toStartWith('--build-arg SSH_PRIVATE_KEY="'); + expect($sshArg)->toEndWith('"'); + expect($sshArg)->toContain('BEGIN OPENSSH PRIVATE KEY'); + expect($sshArg)->not->toContain("'BEGIN"); // Should not have the wrapper single quotes + + // Regular var should use escapeshellarg (single quotes) + $regularArg = $buildArgs->last(); + expect($regularArg)->toBe("--build-arg REGULAR_VAR='simple value'"); +}); + +test('multiline variables with special bash characters are escaped correctly', function () { + $valueWithSpecialChars = "line1\nline2 with \"quotes\"\nline3 with \$variables\nline4 with `backticks`"; + + $variables = [ + ['key' => 'SPECIAL_VALUE', 'value' => "'{$valueWithSpecialChars}'", 'is_multiline' => true], + ]; + + $buildArgs = generateDockerBuildArgs($variables); + $arg = $buildArgs->first(); + + // Verify double quotes are escaped + expect($arg)->toContain('\\"quotes\\"'); + // Verify dollar signs are escaped + expect($arg)->toContain('\\$variables'); + // Verify backticks are escaped + expect($arg)->toContain('\\`backticks\\`'); +}); + +test('single-line environment variables use escapeshellarg', function () { + $variables = [ + ['key' => 'SIMPLE_VAR', 'value' => 'simple value with spaces', 'is_multiline' => false], + ]; + + $buildArgs = generateDockerBuildArgs($variables); + $arg = $buildArgs->first(); + + // Should use single quotes from escapeshellarg + expect($arg)->toBe("--build-arg SIMPLE_VAR='simple value with spaces'"); +}); + +test('multiline certificate with newlines is preserved', function () { + $certificate = '-----BEGIN CERTIFICATE----- +MIIDXTCCAkWgAwIBAgIJAKL0UG+mRkSvMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTkwOTE3MDUzMzI5WhcNMjkwOTE0MDUzMzI5WjBF +-----END CERTIFICATE-----'; + + $variables = [ + ['key' => 'TLS_CERT', 'value' => "'{$certificate}'", 'is_multiline' => true], + ]; + + $buildArgs = generateDockerBuildArgs($variables); + $arg = $buildArgs->first(); + + // Newlines should be preserved in the output + expect($arg)->toContain("\n"); + expect($arg)->toContain('BEGIN CERTIFICATE'); + expect($arg)->toContain('END CERTIFICATE'); + expect(substr_count($arg, "\n"))->toBeGreaterThan(0); +}); + +test('multiline JSON configuration is properly escaped', function () { + $jsonConfig = '{ + "key": "value", + "nested": { + "array": [1, 2, 3] + } +}'; + + $variables = [ + ['key' => 'JSON_CONFIG', 'value' => "'{$jsonConfig}'", 'is_multiline' => true], + ]; + + $buildArgs = generateDockerBuildArgs($variables); + $arg = $buildArgs->first(); + + // All double quotes in JSON should be escaped + expect($arg)->toContain('\\"key\\"'); + expect($arg)->toContain('\\"value\\"'); + expect($arg)->toContain('\\"nested\\"'); +}); + +test('empty multiline variable is handled correctly', function () { + $variables = [ + ['key' => 'EMPTY_VAR', 'value' => "''", 'is_multiline' => true], + ]; + + $buildArgs = generateDockerBuildArgs($variables); + $arg = $buildArgs->first(); + + expect($arg)->toBe('--build-arg EMPTY_VAR=""'); +}); + +test('multiline variable with only newlines', function () { + $onlyNewlines = "\n\n\n"; + + $variables = [ + ['key' => 'NEWLINES_ONLY', 'value' => "'{$onlyNewlines}'", 'is_multiline' => true], + ]; + + $buildArgs = generateDockerBuildArgs($variables); + $arg = $buildArgs->first(); + + expect($arg)->toContain("\n"); + // Should have 3 newlines preserved + expect(substr_count($arg, "\n"))->toBe(3); +}); + +test('multiline variable with backslashes is escaped correctly', function () { + $valueWithBackslashes = "path\\to\\file\nC:\\Windows\\System32"; + + $variables = [ + ['key' => 'PATH_VAR', 'value' => "'{$valueWithBackslashes}'", 'is_multiline' => true], + ]; + + $buildArgs = generateDockerBuildArgs($variables); + $arg = $buildArgs->first(); + + // Backslashes should be doubled + expect($arg)->toContain('path\\\\to\\\\file'); + expect($arg)->toContain('C:\\\\Windows\\\\System32'); +}); + +test('generateDockerEnvFlags produces correct format', function () { + $variables = [ + ['key' => 'NORMAL_VAR', 'value' => 'value', 'is_multiline' => false], + ['key' => 'MULTILINE_VAR', 'value' => "'line1\nline2'", 'is_multiline' => true], + ]; + + $envFlags = generateDockerEnvFlags($variables); + + expect($envFlags)->toContain('-e NORMAL_VAR='); + expect($envFlags)->toContain('-e MULTILINE_VAR="'); + expect($envFlags)->toContain('line1'); + expect($envFlags)->toContain('line2'); +}); + +test('helper functions work with collection input', function () { + $variables = collect([ + (object) ['key' => 'VAR1', 'value' => 'value1', 'is_multiline' => false], + (object) ['key' => 'VAR2', 'value' => "'multiline\nvalue'", 'is_multiline' => true], + ]); + + $buildArgs = generateDockerBuildArgs($variables); + expect($buildArgs)->toHaveCount(2); + + $envFlags = generateDockerEnvFlags($variables); + expect($envFlags)->toBeString(); + expect($envFlags)->toContain('-e VAR1='); + expect($envFlags)->toContain('-e VAR2="'); +}); + +test('variables without is_multiline default to false', function () { + $variables = [ + ['key' => 'NO_FLAG_VAR', 'value' => 'some value'], + ]; + + $buildArgs = generateDockerBuildArgs($variables); + $arg = $buildArgs->first(); + + // Should use escapeshellarg (single quotes) since is_multiline defaults to false + expect($arg)->toBe("--build-arg NO_FLAG_VAR='some value'"); +}); + +test('real world SSH key example', function () { + // Simulate what real_value returns (wrapped in single quotes) + $sshKey = "'-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevAAAAJi/QySHv0Mk +hwAAAAtzc2gtZWQyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevA +AAAECBQw4jg1WRT2IGHMncCiZhURCts2s24HoDS0thHnnRKVuGmoeGq/pojrsyP1pszcNV +uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== +-----END OPENSSH PRIVATE KEY-----'"; + + $variables = [ + ['key' => 'KEY', 'value' => $sshKey, 'is_multiline' => true], + ]; + + $buildArgs = generateDockerBuildArgs($variables); + $arg = $buildArgs->first(); + + // Should produce clean output without wrapper quotes + expect($arg)->toStartWith('--build-arg KEY="-----BEGIN OPENSSH PRIVATE KEY-----'); + expect($arg)->toEndWith('-----END OPENSSH PRIVATE KEY-----"'); + // Should NOT have the escaped quote sequence that was in the bug + expect($arg)->not->toContain("''"); + expect($arg)->not->toContain("'\\''"); +}); From d63802e03d16dde3ef7b3ebc4804ae18d8dbe22d Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Thu, 2 Oct 2025 18:34:39 +0200 Subject: [PATCH 4/5] feat(deployments): add log copying functionality to clipboard in dev --- .../Project/Application/DeploymentNavbar.php | 22 +++++++++++++++++++ .../application/deployment-navbar.blade.php | 3 +++ 2 files changed, 25 insertions(+) diff --git a/app/Livewire/Project/Application/DeploymentNavbar.php b/app/Livewire/Project/Application/DeploymentNavbar.php index dccd1e499..ebdc014ae 100644 --- a/app/Livewire/Project/Application/DeploymentNavbar.php +++ b/app/Livewire/Project/Application/DeploymentNavbar.php @@ -50,6 +50,28 @@ public function force_start() } } + public function copyLogsToClipboard(): string + { + $logs = json_decode($this->application_deployment_queue->logs, associative: true, flags: JSON_THROW_ON_ERROR); + + if (! $logs) { + return ''; + } + + $markdown = "# Deployment Logs\n\n"; + $markdown .= "```\n"; + + foreach ($logs as $log) { + if (isset($log['output'])) { + $markdown .= $log['output']."\n"; + } + } + + $markdown .= "```\n"; + + return $markdown; + } + public function cancel() { $deployment_uuid = $this->application_deployment_queue->deployment_uuid; diff --git a/resources/views/livewire/project/application/deployment-navbar.blade.php b/resources/views/livewire/project/application/deployment-navbar.blade.php index effb6b6fe..60c660bf7 100644 --- a/resources/views/livewire/project/application/deployment-navbar.blade.php +++ b/resources/views/livewire/project/application/deployment-navbar.blade.php @@ -5,6 +5,9 @@ @else Show Debug Logs @endif + @if (isDev()) + Copy Logs + @endif @if (data_get($application_deployment_queue, 'status') === 'queued') Force Start @endif From 2216832f67efef68b2af1649bf03101dc8c2ff01 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 3 Oct 2025 09:20:05 +0200 Subject: [PATCH 5/5] fix(deployments): enhance builder container management and environment variable handling - Added a new method to restart the builder container with the actual commit value, ensuring accurate deployment. - Improved the generation of environment variables by consolidating user-defined and Coolify-specific variables. - Updated Dockerfile modification logic to handle environment variables more effectively, including support for multiline variables. - Enhanced logging for better visibility during deployment processes. --- app/Jobs/ApplicationDeploymentJob.php | 116 +++++++++++++++++--------- 1 file changed, 76 insertions(+), 40 deletions(-) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 04b11b9b4..965eab68e 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -1526,7 +1526,6 @@ private function prepare_builder_image() $this->dockerConfigFileExists = instant_remote_process(["test -f {$this->serverUserHomeDir}/.docker/config.json && echo 'OK' || echo 'NOK'"], $this->server); $env_flags = $this->generate_docker_env_flags_for_secrets(); - if ($this->use_build_server) { if ($this->dockerConfigFileExists === 'NOK') { throw new RuntimeException('Docker config file (~/.docker/config.json) not found on the build server. Please run "docker login" to login to the docker registry on the server.'); @@ -1553,6 +1552,18 @@ private function prepare_builder_image() $this->run_pre_deployment_command(); } + private function restart_builder_container_with_actual_commit() + { + // Stop and remove the current helper container + $this->graceful_shutdown_container($this->deployment_uuid); + + // Clear cached env_args to force regeneration with actual SOURCE_COMMIT value + $this->env_args = null; + + // Restart the helper container with updated environment variables (including actual SOURCE_COMMIT) + $this->prepare_builder_image(); + } + private function deploy_to_additional_destinations() { if ($this->application->additional_networks->count() === 0) { @@ -1621,6 +1632,8 @@ private function set_coolify_variables() if (isset($this->application->git_branch)) { $this->coolify_variables .= "COOLIFY_BRANCH={$this->application->git_branch} "; } + $this->coolify_variables .= "COOLIFY_RESOURCE_UUID={$this->application->uuid} "; + $this->coolify_variables .= "COOLIFY_CONTAINER_NAME={$this->container_name} "; } private function check_git_if_build_needed() @@ -1688,6 +1701,12 @@ private function check_git_if_build_needed() $this->application_deployment_queue->save(); } $this->set_coolify_variables(); + + // Restart helper container with actual SOURCE_COMMIT value + if ($this->application->settings->use_build_secrets && $this->commit !== 'HEAD') { + $this->application_deployment_queue->addLogEntry('Restarting helper container with actual SOURCE_COMMIT value.'); + $this->restart_builder_container_with_actual_commit(); + } } private function clone_repository() @@ -1936,7 +1955,6 @@ private function generate_env_variables() $this->env_args = collect([]); $this->env_args->put('SOURCE_COMMIT', $this->commit); $coolify_envs = $this->generate_coolify_env_variables(); - // For build process, include only environment variables where is_buildtime = true if ($this->pull_request_id === 0) { // Get environment variables that are marked as available during build @@ -1991,6 +2009,12 @@ private function generate_env_variables() } } } + + // Merge COOLIFY_* variables into env_args for build process + // This ensures they're available for both build args and build secrets + $coolify_envs->each(function ($value, $key) { + $this->env_args->put($key, $value); + }); } private function generate_compose_file() @@ -2610,6 +2634,8 @@ private function build_image() } else { // Dockerfile buildpack if ($this->dockerBuildkitSupported) { + // Modify the Dockerfile to use build secrets + $this->modify_dockerfile_for_secrets("{$this->workdir}{$this->dockerfile_location}"); // Use BuildKit with secrets $secrets_flags = $this->build_secrets ? " {$this->build_secrets}" : ''; if ($this->force_rebuild) { @@ -2764,7 +2790,6 @@ private function generate_build_env_variables() $this->generate_env_variables(); $variables = collect([])->merge($this->env_args); } - // Analyze build variables for potential issues if ($variables->isNotEmpty()) { $this->analyzeBuildTimeVariables($variables); @@ -2809,9 +2834,13 @@ private function generate_docker_env_flags_for_secrets() return ''; } - $variables = $this->pull_request_id === 0 - ? $this->application->environment_variables()->where('key', 'not like', 'NIXPACKS_%')->where('is_buildtime', true)->get() - : $this->application->environment_variables_preview()->where('key', 'not like', 'NIXPACKS_%')->where('is_buildtime', true)->get(); + // Generate env variables if not already done + // This populates $this->env_args with both user-defined and COOLIFY_* variables + if (! $this->env_args || $this->env_args->isEmpty()) { + $this->generate_env_variables(); + } + + $variables = $this->env_args; if ($variables->isEmpty()) { return ''; @@ -2819,12 +2848,19 @@ private function generate_docker_env_flags_for_secrets() $secrets_hash = $this->generate_secrets_hash($variables); + // Get database env vars to check for multiline flag + $env_vars = $this->pull_request_id === 0 + ? $this->application->environment_variables()->where('is_buildtime', true)->get() + : $this->application->environment_variables_preview()->where('is_buildtime', true)->get(); + // Map to simple array format for the helper function - $vars_array = $variables->map(function ($env) { + $vars_array = $variables->map(function ($value, $key) use ($env_vars) { + $env = $env_vars->firstWhere('key', $key); + return [ - 'key' => $env->key, - 'value' => $env->real_value, - 'is_multiline' => $env->is_multiline, + 'key' => $key, + 'value' => $value, + 'is_multiline' => $env ? $env->is_multiline : false, ]; }); @@ -2890,7 +2926,6 @@ private function add_build_env_variables_to_dockerfile() 'save' => 'dockerfile', ]); $dockerfile = collect(str($this->saved_outputs->get('dockerfile'))->trim()->explode("\n")); - if ($this->pull_request_id === 0) { // Only add environment variables that are available during build $envs = $this->application->environment_variables() @@ -2975,16 +3010,19 @@ private function modify_dockerfile_for_secrets($dockerfile_path) $dockerfile->prepend('# syntax=docker/dockerfile:1'); } - // Get environment variables for secrets - $variables = $this->pull_request_id === 0 - ? $this->application->environment_variables()->where('key', 'not like', 'NIXPACKS_%')->where('is_buildtime', true)->get() - : $this->application->environment_variables_preview()->where('key', 'not like', 'NIXPACKS_%')->where('is_buildtime', true)->get(); + // Generate env variables if not already done + // This populates $this->env_args with both user-defined and COOLIFY_* variables + if (! $this->env_args || $this->env_args->isEmpty()) { + $this->generate_env_variables(); + } + + $variables = $this->env_args; if ($variables->isEmpty()) { return; } // Generate mount strings for all secrets - $mountStrings = $variables->map(fn ($env) => "--mount=type=secret,id={$env->key},env={$env->key}")->implode(' '); + $mountStrings = $variables->map(fn ($value, $key) => "--mount=type=secret,id={$key},env={$key}")->implode(' '); // Add mount for the secrets hash to ensure cache invalidation $mountStrings .= ' --mount=type=secret,id=COOLIFY_BUILD_SECRETS_HASH,env=COOLIFY_BUILD_SECRETS_HASH'; @@ -3012,8 +3050,6 @@ private function modify_dockerfile_for_secrets($dockerfile_path) executeInDocker($this->deployment_uuid, "echo '{$dockerfile_base64}' | base64 -d | tee {$dockerfile_path} > /dev/null"), 'hidden' => true, ]); - - $this->application_deployment_queue->addLogEntry('Modified Dockerfile to use build secrets.'); } } @@ -3023,15 +3059,13 @@ private function modify_dockerfiles_for_compose($composeFile) return; } - $variables = $this->pull_request_id === 0 - ? $this->application->environment_variables() - ->where('key', 'not like', 'NIXPACKS_%') - ->where('is_buildtime', true) - ->get() - : $this->application->environment_variables_preview() - ->where('key', 'not like', 'NIXPACKS_%') - ->where('is_buildtime', true) - ->get(); + // Generate env variables if not already done + // This populates $this->env_args with both user-defined and COOLIFY_* variables + if (! $this->env_args || $this->env_args->isEmpty()) { + $this->generate_env_variables(); + } + + $variables = $this->env_args; if ($variables->isEmpty()) { $this->application_deployment_queue->addLogEntry('No build-time variables to add to Dockerfiles.'); @@ -3105,8 +3139,8 @@ private function modify_dockerfiles_for_compose($composeFile) $isMultiStage = count($fromIndices) > 1; $argsToAdd = collect([]); - foreach ($variables as $env) { - $argsToAdd->push("ARG {$env->key}"); + foreach ($variables as $key => $value) { + $argsToAdd->push("ARG {$key}"); } ray($argsToAdd); @@ -3177,19 +3211,22 @@ private function modify_dockerfiles_for_compose($composeFile) private function add_build_secrets_to_compose($composeFile) { - // Get environment variables for secrets - $variables = $this->pull_request_id === 0 - ? $this->application->environment_variables()->where('key', 'not like', 'NIXPACKS_%')->get() - : $this->application->environment_variables_preview()->where('key', 'not like', 'NIXPACKS_%')->get(); + // Generate env variables if not already done + // This populates $this->env_args with both user-defined and COOLIFY_* variables + if (! $this->env_args || $this->env_args->isEmpty()) { + $this->generate_env_variables(); + } + + $variables = $this->env_args; if ($variables->isEmpty()) { return $composeFile; } $secrets = []; - foreach ($variables as $env) { - $secrets[$env->key] = [ - 'environment' => $env->key, + foreach ($variables as $key => $value) { + $secrets[$key] = [ + 'environment' => $key, ]; } @@ -3204,9 +3241,9 @@ private function add_build_secrets_to_compose($composeFile) if (! isset($service['build']['secrets'])) { $service['build']['secrets'] = []; } - foreach ($variables as $env) { - if (! in_array($env->key, $service['build']['secrets'])) { - $service['build']['secrets'][] = $env->key; + foreach ($variables as $key => $value) { + if (! in_array($key, $service['build']['secrets'])) { + $service['build']['secrets'][] = $key; } } } @@ -3325,7 +3362,6 @@ private function next(string $status) queue_next_deployment($this->application); if ($status === ApplicationDeploymentStatus::FINISHED->value) { - ray($this->application->team()->id); event(new ApplicationConfigurationChanged($this->application->team()->id)); if (! $this->only_this_server) {