fix: auto-inject environment variables into custom Docker Compose commands

This commit is contained in:
Andras Bacsai 2025-11-18 13:07:03 +01:00
parent 1094ab7a46
commit 37c3cd9f4e

View file

@ -41,6 +41,12 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
{ {
use Dispatchable, EnvironmentVariableAnalyzer, ExecuteRemoteCommand, InteractsWithQueue, Queueable, SerializesModels; 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 $tries = 1;
public $timeout = 3600; public $timeout = 3600;
@ -652,17 +658,12 @@ private function deploy_docker_compose_buildpack()
$this->save_buildtime_environment_variables(); $this->save_buildtime_environment_variables();
if ($this->docker_compose_custom_build_command) { if ($this->docker_compose_custom_build_command) {
$build_command = $this->docker_compose_custom_build_command; // Auto-inject -f (compose file) and --env-file flags using helper function
$build_command = injectDockerComposeFlags(
// Inject --env-file flag if not already present in custom command $this->docker_compose_custom_build_command,
// This ensures build-time environment variables are available during the build "{$this->workdir}{$this->docker_compose_location}",
if (! str_contains($build_command, '--env-file')) { self::BUILD_TIME_ENV_PATH
$build_command = str_replace( );
'docker compose',
'docker compose --env-file /artifacts/build-time.env',
$build_command
);
}
// Prepend DOCKER_BUILDKIT=1 if BuildKit is supported // Prepend DOCKER_BUILDKIT=1 if BuildKit is supported
if ($this->dockerBuildkitSupported) { if ($this->dockerBuildkitSupported) {
@ -688,7 +689,7 @@ private function deploy_docker_compose_buildpack()
$command = "DOCKER_BUILDKIT=1 {$command}"; $command = "DOCKER_BUILDKIT=1 {$command}";
} }
// Use build-time .env file from /artifacts (outside Docker context to prevent it from being in the image) // 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) { 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"; $command .= " --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build --pull --no-cache";
} else { } else {
@ -736,9 +737,16 @@ private function deploy_docker_compose_buildpack()
$server_workdir = $this->application->workdir(); $server_workdir = $this->application->workdir();
if ($this->application->settings->is_raw_compose_deployment_enabled) { if ($this->application->settings->is_raw_compose_deployment_enabled) {
if ($this->docker_compose_custom_start_command) { 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->write_deployment_configurations();
$this->execute_remote_command( $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 { } else {
$this->write_deployment_configurations(); $this->write_deployment_configurations();
@ -754,9 +762,18 @@ private function deploy_docker_compose_buildpack()
} }
} else { } else {
if ($this->docker_compose_custom_start_command) { 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->write_deployment_configurations();
$this->execute_remote_command( $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 { } else {
$command = "{$this->coolify_variables} docker compose"; $command = "{$this->coolify_variables} docker compose";
@ -1555,10 +1572,10 @@ private function save_buildtime_environment_variables()
$this->execute_remote_command( $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, 'hidden' => true,
], ],
); );
@ -1569,7 +1586,7 @@ private function save_buildtime_environment_variables()
$this->execute_remote_command( $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 '{$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, 'hidden' => true,
], ],
[ [
executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'), executeInDocker($this->deployment_uuid, 'cat '.self::BUILD_SCRIPT_PATH),
'hidden' => true, 'hidden' => true,
], ],
[ [
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), executeInDocker($this->deployment_uuid, 'bash '.self::BUILD_SCRIPT_PATH),
'hidden' => true, '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) * This enables shell interpolation of variables (e.g., APP_URL=$COOLIFY_URL)
* *
* @param string $build_command The docker build command to wrap * @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 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() private function build_image()
@ -2758,10 +2775,10 @@ private function build_image()
} }
if ($this->application->build_pack === 'nixpacks') { if ($this->application->build_pack === 'nixpacks') {
$this->nixpacks_plan = base64_encode($this->nixpacks_plan); $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) { if ($this->force_rebuild) {
$this->execute_remote_command([ $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, 'hidden' => true,
], [ ], [
executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"), executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"),
@ -2781,7 +2798,7 @@ private function build_image()
} }
} else { } else {
$this->execute_remote_command([ $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, 'hidden' => true,
], [ ], [
executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"), executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"),
@ -2805,19 +2822,19 @@ private function build_image()
$base64_build_command = base64_encode($build_command); $base64_build_command = base64_encode($build_command);
$this->execute_remote_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, 'hidden' => true,
], ],
[ [
executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'), executeInDocker($this->deployment_uuid, 'cat '.self::BUILD_SCRIPT_PATH),
'hidden' => true, 'hidden' => true,
], ],
[ [
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), executeInDocker($this->deployment_uuid, 'bash '.self::BUILD_SCRIPT_PATH),
'hidden' => true, '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 { } else {
// Dockerfile buildpack // Dockerfile buildpack
if ($this->dockerBuildkitSupported && $this->application->settings->use_build_secrets) { if ($this->dockerBuildkitSupported && $this->application->settings->use_build_secrets) {
@ -2849,15 +2866,15 @@ private function build_image()
$base64_build_command = base64_encode($build_command); $base64_build_command = base64_encode($build_command);
$this->execute_remote_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, 'hidden' => true,
], ],
[ [
executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'), executeInDocker($this->deployment_uuid, 'cat '.self::BUILD_SCRIPT_PATH),
'hidden' => true, 'hidden' => true,
], ],
[ [
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), executeInDocker($this->deployment_uuid, 'bash '.self::BUILD_SCRIPT_PATH),
'hidden' => true, '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 '{$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, 'hidden' => true,
], ],
[ [
executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'), executeInDocker($this->deployment_uuid, 'cat '.self::BUILD_SCRIPT_PATH),
'hidden' => true, 'hidden' => true,
], ],
[ [
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), executeInDocker($this->deployment_uuid, 'bash '.self::BUILD_SCRIPT_PATH),
'hidden' => true, 'hidden' => true,
] ]
); );
@ -2923,25 +2940,25 @@ private function build_image()
$base64_build_command = base64_encode($build_command); $base64_build_command = base64_encode($build_command);
$this->execute_remote_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, 'hidden' => true,
], ],
[ [
executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'), executeInDocker($this->deployment_uuid, 'cat '.self::BUILD_SCRIPT_PATH),
'hidden' => true, 'hidden' => true,
], ],
[ [
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), executeInDocker($this->deployment_uuid, 'bash '.self::BUILD_SCRIPT_PATH),
'hidden' => true, 'hidden' => true,
] ]
); );
} else { } else {
if ($this->application->build_pack === 'nixpacks') { if ($this->application->build_pack === 'nixpacks') {
$this->nixpacks_plan = base64_encode($this->nixpacks_plan); $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) { if ($this->force_rebuild) {
$this->execute_remote_command([ $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, 'hidden' => true,
], [ ], [
executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"), executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"),
@ -2962,7 +2979,7 @@ private function build_image()
} }
} else { } else {
$this->execute_remote_command([ $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, 'hidden' => true,
], [ ], [
executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"), executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"),
@ -2985,19 +3002,19 @@ private function build_image()
$base64_build_command = base64_encode($build_command); $base64_build_command = base64_encode($build_command);
$this->execute_remote_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, 'hidden' => true,
], ],
[ [
executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'), executeInDocker($this->deployment_uuid, 'cat '.self::BUILD_SCRIPT_PATH),
'hidden' => true, 'hidden' => true,
], ],
[ [
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), executeInDocker($this->deployment_uuid, 'bash '.self::BUILD_SCRIPT_PATH),
'hidden' => true, '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 { } else {
// Dockerfile buildpack // Dockerfile buildpack
if ($this->dockerBuildkitSupported && $this->application->settings->use_build_secrets) { if ($this->dockerBuildkitSupported && $this->application->settings->use_build_secrets) {
@ -3030,15 +3047,15 @@ private function build_image()
$base64_build_command = base64_encode($build_command); $base64_build_command = base64_encode($build_command);
$this->execute_remote_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, 'hidden' => true,
], ],
[ [
executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'), executeInDocker($this->deployment_uuid, 'cat '.self::BUILD_SCRIPT_PATH),
'hidden' => true, 'hidden' => true,
], ],
[ [
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), executeInDocker($this->deployment_uuid, 'bash '.self::BUILD_SCRIPT_PATH),
'hidden' => true, 'hidden' => true,
] ]
); );