Fix: Docker build args injection regex to support service names
The regex pattern in injectDockerComposeBuildArgs() was too restrictive and failed to match `docker compose build servicename` commands. Changed the lookahead from `(?=\s+(?:--|-)|\s+(?:&&|\|\||;|\|)|$)` to the simpler `(?=\s|$)` to allow any content after the build command, including service names with hyphens/underscores and flags. Also improved the ApplicationDeploymentJob to use the new helper function and added comprehensive test coverage for service-specific builds. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
a56fde7f12
commit
d59c75c2b2
3 changed files with 124 additions and 3 deletions
|
|
@ -670,13 +670,20 @@ private function deploy_docker_compose_buildpack()
|
||||||
$build_command = "DOCKER_BUILDKIT=1 {$build_command}";
|
$build_command = "DOCKER_BUILDKIT=1 {$build_command}";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append build arguments if not using build secrets (matching default behavior)
|
// Inject build arguments after build subcommand if not using build secrets
|
||||||
if (! $this->application->settings->use_build_secrets && $this->build_args instanceof \Illuminate\Support\Collection && $this->build_args->isNotEmpty()) {
|
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(' ');
|
$build_args_string = $this->build_args->implode(' ');
|
||||||
// Escape single quotes for bash -c context used by executeInDocker
|
// Escape single quotes for bash -c context used by executeInDocker
|
||||||
$build_args_string = str_replace("'", "'\\''", $build_args_string);
|
$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.');
|
// Inject build args right after 'build' subcommand (not at the end)
|
||||||
|
$original_command = $build_command;
|
||||||
|
$build_command = injectDockerComposeBuildArgs($build_command, $build_args_string);
|
||||||
|
|
||||||
|
// Only log if build args were actually injected (command was modified)
|
||||||
|
if ($build_command !== $original_command) {
|
||||||
|
$this->application_deployment_queue->addLogEntry('Adding build arguments to custom Docker Compose build command.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
|
|
|
||||||
|
|
@ -1376,3 +1376,62 @@ function injectDockerComposeFlags(string $command, string $composeFilePath, stri
|
||||||
// Replace only first occurrence to avoid modifying comments/strings/chained commands
|
// Replace only first occurrence to avoid modifying comments/strings/chained commands
|
||||||
return preg_replace('/docker\s+compose/', $dockerComposeReplacement, $command, 1);
|
return preg_replace('/docker\s+compose/', $dockerComposeReplacement, $command, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject build arguments right after build-related subcommands in docker/docker compose commands.
|
||||||
|
* This ensures build args are only applied to build operations, not to push, pull, up, etc.
|
||||||
|
*
|
||||||
|
* Supports:
|
||||||
|
* - docker compose build
|
||||||
|
* - docker buildx build
|
||||||
|
* - docker builder build
|
||||||
|
* - docker build (legacy)
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
* - Input: "docker compose -f file.yml build"
|
||||||
|
* Output: "docker compose -f file.yml build --build-arg X --build-arg Y"
|
||||||
|
*
|
||||||
|
* - Input: "docker buildx build --platform linux/amd64"
|
||||||
|
* Output: "docker buildx build --build-arg X --build-arg Y --platform linux/amd64"
|
||||||
|
*
|
||||||
|
* - Input: "docker builder build --tag myimage:latest"
|
||||||
|
* Output: "docker builder build --build-arg X --build-arg Y --tag myimage:latest"
|
||||||
|
*
|
||||||
|
* - Input: "docker compose build && docker compose push"
|
||||||
|
* Output: "docker compose build --build-arg X --build-arg Y && docker compose push"
|
||||||
|
*
|
||||||
|
* - Input: "docker compose push"
|
||||||
|
* Output: "docker compose push" (unchanged - no build command found)
|
||||||
|
*
|
||||||
|
* @param string $command The docker command
|
||||||
|
* @param string $buildArgsString The build arguments to inject (e.g., "--build-arg X --build-arg Y")
|
||||||
|
* @return string The modified command with build args injected after build subcommand
|
||||||
|
*/
|
||||||
|
function injectDockerComposeBuildArgs(string $command, string $buildArgsString): string
|
||||||
|
{
|
||||||
|
// Early return if no build args to inject
|
||||||
|
if (empty(trim($buildArgsString))) {
|
||||||
|
return $command;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match build-related commands:
|
||||||
|
// - ' builder build' (docker builder build)
|
||||||
|
// - ' buildx build' (docker buildx build)
|
||||||
|
// - ' build' (docker compose build, docker build)
|
||||||
|
// Followed by either:
|
||||||
|
// - whitespace (allowing service names, flags, or any valid arguments)
|
||||||
|
// - end of string ($)
|
||||||
|
// This regex ensures we match build subcommands, not "build" in other contexts
|
||||||
|
// IMPORTANT: Order matters - check longer patterns first (builder build, buildx build) before ' build'
|
||||||
|
$pattern = '/( builder build| buildx build| build)(?=\s|$)/';
|
||||||
|
|
||||||
|
// Replace the first occurrence of build command with build command + build-args
|
||||||
|
$modifiedCommand = preg_replace(
|
||||||
|
$pattern,
|
||||||
|
'$1 '.$buildArgsString,
|
||||||
|
$command,
|
||||||
|
1 // Only replace first occurrence
|
||||||
|
);
|
||||||
|
|
||||||
|
return $modifiedCommand ?? $command;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -615,3 +615,58 @@
|
||||||
expect($path)->not->toContain('//', "Double slash found for baseDir: {$case['baseDir']}");
|
expect($path)->not->toContain('//', "Double slash found for baseDir: {$case['baseDir']}");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Tests for injectDockerComposeBuildArgs() helper function
|
||||||
|
it('injects build args when building specific service', function () {
|
||||||
|
$command = 'docker compose build web';
|
||||||
|
$buildArgs = '--build-arg ENV=prod';
|
||||||
|
|
||||||
|
$result = injectDockerComposeBuildArgs($command, $buildArgs);
|
||||||
|
|
||||||
|
expect($result)->toBe('docker compose build --build-arg ENV=prod web');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('injects build args with service name containing hyphens', function () {
|
||||||
|
$command = 'docker compose build my-service-name';
|
||||||
|
$buildArgs = '--build-arg TEST=value';
|
||||||
|
|
||||||
|
$result = injectDockerComposeBuildArgs($command, $buildArgs);
|
||||||
|
|
||||||
|
expect($result)->toBe('docker compose build --build-arg TEST=value my-service-name');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('injects build args with service name containing underscores', function () {
|
||||||
|
$command = 'docker compose build my_service_name';
|
||||||
|
$buildArgs = '--build-arg TEST=value';
|
||||||
|
|
||||||
|
$result = injectDockerComposeBuildArgs($command, $buildArgs);
|
||||||
|
|
||||||
|
expect($result)->toBe('docker compose build --build-arg TEST=value my_service_name');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('injects build args before service name and existing flags', function () {
|
||||||
|
$command = 'docker compose build backend --no-cache';
|
||||||
|
$buildArgs = '--build-arg FOO=bar';
|
||||||
|
|
||||||
|
$result = injectDockerComposeBuildArgs($command, $buildArgs);
|
||||||
|
|
||||||
|
expect($result)->toBe('docker compose build --build-arg FOO=bar backend --no-cache');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles buildx with target and flags', function () {
|
||||||
|
$command = 'docker buildx build --platform linux/amd64 -t myimage:latest .';
|
||||||
|
$buildArgs = '--build-arg VERSION=1.0';
|
||||||
|
|
||||||
|
$result = injectDockerComposeBuildArgs($command, $buildArgs);
|
||||||
|
|
||||||
|
expect($result)->toBe('docker buildx build --build-arg VERSION=1.0 --platform linux/amd64 -t myimage:latest .');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles docker compose build with no arguments', function () {
|
||||||
|
$command = 'docker compose build';
|
||||||
|
$buildArgs = '--build-arg FOO=bar';
|
||||||
|
|
||||||
|
$result = injectDockerComposeBuildArgs($command, $buildArgs);
|
||||||
|
|
||||||
|
expect($result)->toBe('docker compose build --build-arg FOO=bar');
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue