fix(previews): give each Compose preview its own project name

Closes #9014
This commit is contained in:
Andras Bacsai committed 2026-11-03 23:20:52 +01:00
1 parent 997396b926
commit d09480973e
2 files changed
+33 -12

No files matched your search

+20 -10
View File
@@ -1029,9 +1029,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
// Use build-time .env file from /artifacts (outside Docker context to prevent it from being in the image)
$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";
$command .= " --project-name {$this->composeProjectName()} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build --pull --no-cache";
} else {
$command .= " --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build --pull";
$command .= " --project-name {$this->composeProjectName()} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build --pull";
}
if (! $this->application->settings->use_build_secrets && $this->build_args instanceof Collection && $this->build_args->isNotEmpty()) {
@@ -1140,10 +1140,10 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->write_deployment_configurations();
$this->server = $this->mainServer;
$workdir = $this->application->workdir();
$command = serverDockerConfigPrefix($this->server)."{$this->coolify_variables} docker compose --env-file {$workdir}/.env --project-name {$this->application->uuid} --project-directory {$workdir} -f {$workdir}{$this->docker_compose_location} pull --ignore-buildable {$services}";
$command = serverDockerConfigPrefix($this->server)."{$this->coolify_variables} docker compose --env-file {$workdir}/.env --project-name {$this->composeProjectName()} --project-directory {$workdir} -f {$workdir}{$this->docker_compose_location} pull --ignore-buildable {$services}";
} else {
$workdir = $this->workdir;
$command = executeInDocker($this->deployment_uuid, $this->withoutHelperBuildSecretEnvironment("{$this->coolify_variables} docker compose --env-file {$workdir}/.env --project-name {$this->application->uuid} --project-directory {$workdir} -f {$workdir}{$this->docker_compose_location} pull --ignore-buildable {$services}"));
$command = executeInDocker($this->deployment_uuid, $this->withoutHelperBuildSecretEnvironment("{$this->coolify_variables} docker compose --env-file {$workdir}/.env --project-name {$this->composeProjectName()} --project-directory {$workdir} -f {$workdir}{$this->docker_compose_location} pull --ignore-buildable {$services}"));
}
$this->execute_remote_command([
@@ -1172,6 +1172,16 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
->values();
}
/**
* The Docker Compose project of this deployment. Each preview has its own project, so Docker
* Compose does not treat the containers of the application and of the other previews as orphans
* of the deployed preview, and networks that the Compose file declares are not shared between them.
*/
private function composeProjectName(): string
{
return addPreviewDeploymentSuffix($this->application->uuid, $this->pull_request_id);
}
/**
* Removes the containers that `docker compose up` would give a volume that the user deleted
* in Coolify (see composeContainersWithRemovedVolumes()).
@@ -1276,7 +1286,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
if ($this->preserveRepository) {
// Always use .env file
$command .= " --env-file {$server_workdir}/.env";
$command .= " --project-name {$this->application->uuid} --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d";
$command .= " --project-name {$this->composeProjectName()} --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d";
$this->write_deployment_configurations();
$this->execute_remote_command(
@@ -1285,7 +1295,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
} else {
// Always use .env file
$command .= " --env-file {$this->workdir}/.env";
$command .= " --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d";
$command .= " --project-name {$this->composeProjectName()} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d";
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, $this->withoutHelperBuildSecretEnvironment($command)), 'hidden' => false, 'type' => 'stdout', 'command_hidden' => true],
);
@@ -5485,17 +5495,17 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
if ($this->application->build_pack === 'dockerimage') {
$this->application_deployment_queue->addLogEntry('Pulling latest images from the registry.');
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, "docker compose --project-name {$this->application->uuid} --project-directory {$this->workdir} pull"), 'hidden' => true],
[executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-name {$this->application->uuid} --project-directory {$this->workdir} up --build -d"), 'hidden' => true],
[executeInDocker($this->deployment_uuid, "docker compose --project-name {$this->composeProjectName()} --project-directory {$this->workdir} pull"), 'hidden' => true],
[executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-name {$this->composeProjectName()} --project-directory {$this->workdir} up --build -d"), 'hidden' => true],
);
} else {
if ($this->use_build_server) {
$this->execute_remote_command(
[serverDockerConfigPrefix($this->server)."{$this->coolify_variables} docker compose --project-name {$this->application->uuid} --project-directory {$this->configuration_dir} -f {$this->configuration_dir}{$this->docker_compose_location} up --pull always --build -d", 'hidden' => true],
[serverDockerConfigPrefix($this->server)."{$this->coolify_variables} docker compose --project-name {$this->composeProjectName()} --project-directory {$this->configuration_dir} -f {$this->configuration_dir}{$this->docker_compose_location} up --pull always --build -d", 'hidden' => true],
);
} else {
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up --build -d"), 'hidden' => true],
[executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-name {$this->composeProjectName()} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up --build -d"), 'hidden' => true],
);
}
}
+13 -2
View File
@@ -28,7 +28,7 @@ class TestableDockerComposeImagePullDeploymentJob extends ApplicationDeploymentJ
}
}
function makeDockerComposeImagePullJob(bool $useBuildServer = false, ?string $customBuildCommand = null, ?string $compose = null): array
function makeDockerComposeImagePullJob(bool $useBuildServer = false, ?string $customBuildCommand = null, ?string $compose = null, int $pullRequestId = 0): array
{
$compose ??= <<<'YAML'
services:
@@ -70,7 +70,7 @@ function makeDockerComposeImagePullJob(bool $useBuildServer = false, ?string $cu
'use_build_server' => $useBuildServer,
'workdir' => '/artifacts/application',
'configuration_dir' => '/runtime/application',
'pull_request_id' => 0,
'pull_request_id' => $pullRequestId,
'deployment_uuid' => 'deployment-uuid',
'docker_compose_location' => '/docker-compose.yaml',
'coolify_variables' => '',
@@ -142,3 +142,14 @@ it('pulls on the runtime server when a custom build command runs on a build serv
expect($job->commandServers)->each->toBe($mainServer)
->and(last($job->recordedCommands)[0][0])->toEndWith("pull --ignore-buildable 'db' 'cache'");
});
it('uses its own compose project for each preview deployment', function (int $pullRequestId, string $projectName) {
[$job, $reflection] = makeDockerComposeImagePullJob(pullRequestId: $pullRequestId);
$reflection->getMethod('pull_docker_compose_images')->invoke($job);
expect($job->recordedCommands[0][0][0])->toContain("--project-name {$projectName} --project-directory");
})->with([
'application' => [0, 'application-uuid'],
'preview' => [42, 'application-uuid-pr-42'],
]);