diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php
index 5dced0599..44e489976 100644
--- a/app/Jobs/ApplicationDeploymentJob.php
+++ b/app/Jobs/ApplicationDeploymentJob.php
@@ -652,11 +652,32 @@ private function deploy_docker_compose_buildpack()
$this->save_buildtime_environment_variables();
if ($this->docker_compose_custom_build_command) {
- // Prepend DOCKER_BUILDKIT=1 if BuildKit is supported
$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
+ );
+ }
+
+ // Prepend DOCKER_BUILDKIT=1 if BuildKit is supported
if ($this->dockerBuildkitSupported) {
$build_command = "DOCKER_BUILDKIT=1 {$build_command}";
}
+
+ // Append build arguments if not using build secrets (matching default behavior)
+ if (! $this->application->settings->use_build_secrets && $this->build_args instanceof \Illuminate\Support\Collection && $this->build_args->isNotEmpty()) {
+ $build_args_string = $this->build_args->implode(' ');
+ // Escape single quotes for bash -c context used by executeInDocker
+ $build_args_string = str_replace("'", "'\\''", $build_args_string);
+ $build_command .= " {$build_args_string}";
+ $this->application_deployment_queue->addLogEntry('Adding build arguments to custom Docker Compose build command.');
+ }
+
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$build_command}"), 'hidden' => true],
);
diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php
index c95260efe..415a1d378 100644
--- a/resources/views/livewire/project/application/general.blade.php
+++ b/resources/views/livewire/project/application/general.blade.php
@@ -259,7 +259,7 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry"
toBe('docker compose --env-file /artifacts/build-time.env -f ./docker-compose.yaml build');
+ expect($customCommand)->toContain('--env-file /artifacts/build-time.env');
+});
+
+it('does not duplicate --env-file flag when already present', function () {
+ $customCommand = 'docker compose --env-file /custom/.env -f ./docker-compose.yaml build';
+
+ // Simulate the injection logic from ApplicationDeploymentJob
+ if (! str_contains($customCommand, '--env-file')) {
+ $customCommand = str_replace(
+ 'docker compose',
+ 'docker compose --env-file /artifacts/build-time.env',
+ $customCommand
+ );
+ }
+
+ expect($customCommand)->toBe('docker compose --env-file /custom/.env -f ./docker-compose.yaml build');
+ expect(substr_count($customCommand, '--env-file'))->toBe(1);
+});
+
+it('preserves custom build command structure with env-file injection', function () {
+ $customCommand = 'docker compose -f ./custom/path/docker-compose.prod.yaml build --no-cache';
+
+ // Simulate the injection logic from ApplicationDeploymentJob
+ if (! str_contains($customCommand, '--env-file')) {
+ $customCommand = str_replace(
+ 'docker compose',
+ 'docker compose --env-file /artifacts/build-time.env',
+ $customCommand
+ );
+ }
+
+ expect($customCommand)->toBe('docker compose --env-file /artifacts/build-time.env -f ./custom/path/docker-compose.prod.yaml build --no-cache');
+ expect($customCommand)->toContain('--env-file /artifacts/build-time.env');
+ expect($customCommand)->toContain('-f ./custom/path/docker-compose.prod.yaml');
+ expect($customCommand)->toContain('build --no-cache');
+});
+
+it('handles multiple docker compose commands in custom build command', function () {
+ // Edge case: Only the first 'docker compose' should get the env-file flag
+ $customCommand = 'docker compose -f ./docker-compose.yaml build';
+
+ // Simulate the injection logic from ApplicationDeploymentJob
+ if (! str_contains($customCommand, '--env-file')) {
+ $customCommand = str_replace(
+ 'docker compose',
+ 'docker compose --env-file /artifacts/build-time.env',
+ $customCommand
+ );
+ }
+
+ // Note: str_replace replaces ALL occurrences, which is acceptable in this case
+ // since you typically only have one 'docker compose' command
+ expect($customCommand)->toContain('docker compose --env-file /artifacts/build-time.env');
+});
+
+it('verifies build args would be appended correctly', function () {
+ $customCommand = 'docker compose --env-file /artifacts/build-time.env -f ./docker-compose.yaml build';
+ $buildArgs = collect([
+ '--build-arg NODE_ENV=production',
+ '--build-arg API_URL=https://api.example.com',
+ ]);
+
+ // Simulate build args appending logic
+ $buildArgsString = $buildArgs->implode(' ');
+ $buildArgsString = str_replace("'", "'\\''", $buildArgsString);
+ $customCommand .= " {$buildArgsString}";
+
+ expect($customCommand)->toContain('--build-arg NODE_ENV=production');
+ expect($customCommand)->toContain('--build-arg API_URL=https://api.example.com');
+ expect($customCommand)->toBe(
+ 'docker compose --env-file /artifacts/build-time.env -f ./docker-compose.yaml build --build-arg NODE_ENV=production --build-arg API_URL=https://api.example.com'
+ );
+});
+
+it('properly escapes single quotes in build args', function () {
+ $buildArg = "--build-arg MESSAGE='Hello World'";
+
+ // Simulate the escaping logic from ApplicationDeploymentJob
+ $escapedBuildArg = str_replace("'", "'\\''", $buildArg);
+
+ expect($escapedBuildArg)->toBe("--build-arg MESSAGE='\\''Hello World'\\''");
+});
+
+it('handles DOCKER_BUILDKIT prefix with env-file injection', function () {
+ $customCommand = 'docker compose -f ./docker-compose.yaml build';
+
+ // Simulate the injection logic from ApplicationDeploymentJob
+ if (! str_contains($customCommand, '--env-file')) {
+ $customCommand = str_replace(
+ 'docker compose',
+ 'docker compose --env-file /artifacts/build-time.env',
+ $customCommand
+ );
+ }
+
+ // Simulate BuildKit support
+ $dockerBuildkitSupported = true;
+ if ($dockerBuildkitSupported) {
+ $customCommand = "DOCKER_BUILDKIT=1 {$customCommand}";
+ }
+
+ expect($customCommand)->toBe('DOCKER_BUILDKIT=1 docker compose --env-file /artifacts/build-time.env -f ./docker-compose.yaml build');
+ expect($customCommand)->toStartWith('DOCKER_BUILDKIT=1');
+ expect($customCommand)->toContain('--env-file /artifacts/build-time.env');
+});