From 274c37e33380e1003707d7b930ec9d6bf5b0a980 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Tue, 18 Nov 2025 13:07:03 +0100 Subject: [PATCH] fix: auto-inject environment variables into custom Docker Compose commands --- app/Jobs/ApplicationDeploymentJob.php | 113 +++++++++++++++----------- 1 file changed, 65 insertions(+), 48 deletions(-) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 44e489976..503366e5d 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -41,6 +41,12 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, EnvironmentVariableAnalyzer, ExecuteRemoteCommand, InteractsWithQueue, Queueable, SerializesModels; + private const BUILD_TIME_ENV_PATH = '/artifacts/build-time.env'; + + private const BUILD_SCRIPT_PATH = '/artifacts/build.sh'; + + private const NIXPACKS_PLAN_PATH = '/artifacts/thegameplan.json'; + public $tries = 1; public $timeout = 3600; @@ -652,17 +658,12 @@ private function deploy_docker_compose_buildpack() $this->save_buildtime_environment_variables(); if ($this->docker_compose_custom_build_command) { - $build_command = $this->docker_compose_custom_build_command; - - // Inject --env-file flag if not already present in custom command - // This ensures build-time environment variables are available during the build - if (! str_contains($build_command, '--env-file')) { - $build_command = str_replace( - 'docker compose', - 'docker compose --env-file /artifacts/build-time.env', - $build_command - ); - } + // Auto-inject -f (compose file) and --env-file flags using helper function + $build_command = injectDockerComposeFlags( + $this->docker_compose_custom_build_command, + "{$this->workdir}{$this->docker_compose_location}", + self::BUILD_TIME_ENV_PATH + ); // Prepend DOCKER_BUILDKIT=1 if BuildKit is supported if ($this->dockerBuildkitSupported) { @@ -688,7 +689,7 @@ private function deploy_docker_compose_buildpack() $command = "DOCKER_BUILDKIT=1 {$command}"; } // Use build-time .env file from /artifacts (outside Docker context to prevent it from being in the image) - $command .= ' --env-file /artifacts/build-time.env'; + $command .= ' --env-file '.self::BUILD_TIME_ENV_PATH; if ($this->force_rebuild) { $command .= " --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build --pull --no-cache"; } else { @@ -736,9 +737,16 @@ private function deploy_docker_compose_buildpack() $server_workdir = $this->application->workdir(); if ($this->application->settings->is_raw_compose_deployment_enabled) { if ($this->docker_compose_custom_start_command) { + // Auto-inject -f (compose file) and --env-file flags using helper function + $start_command = injectDockerComposeFlags( + $this->docker_compose_custom_start_command, + "{$server_workdir}{$this->docker_compose_location}", + "{$server_workdir}/.env" + ); + $this->write_deployment_configurations(); $this->execute_remote_command( - [executeInDocker($this->deployment_uuid, "cd {$this->workdir} && {$this->docker_compose_custom_start_command}"), 'hidden' => true], + [executeInDocker($this->deployment_uuid, "cd {$this->workdir} && {$start_command}"), 'hidden' => true], ); } else { $this->write_deployment_configurations(); @@ -754,9 +762,18 @@ private function deploy_docker_compose_buildpack() } } else { if ($this->docker_compose_custom_start_command) { + // Auto-inject -f (compose file) and --env-file flags using helper function + // Use $this->workdir for non-preserve-repository mode + $workdir_path = $this->preserveRepository ? $server_workdir : $this->workdir; + $start_command = injectDockerComposeFlags( + $this->docker_compose_custom_start_command, + "{$workdir_path}{$this->docker_compose_location}", + "{$workdir_path}/.env" + ); + $this->write_deployment_configurations(); $this->execute_remote_command( - [executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_start_command}"), 'hidden' => true], + [executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$start_command}"), 'hidden' => true], ); } else { $command = "{$this->coolify_variables} docker compose"; @@ -1555,10 +1572,10 @@ private function save_buildtime_environment_variables() $this->execute_remote_command( [ - executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d | tee /artifacts/build-time.env > /dev/null"), + executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d | tee ".self::BUILD_TIME_ENV_PATH.' > /dev/null'), ], [ - executeInDocker($this->deployment_uuid, 'cat /artifacts/build-time.env'), + executeInDocker($this->deployment_uuid, 'cat '.self::BUILD_TIME_ENV_PATH), 'hidden' => true, ], ); @@ -1569,7 +1586,7 @@ private function save_buildtime_environment_variables() $this->execute_remote_command( [ - executeInDocker($this->deployment_uuid, 'touch /artifacts/build-time.env'), + executeInDocker($this->deployment_uuid, 'touch '.self::BUILD_TIME_ENV_PATH), ] ); } @@ -2695,15 +2712,15 @@ private function build_static_image() executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d | tee {$this->workdir}/nginx.conf > /dev/null"), ], [ - executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), + executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee ".self::BUILD_SCRIPT_PATH.' > /dev/null'), 'hidden' => true, ], [ - executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'), + executeInDocker($this->deployment_uuid, 'cat '.self::BUILD_SCRIPT_PATH), 'hidden' => true, ], [ - executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), + executeInDocker($this->deployment_uuid, 'bash '.self::BUILD_SCRIPT_PATH), 'hidden' => true, ] ); @@ -2711,7 +2728,7 @@ private function build_static_image() } /** - * Wrap a docker build command with environment export from /artifacts/build-time.env + * Wrap a docker build command with environment export from build-time .env file * This enables shell interpolation of variables (e.g., APP_URL=$COOLIFY_URL) * * @param string $build_command The docker build command to wrap @@ -2719,7 +2736,7 @@ private function build_static_image() */ private function wrap_build_command_with_env_export(string $build_command): string { - return "cd {$this->workdir} && set -a && source /artifacts/build-time.env && set +a && {$build_command}"; + return "cd {$this->workdir} && set -a && source ".self::BUILD_TIME_ENV_PATH." && set +a && {$build_command}"; } private function build_image() @@ -2758,10 +2775,10 @@ private function build_image() } if ($this->application->build_pack === 'nixpacks') { $this->nixpacks_plan = base64_encode($this->nixpacks_plan); - $this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]); + $this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee ".self::NIXPACKS_PLAN_PATH.' > /dev/null'), 'hidden' => true]); if ($this->force_rebuild) { $this->execute_remote_command([ - executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"), + executeInDocker($this->deployment_uuid, 'nixpacks build -c '.self::NIXPACKS_PLAN_PATH." --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"), 'hidden' => true, ], [ executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"), @@ -2781,7 +2798,7 @@ private function build_image() } } else { $this->execute_remote_command([ - executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"), + executeInDocker($this->deployment_uuid, 'nixpacks build -c '.self::NIXPACKS_PLAN_PATH." --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"), 'hidden' => true, ], [ executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"), @@ -2805,19 +2822,19 @@ private function build_image() $base64_build_command = base64_encode($build_command); $this->execute_remote_command( [ - executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), + executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee ".self::BUILD_SCRIPT_PATH.' > /dev/null'), 'hidden' => true, ], [ - executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'), + executeInDocker($this->deployment_uuid, 'cat '.self::BUILD_SCRIPT_PATH), 'hidden' => true, ], [ - executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), + executeInDocker($this->deployment_uuid, 'bash '.self::BUILD_SCRIPT_PATH), 'hidden' => true, ] ); - $this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]); + $this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm '.self::NIXPACKS_PLAN_PATH), 'hidden' => true]); } else { // Dockerfile buildpack if ($this->dockerBuildkitSupported && $this->application->settings->use_build_secrets) { @@ -2849,15 +2866,15 @@ private function build_image() $base64_build_command = base64_encode($build_command); $this->execute_remote_command( [ - executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), + executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee ".self::BUILD_SCRIPT_PATH.' > /dev/null'), 'hidden' => true, ], [ - executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'), + executeInDocker($this->deployment_uuid, 'cat '.self::BUILD_SCRIPT_PATH), 'hidden' => true, ], [ - executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), + executeInDocker($this->deployment_uuid, 'bash '.self::BUILD_SCRIPT_PATH), 'hidden' => true, ] ); @@ -2888,15 +2905,15 @@ private function build_image() executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d | tee {$this->workdir}/nginx.conf > /dev/null"), ], [ - executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), + executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee ".self::BUILD_SCRIPT_PATH.' > /dev/null'), 'hidden' => true, ], [ - executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'), + executeInDocker($this->deployment_uuid, 'cat '.self::BUILD_SCRIPT_PATH), 'hidden' => true, ], [ - executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), + executeInDocker($this->deployment_uuid, 'bash '.self::BUILD_SCRIPT_PATH), 'hidden' => true, ] ); @@ -2923,25 +2940,25 @@ private function build_image() $base64_build_command = base64_encode($build_command); $this->execute_remote_command( [ - executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), + executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee ".self::BUILD_SCRIPT_PATH.' > /dev/null'), 'hidden' => true, ], [ - executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'), + executeInDocker($this->deployment_uuid, 'cat '.self::BUILD_SCRIPT_PATH), 'hidden' => true, ], [ - executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), + executeInDocker($this->deployment_uuid, 'bash '.self::BUILD_SCRIPT_PATH), 'hidden' => true, ] ); } else { if ($this->application->build_pack === 'nixpacks') { $this->nixpacks_plan = base64_encode($this->nixpacks_plan); - $this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]); + $this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee ".self::NIXPACKS_PLAN_PATH.' > /dev/null'), 'hidden' => true]); if ($this->force_rebuild) { $this->execute_remote_command([ - executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"), + executeInDocker($this->deployment_uuid, 'nixpacks build -c '.self::NIXPACKS_PLAN_PATH." --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"), 'hidden' => true, ], [ executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"), @@ -2962,7 +2979,7 @@ private function build_image() } } else { $this->execute_remote_command([ - executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"), + executeInDocker($this->deployment_uuid, 'nixpacks build -c '.self::NIXPACKS_PLAN_PATH." --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"), 'hidden' => true, ], [ executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"), @@ -2985,19 +3002,19 @@ private function build_image() $base64_build_command = base64_encode($build_command); $this->execute_remote_command( [ - executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), + executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee ".self::BUILD_SCRIPT_PATH.' > /dev/null'), 'hidden' => true, ], [ - executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'), + executeInDocker($this->deployment_uuid, 'cat '.self::BUILD_SCRIPT_PATH), 'hidden' => true, ], [ - executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), + executeInDocker($this->deployment_uuid, 'bash '.self::BUILD_SCRIPT_PATH), 'hidden' => true, ] ); - $this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]); + $this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm '.self::NIXPACKS_PLAN_PATH), 'hidden' => true]); } else { // Dockerfile buildpack if ($this->dockerBuildkitSupported && $this->application->settings->use_build_secrets) { @@ -3030,15 +3047,15 @@ private function build_image() $base64_build_command = base64_encode($build_command); $this->execute_remote_command( [ - executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), + executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee ".self::BUILD_SCRIPT_PATH.' > /dev/null'), 'hidden' => true, ], [ - executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'), + executeInDocker($this->deployment_uuid, 'cat '.self::BUILD_SCRIPT_PATH), 'hidden' => true, ], [ - executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), + executeInDocker($this->deployment_uuid, 'bash '.self::BUILD_SCRIPT_PATH), 'hidden' => true, ] );