Merge pull request #6616 from coollabsio/next

v4.0.0-beta.428
This commit is contained in:
Andras Bacsai 2025-09-15 18:52:24 +02:00 committed by GitHub
commit 7a9bc8dcb9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 298 additions and 321 deletions

View file

@ -99,12 +99,8 @@ public function handle(StandaloneClickhouse $database)
$docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); $docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
$docker_compose = Yaml::dump($docker_compose, 10); $docker_compose = Yaml::dump($docker_compose, 10);
$this->commands[] = [ $docker_compose_base64 = base64_encode($docker_compose);
'transfer_file' => [ $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
'content' => $docker_compose,
'destination' => "$this->configuration_dir/docker-compose.yml",
],
];
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$database->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";

View file

@ -52,9 +52,8 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St
} }
$configuration_dir = database_proxy_dir($database->uuid); $configuration_dir = database_proxy_dir($database->uuid);
$volume_configuration_dir = $configuration_dir;
if (isDev()) { if (isDev()) {
$volume_configuration_dir = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/databases/'.$database->uuid.'/proxy'; $configuration_dir = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/databases/'.$database->uuid.'/proxy';
} }
$nginxconf = <<<EOF $nginxconf = <<<EOF
user nginx; user nginx;
@ -87,7 +86,7 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St
'volumes' => [ 'volumes' => [
[ [
'type' => 'bind', 'type' => 'bind',
'source' => "$volume_configuration_dir/nginx.conf", 'source' => "$configuration_dir/nginx.conf",
'target' => '/etc/nginx/nginx.conf', 'target' => '/etc/nginx/nginx.conf',
], ],
], ],
@ -116,18 +115,8 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St
instant_remote_process(["docker rm -f $proxyContainerName"], $server, false); instant_remote_process(["docker rm -f $proxyContainerName"], $server, false);
instant_remote_process([ instant_remote_process([
"mkdir -p $configuration_dir", "mkdir -p $configuration_dir",
[ "echo '{$nginxconf_base64}' | base64 -d | tee $configuration_dir/nginx.conf > /dev/null",
'transfer_file' => [ "echo '{$dockercompose_base64}' | base64 -d | tee $configuration_dir/docker-compose.yaml > /dev/null",
'content' => base64_decode($nginxconf_base64),
'destination' => "$configuration_dir/nginx.conf",
],
],
[
'transfer_file' => [
'content' => base64_decode($dockercompose_base64),
'destination' => "$configuration_dir/docker-compose.yaml",
],
],
"docker compose --project-directory {$configuration_dir} pull", "docker compose --project-directory {$configuration_dir} pull",
"docker compose --project-directory {$configuration_dir} up -d", "docker compose --project-directory {$configuration_dir} up -d",
], $server); ], $server);

View file

@ -183,12 +183,8 @@ public function handle(StandaloneDragonfly $database)
$docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); $docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
$docker_compose = Yaml::dump($docker_compose, 10); $docker_compose = Yaml::dump($docker_compose, 10);
$this->commands[] = [ $docker_compose_base64 = base64_encode($docker_compose);
'transfer_file' => [ $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
'content' => $docker_compose,
'destination' => "$this->configuration_dir/docker-compose.yml",
],
];
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$database->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";

View file

@ -199,12 +199,8 @@ public function handle(StandaloneKeydb $database)
$docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options); $docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options);
$docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); $docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
$docker_compose = Yaml::dump($docker_compose, 10); $docker_compose = Yaml::dump($docker_compose, 10);
$this->commands[] = [ $docker_compose_base64 = base64_encode($docker_compose);
'transfer_file' => [ $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
'content' => $docker_compose,
'destination' => "$this->configuration_dir/docker-compose.yml",
],
];
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$database->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";

View file

@ -203,12 +203,8 @@ public function handle(StandaloneMariadb $database)
} }
$docker_compose = Yaml::dump($docker_compose, 10); $docker_compose = Yaml::dump($docker_compose, 10);
$this->commands[] = [ $docker_compose_base64 = base64_encode($docker_compose);
'transfer_file' => [ $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
'content' => $docker_compose,
'destination' => "$this->configuration_dir/docker-compose.yml",
],
];
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$database->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";
@ -288,11 +284,7 @@ private function add_custom_mysql()
} }
$filename = 'custom-config.cnf'; $filename = 'custom-config.cnf';
$content = $this->database->mariadb_conf; $content = $this->database->mariadb_conf;
$this->commands[] = [ $content_base64 = base64_encode($content);
'transfer_file' => [ $this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
'content' => $content,
'destination' => "$this->configuration_dir/{$filename}",
],
];
} }
} }

View file

@ -28,6 +28,9 @@ public function handle(StandaloneMongodb $database)
$container_name = $this->database->uuid; $container_name = $this->database->uuid;
$this->configuration_dir = database_configuration_dir().'/'.$container_name; $this->configuration_dir = database_configuration_dir().'/'.$container_name;
if (isDev()) {
$this->configuration_dir = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/databases/'.$container_name;
}
$this->commands = [ $this->commands = [
"echo 'Starting database.'", "echo 'Starting database.'",
@ -251,12 +254,8 @@ public function handle(StandaloneMongodb $database)
} }
$docker_compose = Yaml::dump($docker_compose, 10); $docker_compose = Yaml::dump($docker_compose, 10);
$this->commands[] = [ $docker_compose_base64 = base64_encode($docker_compose);
'transfer_file' => [ $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
'content' => $docker_compose,
'destination' => "$this->configuration_dir/docker-compose.yml",
],
];
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$database->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";
@ -333,22 +332,15 @@ private function add_custom_mongo_conf()
} }
$filename = 'mongod.conf'; $filename = 'mongod.conf';
$content = $this->database->mongo_conf; $content = $this->database->mongo_conf;
$this->commands[] = [ $content_base64 = base64_encode($content);
'transfer_file' => [ $this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
'content' => $content,
'destination' => "$this->configuration_dir/{$filename}",
],
];
} }
private function add_default_database() private function add_default_database()
{ {
$content = "db = db.getSiblingDB(\"{$this->database->mongo_initdb_database}\");db.createCollection('init_collection');db.createUser({user: \"{$this->database->mongo_initdb_root_username}\", pwd: \"{$this->database->mongo_initdb_root_password}\",roles: [{role:\"readWrite\",db:\"{$this->database->mongo_initdb_database}\"}]});"; $content = "db = db.getSiblingDB(\"{$this->database->mongo_initdb_database}\");db.createCollection('init_collection');db.createUser({user: \"{$this->database->mongo_initdb_root_username}\", pwd: \"{$this->database->mongo_initdb_root_password}\",roles: [{role:\"readWrite\",db:\"{$this->database->mongo_initdb_database}\"}]});";
$this->commands[] = [ $content_base64 = base64_encode($content);
'transfer_file' => [ $this->commands[] = "mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d";
'content' => $content, $this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/docker-entrypoint-initdb.d/01-default-database.js > /dev/null";
'destination' => "$this->configuration_dir/docker-entrypoint-initdb.d/01-default-database.js",
],
];
} }
} }

View file

@ -204,12 +204,8 @@ public function handle(StandaloneMysql $database)
} }
$docker_compose = Yaml::dump($docker_compose, 10); $docker_compose = Yaml::dump($docker_compose, 10);
$this->commands[] = [ $docker_compose_base64 = base64_encode($docker_compose);
'transfer_file' => [ $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
'content' => $docker_compose,
'destination' => "$this->configuration_dir/docker-compose.yml",
],
];
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$database->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";
@ -291,11 +287,7 @@ private function add_custom_mysql()
} }
$filename = 'custom-config.cnf'; $filename = 'custom-config.cnf';
$content = $this->database->mysql_conf; $content = $this->database->mysql_conf;
$this->commands[] = [ $content_base64 = base64_encode($content);
'transfer_file' => [ $this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
'content' => $content,
'destination' => "$this->configuration_dir/{$filename}",
],
];
} }
} }

View file

@ -27,6 +27,9 @@ public function handle(StandalonePostgresql $database)
$this->database = $database; $this->database = $database;
$container_name = $this->database->uuid; $container_name = $this->database->uuid;
$this->configuration_dir = database_configuration_dir().'/'.$container_name; $this->configuration_dir = database_configuration_dir().'/'.$container_name;
if (isDev()) {
$this->configuration_dir = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/databases/'.$container_name;
}
$this->commands = [ $this->commands = [
"echo 'Starting database.'", "echo 'Starting database.'",
@ -214,12 +217,8 @@ public function handle(StandalonePostgresql $database)
} }
$docker_compose = Yaml::dump($docker_compose, 10); $docker_compose = Yaml::dump($docker_compose, 10);
$this->commands[] = [ $docker_compose_base64 = base64_encode($docker_compose);
'transfer_file' => [ $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
'content' => $docker_compose,
'destination' => "$this->configuration_dir/docker-compose.yml",
],
];
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$database->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";
@ -305,12 +304,8 @@ private function generate_init_scripts()
foreach ($this->database->init_scripts as $init_script) { foreach ($this->database->init_scripts as $init_script) {
$filename = data_get($init_script, 'filename'); $filename = data_get($init_script, 'filename');
$content = data_get($init_script, 'content'); $content = data_get($init_script, 'content');
$this->commands[] = [ $content_base64 = base64_encode($content);
'transfer_file' => [ $this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/docker-entrypoint-initdb.d/{$filename} > /dev/null";
'content' => $content,
'destination' => "$this->configuration_dir/docker-entrypoint-initdb.d/{$filename}",
],
];
$this->init_scripts[] = "$this->configuration_dir/docker-entrypoint-initdb.d/{$filename}"; $this->init_scripts[] = "$this->configuration_dir/docker-entrypoint-initdb.d/{$filename}";
} }
} }
@ -332,11 +327,7 @@ private function add_custom_conf()
$this->database->postgres_conf = $content; $this->database->postgres_conf = $content;
$this->database->save(); $this->database->save();
} }
$this->commands[] = [ $content_base64 = base64_encode($content);
'transfer_file' => [ $this->commands[] = "echo '{$content_base64}' | base64 -d | tee $config_file_path > /dev/null";
'content' => $content,
'destination' => $config_file_path,
],
];
} }
} }

View file

@ -196,12 +196,8 @@ public function handle(StandaloneRedis $database)
$docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); $docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
$docker_compose = Yaml::dump($docker_compose, 10); $docker_compose = Yaml::dump($docker_compose, 10);
$this->commands[] = [ $docker_compose_base64 = base64_encode($docker_compose);
'transfer_file' => [ $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
'content' => $docker_compose,
'destination' => "$this->configuration_dir/docker-compose.yml",
],
];
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$database->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";

View file

@ -21,12 +21,7 @@ public function handle(Server $server, string $configuration): void
// Transfer the configuration file to the server // Transfer the configuration file to the server
instant_remote_process([ instant_remote_process([
"mkdir -p $proxy_path", "mkdir -p $proxy_path",
[ "echo '$docker_compose_yml_base64' | base64 -d | tee $proxy_path/docker-compose.yml > /dev/null",
'transfer_file' => [
'content' => base64_decode($docker_compose_yml_base64),
'destination' => "$proxy_path/docker-compose.yml",
],
],
], $server); ], $server);
} }
} }

View file

@ -40,12 +40,7 @@ public function handle(Server $server, string $cloudflare_token, string $ssh_dom
$commands = collect([ $commands = collect([
'mkdir -p /tmp/cloudflared', 'mkdir -p /tmp/cloudflared',
'cd /tmp/cloudflared', 'cd /tmp/cloudflared',
[ "echo '$docker_compose_yml_base64' | base64 -d | tee docker-compose.yml > /dev/null",
'transfer_file' => [
'content' => base64_decode($docker_compose_yml_base64),
'destination' => '/tmp/cloudflared/docker-compose.yml',
],
],
'echo Pulling latest Cloudflare Tunnel image.', 'echo Pulling latest Cloudflare Tunnel image.',
'docker compose pull', 'docker compose pull',
'echo Stopping existing Cloudflare Tunnel container.', 'echo Stopping existing Cloudflare Tunnel container.',

View file

@ -14,7 +14,6 @@ class InstallDocker
public function handle(Server $server) public function handle(Server $server)
{ {
ray('install docker');
$dockerVersion = config('constants.docker.minimum_required_version'); $dockerVersion = config('constants.docker.minimum_required_version');
$supported_os_type = $server->validateOS(); $supported_os_type = $server->validateOS();
if (! $supported_os_type) { if (! $supported_os_type) {
@ -104,15 +103,8 @@ public function handle(Server $server)
"curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh || curl https://get.docker.com | sh -s -- --version {$dockerVersion}", "curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh || curl https://get.docker.com | sh -s -- --version {$dockerVersion}",
"echo 'Configuring Docker Engine (merging existing configuration with the required)...'", "echo 'Configuring Docker Engine (merging existing configuration with the required)...'",
'test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json "/etc/docker/daemon.json.original-$(date +"%Y%m%d-%H%M%S")"', 'test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json "/etc/docker/daemon.json.original-$(date +"%Y%m%d-%H%M%S")"',
[ "test ! -s /etc/docker/daemon.json && echo '{$config}' | base64 -d | tee /etc/docker/daemon.json > /dev/null",
'transfer_file' => [ "echo '{$config}' | base64 -d | tee /etc/docker/daemon.json.coolify > /dev/null",
'content' => base64_decode($config),
'destination' => '/tmp/daemon.json.new',
],
],
'test ! -s /etc/docker/daemon.json && cp /tmp/daemon.json.new /etc/docker/daemon.json',
'cp /tmp/daemon.json.new /etc/docker/daemon.json.coolify',
'rm -f /tmp/daemon.json.new',
'jq . /etc/docker/daemon.json.coolify | tee /etc/docker/daemon.json.coolify.pretty > /dev/null', 'jq . /etc/docker/daemon.json.coolify | tee /etc/docker/daemon.json.coolify.pretty > /dev/null',
'mv /etc/docker/daemon.json.coolify.pretty /etc/docker/daemon.json.coolify', 'mv /etc/docker/daemon.json.coolify.pretty /etc/docker/daemon.json.coolify',
"jq -s '.[0] * .[1]' /etc/docker/daemon.json.coolify /etc/docker/daemon.json | tee /etc/docker/daemon.json.appended > /dev/null", "jq -s '.[0] * .[1]' /etc/docker/daemon.json.coolify /etc/docker/daemon.json | tee /etc/docker/daemon.json.appended > /dev/null",

View file

@ -180,30 +180,10 @@ public function handle(Server $server)
$command = [ $command = [
"echo 'Saving configuration'", "echo 'Saving configuration'",
"mkdir -p $config_path", "mkdir -p $config_path",
[ "echo '{$parsers}' | base64 -d | tee $parsers_config > /dev/null",
'transfer_file' => [ "echo '{$config}' | base64 -d | tee $fluent_bit_config > /dev/null",
'content' => base64_decode($parsers), "echo '{$compose}' | base64 -d | tee $compose_path > /dev/null",
'destination' => $parsers_config, "echo '{$readme}' | base64 -d | tee $readme_path > /dev/null",
],
],
[
'transfer_file' => [
'content' => base64_decode($config),
'destination' => $fluent_bit_config,
],
],
[
'transfer_file' => [
'content' => base64_decode($compose),
'destination' => $compose_path,
],
],
[
'transfer_file' => [
'content' => base64_decode($readme),
'destination' => $readme_path,
],
],
"test -f $config_path/.env && rm $config_path/.env", "test -f $config_path/.env && rm $config_path/.env",
]; ];
if ($type === 'newrelic') { if ($type === 'newrelic') {

View file

@ -388,8 +388,11 @@ private function deploy_simple_dockerfile()
$dockerfile_base64 = base64_encode($this->application->dockerfile); $dockerfile_base64 = base64_encode($this->application->dockerfile);
$this->application_deployment_queue->addLogEntry("Starting deployment of {$this->application->name} to {$this->server->name}."); $this->application_deployment_queue->addLogEntry("Starting deployment of {$this->application->name} to {$this->server->name}.");
$this->prepare_builder_image(); $this->prepare_builder_image();
$dockerfile_content = base64_decode($dockerfile_base64); $this->execute_remote_command(
transfer_file_to_container($dockerfile_content, "{$this->workdir}{$this->dockerfile_location}", $this->deployment_uuid, $this->server); [
executeInDocker($this->deployment_uuid, "echo '$dockerfile_base64' | base64 -d | tee {$this->workdir}{$this->dockerfile_location} > /dev/null"),
],
);
$this->generate_image_names(); $this->generate_image_names();
$this->generate_compose_file(); $this->generate_compose_file();
$this->generate_build_env_variables(); $this->generate_build_env_variables();
@ -479,7 +482,7 @@ private function deploy_docker_compose_buildpack()
if (filled($this->env_filename)) { if (filled($this->env_filename)) {
$services = collect(data_get($composeFile, 'services', [])); $services = collect(data_get($composeFile, 'services', []));
$services = $services->map(function ($service, $name) { $services = $services->map(function ($service, $name) {
$service['env_file'] = ["/artifacts/{$this->env_filename}"]; $service['env_file'] = [$this->env_filename];
return $service; return $service;
}); });
@ -494,7 +497,10 @@ private function deploy_docker_compose_buildpack()
$yaml = Yaml::dump(convertToArray($composeFile), 10); $yaml = Yaml::dump(convertToArray($composeFile), 10);
} }
$this->docker_compose_base64 = base64_encode($yaml); $this->docker_compose_base64 = base64_encode($yaml);
transfer_file_to_container($yaml, "{$this->workdir}{$this->docker_compose_location}", $this->deployment_uuid, $this->server); $this->execute_remote_command([
executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d | tee {$this->workdir}{$this->docker_compose_location} > /dev/null"),
'hidden' => true,
]);
// Build new container to limit downtime. // Build new container to limit downtime.
$this->application_deployment_queue->addLogEntry('Pulling & building required images.'); $this->application_deployment_queue->addLogEntry('Pulling & building required images.');
@ -505,7 +511,7 @@ private function deploy_docker_compose_buildpack()
} else { } else {
$command = "{$this->coolify_variables} docker compose"; $command = "{$this->coolify_variables} docker compose";
if (filled($this->env_filename)) { if (filled($this->env_filename)) {
$command .= " --env-file /artifacts/{$this->env_filename}"; $command .= " --env-file {$this->workdir}/{$this->env_filename}";
} }
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";
@ -551,7 +557,7 @@ private function deploy_docker_compose_buildpack()
$command = "{$this->coolify_variables} docker compose"; $command = "{$this->coolify_variables} docker compose";
if (filled($this->env_filename)) { if (filled($this->env_filename)) {
$command .= " --env-file /artifacts/{$this->env_filename}"; $command .= " --env-file {$server_workdir}/{$this->env_filename}";
} }
$command .= " --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d"; $command .= " --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d";
$this->execute_remote_command( $this->execute_remote_command(
@ -568,7 +574,7 @@ private function deploy_docker_compose_buildpack()
$command = "{$this->coolify_variables} docker compose"; $command = "{$this->coolify_variables} docker compose";
if ($this->preserveRepository) { if ($this->preserveRepository) {
if (filled($this->env_filename)) { if (filled($this->env_filename)) {
$command .= " --env-file /artifacts/{$this->env_filename}"; $command .= " --env-file {$server_workdir}/{$this->env_filename}";
} }
$command .= " --project-name {$this->application->uuid} --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d"; $command .= " --project-name {$this->application->uuid} --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d";
$this->write_deployment_configurations(); $this->write_deployment_configurations();
@ -578,7 +584,7 @@ private function deploy_docker_compose_buildpack()
); );
} else { } else {
if (filled($this->env_filename)) { if (filled($this->env_filename)) {
$command .= " --env-file /artifacts/{$this->env_filename}"; $command .= " --env-file {$this->workdir}/{$this->env_filename}";
} }
$command .= " --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d"; $command .= " --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d";
$this->execute_remote_command( $this->execute_remote_command(
@ -709,12 +715,13 @@ private function write_deployment_configurations()
$composeFileName = "$mainDir/".addPreviewDeploymentSuffix('docker-compose', $this->pull_request_id).'.yaml'; $composeFileName = "$mainDir/".addPreviewDeploymentSuffix('docker-compose', $this->pull_request_id).'.yaml';
$this->docker_compose_location = '/'.addPreviewDeploymentSuffix('docker-compose', $this->pull_request_id).'.yaml'; $this->docker_compose_location = '/'.addPreviewDeploymentSuffix('docker-compose', $this->pull_request_id).'.yaml';
} }
$this->execute_remote_command([
"mkdir -p $mainDir",
]);
$docker_compose_content = base64_decode($this->docker_compose_base64);
transfer_file_to_server($docker_compose_content, $composeFileName, $this->server);
$this->execute_remote_command( $this->execute_remote_command(
[
"mkdir -p $mainDir",
],
[
"echo '{$this->docker_compose_base64}' | base64 -d | tee $composeFileName > /dev/null",
],
[ [
"echo '{$readme}' > $mainDir/README.md", "echo '{$readme}' > $mainDir/README.md",
] ]
@ -911,24 +918,8 @@ private function save_environment_variables()
}); });
if ($this->pull_request_id === 0) { if ($this->pull_request_id === 0) {
$this->env_filename = '.env'; $this->env_filename = '.env';
// Filter out buildtime-only variables from runtime environment
$runtime_environment_variables = $sorted_environment_variables->filter(function ($env) {
return ! $env->is_buildtime_only;
});
foreach ($runtime_environment_variables as $env) {
$envs->push($env->key.'='.$env->real_value);
}
// Add PORT if not exists, use the first port as default
if ($this->build_pack !== 'dockercompose') {
if ($this->application->environment_variables->where('key', 'PORT')->isEmpty()) {
$envs->push("PORT={$ports[0]}");
}
}
// Add HOST if not exists
if ($this->application->environment_variables->where('key', 'HOST')->isEmpty()) {
$envs->push('HOST=0.0.0.0');
}
// Generate SERVICE_ variables first for dockercompose
if ($this->build_pack === 'dockercompose') { if ($this->build_pack === 'dockercompose') {
$domains = collect(json_decode($this->application->docker_compose_domains)) ?? collect([]); $domains = collect(json_decode($this->application->docker_compose_domains)) ?? collect([]);
@ -957,26 +948,38 @@ private function save_environment_variables()
$envs->push('SERVICE_NAME_'.str($serviceName)->upper().'='.$serviceName); $envs->push('SERVICE_NAME_'.str($serviceName)->upper().'='.$serviceName);
} }
} }
} else {
$this->env_filename = '.env'; // Filter out buildtime-only variables from runtime environment
// Filter out buildtime-only variables from runtime environment for preview $runtime_environment_variables = $sorted_environment_variables->filter(function ($env) {
$runtime_environment_variables_preview = $sorted_environment_variables_preview->filter(function ($env) {
return ! $env->is_buildtime_only; return ! $env->is_buildtime_only;
}); });
foreach ($runtime_environment_variables_preview as $env) {
// Sort runtime environment variables: those referencing SERVICE_ variables come after others
$runtime_environment_variables = $runtime_environment_variables->sortBy(function ($env) {
if (str($env->value)->startsWith('$SERVICE_') || str($env->value)->contains('${SERVICE_')) {
return 2;
}
return 1;
});
foreach ($runtime_environment_variables as $env) {
$envs->push($env->key.'='.$env->real_value); $envs->push($env->key.'='.$env->real_value);
} }
// Add PORT if not exists, use the first port as default // Add PORT if not exists, use the first port as default
if ($this->build_pack !== 'dockercompose') { if ($this->build_pack !== 'dockercompose') {
if ($this->application->environment_variables_preview->where('key', 'PORT')->isEmpty()) { if ($this->application->environment_variables->where('key', 'PORT')->isEmpty()) {
$envs->push("PORT={$ports[0]}"); $envs->push("PORT={$ports[0]}");
} }
} }
// Add HOST if not exists // Add HOST if not exists
if ($this->application->environment_variables_preview->where('key', 'HOST')->isEmpty()) { if ($this->application->environment_variables->where('key', 'HOST')->isEmpty()) {
$envs->push('HOST=0.0.0.0'); $envs->push('HOST=0.0.0.0');
} }
} else {
$this->env_filename = '.env';
// Generate SERVICE_ variables first for dockercompose preview
if ($this->build_pack === 'dockercompose') { if ($this->build_pack === 'dockercompose') {
$domains = collect(json_decode(data_get($this->preview, 'docker_compose_domains'))) ?? collect([]); $domains = collect(json_decode(data_get($this->preview, 'docker_compose_domains'))) ?? collect([]);
@ -1001,6 +1004,34 @@ private function save_environment_variables()
$envs->push('SERVICE_NAME_'.str($rawServiceName)->upper().'='.addPreviewDeploymentSuffix($rawServiceName, $this->pull_request_id)); $envs->push('SERVICE_NAME_'.str($rawServiceName)->upper().'='.addPreviewDeploymentSuffix($rawServiceName, $this->pull_request_id));
} }
} }
// Filter out buildtime-only variables from runtime environment for preview
$runtime_environment_variables_preview = $sorted_environment_variables_preview->filter(function ($env) {
return ! $env->is_buildtime_only;
});
// Sort runtime environment variables: those referencing SERVICE_ variables come after others
$runtime_environment_variables_preview = $runtime_environment_variables_preview->sortBy(function ($env) {
if (str($env->value)->startsWith('$SERVICE_') || str($env->value)->contains('${SERVICE_')) {
return 2;
}
return 1;
});
foreach ($runtime_environment_variables_preview as $env) {
$envs->push($env->key.'='.$env->real_value);
}
// Add PORT if not exists, use the first port as default
if ($this->build_pack !== 'dockercompose') {
if ($this->application->environment_variables_preview->where('key', 'PORT')->isEmpty()) {
$envs->push("PORT={$ports[0]}");
}
}
// Add HOST if not exists
if ($this->application->environment_variables_preview->where('key', 'HOST')->isEmpty()) {
$envs->push('HOST=0.0.0.0');
}
} }
if ($envs->isEmpty()) { if ($envs->isEmpty()) {
if ($this->env_filename) { if ($this->env_filename) {
@ -1033,17 +1064,27 @@ private function save_environment_variables()
} }
$this->env_filename = null; $this->env_filename = null;
} else { } else {
$envs_content = $envs->implode("\n"); $envs_base64 = base64_encode($envs->implode("\n"));
transfer_file_to_container($envs_content, "/artifacts/{$this->env_filename}", $this->deployment_uuid, $this->server); $this->execute_remote_command(
[
executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d | tee $this->workdir/{$this->env_filename} > /dev/null"),
],
// Save the env filename with preview deployment suffix );
$env_filename = addPreviewDeploymentSuffix($this->env_filename, $this->pull_request_id);
if ($this->use_build_server) { if ($this->use_build_server) {
$this->server = $this->original_server; $this->server = $this->original_server;
transfer_file_to_server($envs_content, "$this->configuration_dir/{$env_filename}", $this->server); $this->execute_remote_command(
[
"echo '$envs_base64' | base64 -d | tee $this->configuration_dir/{$this->env_filename} > /dev/null",
]
);
$this->server = $this->build_server; $this->server = $this->build_server;
} else { } else {
transfer_file_to_server($envs_content, "$this->configuration_dir/{$env_filename}", $this->server); $this->execute_remote_command(
[
"echo '$envs_base64' | base64 -d | tee $this->configuration_dir/{$this->env_filename} > /dev/null",
]
);
} }
} }
$this->environment_variables = $envs; $this->environment_variables = $envs;
@ -1436,11 +1477,14 @@ private function check_git_if_build_needed()
} }
$private_key = data_get($this->application, 'private_key.private_key'); $private_key = data_get($this->application, 'private_key.private_key');
if ($private_key) { if ($private_key) {
$this->execute_remote_command([ $private_key = base64_encode($private_key);
executeInDocker($this->deployment_uuid, 'mkdir -p /root/.ssh'),
]);
transfer_file_to_container($private_key, '/root/.ssh/id_rsa', $this->deployment_uuid, $this->server);
$this->execute_remote_command( $this->execute_remote_command(
[
executeInDocker($this->deployment_uuid, 'mkdir -p /root/.ssh'),
],
[
executeInDocker($this->deployment_uuid, "echo '{$private_key}' | base64 -d | tee /root/.ssh/id_rsa > /dev/null"),
],
[ [
executeInDocker($this->deployment_uuid, 'chmod 600 /root/.ssh/id_rsa'), executeInDocker($this->deployment_uuid, 'chmod 600 /root/.ssh/id_rsa'),
], ],
@ -1845,7 +1889,7 @@ private function generate_compose_file()
], ],
]; ];
if (filled($this->env_filename)) { if (filled($this->env_filename)) {
$docker_compose['services'][$this->container_name]['env_file'] = ["/artifacts/{$this->env_filename}"]; $docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename];
} }
$docker_compose['services'][$this->container_name]['healthcheck'] = [ $docker_compose['services'][$this->container_name]['healthcheck'] = [
'test' => [ 'test' => [
@ -2002,7 +2046,7 @@ private function generate_compose_file()
$this->docker_compose = Yaml::dump($docker_compose, 10); $this->docker_compose = Yaml::dump($docker_compose, 10);
$this->docker_compose_base64 = base64_encode($this->docker_compose); $this->docker_compose_base64 = base64_encode($this->docker_compose);
transfer_file_to_container(base64_decode($this->docker_compose_base64), "{$this->workdir}/docker-compose.yaml", $this->deployment_uuid, $this->server); $this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d | tee {$this->workdir}/docker-compose.yaml > /dev/null"), 'hidden' => true]);
} }
private function generate_local_persistent_volumes() private function generate_local_persistent_volumes()
@ -2130,8 +2174,7 @@ private function build_image()
} 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);
$nixpacks_content = base64_decode($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]);
transfer_file_to_container($nixpacks_content, '/artifacts/thegameplan.json', $this->deployment_uuid, $this->server);
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 /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"),
@ -2155,7 +2198,7 @@ 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(
[ [
transfer_file_to_container(base64_decode($base64_build_command), '/artifacts/build.sh', $this->deployment_uuid, $this->server), executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
'hidden' => true, 'hidden' => true,
], ],
[ [
@ -2178,7 +2221,7 @@ private function build_image()
} }
$this->execute_remote_command( $this->execute_remote_command(
[ [
transfer_file_to_container(base64_decode($base64_build_command), '/artifacts/build.sh', $this->deployment_uuid, $this->server), executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
'hidden' => true, 'hidden' => true,
], ],
[ [
@ -2210,13 +2253,13 @@ 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(
[ [
transfer_file_to_container(base64_decode($dockerfile), "{$this->workdir}/Dockerfile", $this->deployment_uuid, $this->server), executeInDocker($this->deployment_uuid, "echo '{$dockerfile}' | base64 -d | tee {$this->workdir}/Dockerfile > /dev/null"),
], ],
[ [
transfer_file_to_container(base64_decode($nginx_config), "{$this->workdir}/nginx.conf", $this->deployment_uuid, $this->server), executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d | tee {$this->workdir}/nginx.conf > /dev/null"),
], ],
[ [
transfer_file_to_container(base64_decode($base64_build_command), '/artifacts/build.sh', $this->deployment_uuid, $this->server), executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
'hidden' => true, 'hidden' => true,
], ],
[ [
@ -2239,7 +2282,7 @@ 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(
[ [
transfer_file_to_container(base64_decode($base64_build_command), '/artifacts/build.sh', $this->deployment_uuid, $this->server), executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
'hidden' => true, 'hidden' => true,
], ],
[ [
@ -2254,8 +2297,7 @@ private function build_image()
} 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);
$nixpacks_content = base64_decode($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]);
transfer_file_to_container($nixpacks_content, '/artifacts/thegameplan.json', $this->deployment_uuid, $this->server);
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 /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"),
@ -2278,7 +2320,7 @@ 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(
[ [
transfer_file_to_container(base64_decode($base64_build_command), '/artifacts/build.sh', $this->deployment_uuid, $this->server), executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
'hidden' => true, 'hidden' => true,
], ],
[ [
@ -2301,7 +2343,7 @@ private function build_image()
} }
$this->execute_remote_command( $this->execute_remote_command(
[ [
transfer_file_to_container(base64_decode($base64_build_command), '/artifacts/build.sh', $this->deployment_uuid, $this->server), executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
'hidden' => true, 'hidden' => true,
], ],
[ [
@ -2434,7 +2476,7 @@ private function add_build_env_variables_to_dockerfile()
} }
$dockerfile_base64 = base64_encode($dockerfile->implode("\n")); $dockerfile_base64 = base64_encode($dockerfile->implode("\n"));
$this->execute_remote_command([ $this->execute_remote_command([
transfer_file_to_container(base64_decode($dockerfile_base64), "{$this->workdir}{$this->dockerfile_location}", $this->deployment_uuid, $this->server), executeInDocker($this->deployment_uuid, "echo '{$dockerfile_base64}' | base64 -d | tee {$this->workdir}{$this->dockerfile_location} > /dev/null"),
'hidden' => true, 'hidden' => true,
]); ]);
} }

View file

@ -232,12 +232,8 @@ public function runImport()
break; break;
} }
$this->importCommands[] = [ $restoreCommandBase64 = base64_encode($restoreCommand);
'transfer_file' => [ $this->importCommands[] = "echo \"{$restoreCommandBase64}\" | base64 -d > {$scriptPath}";
'content' => $restoreCommand,
'destination' => $scriptPath,
],
];
$this->importCommands[] = "chmod +x {$scriptPath}"; $this->importCommands[] = "chmod +x {$scriptPath}";
$this->importCommands[] = "docker cp {$scriptPath} {$this->container}:{$scriptPath}"; $this->importCommands[] = "docker cp {$scriptPath} {$this->container}:{$scriptPath}";

View file

@ -78,7 +78,10 @@ public function addDynamicConfiguration()
$yaml = Yaml::dump($yaml, 10, 2); $yaml = Yaml::dump($yaml, 10, 2);
$this->value = $yaml; $this->value = $yaml;
} }
transfer_file_to_server($this->value, $file, $this->server); $base64_value = base64_encode($this->value);
instant_remote_process([
"echo '{$base64_value}' | base64 -d | tee {$file} > /dev/null",
], $this->server);
if ($proxy_type === 'CADDY') { if ($proxy_type === 'CADDY') {
$this->server->reloadCaddy(); $this->server->reloadCaddy();
} }

View file

@ -1073,20 +1073,26 @@ public function generateGitLsRemoteCommands(string $deployment_uuid, bool $exec_
if (is_null($private_key)) { if (is_null($private_key)) {
throw new RuntimeException('Private key not found. Please add a private key to the application and try again.'); throw new RuntimeException('Private key not found. Please add a private key to the application and try again.');
} }
$private_key = base64_encode($private_key);
$base_comamnd = "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" {$base_command} {$customRepository}"; $base_comamnd = "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" {$base_command} {$customRepository}";
$commands = collect([]); if ($exec_in_docker) {
$commands = collect([
executeInDocker($deployment_uuid, 'mkdir -p /root/.ssh'),
executeInDocker($deployment_uuid, "echo '{$private_key}' | base64 -d | tee /root/.ssh/id_rsa > /dev/null"),
executeInDocker($deployment_uuid, 'chmod 600 /root/.ssh/id_rsa'),
]);
} else {
$commands = collect([
'mkdir -p /root/.ssh',
"echo '{$private_key}' | base64 -d | tee /root/.ssh/id_rsa > /dev/null",
'chmod 600 /root/.ssh/id_rsa',
]);
}
if ($exec_in_docker) { if ($exec_in_docker) {
$commands->push(executeInDocker($deployment_uuid, 'mkdir -p /root/.ssh'));
// SSH key transfer handled by ApplicationDeploymentJob, assume key is already in container
$commands->push(executeInDocker($deployment_uuid, 'chmod 600 /root/.ssh/id_rsa'));
$commands->push(executeInDocker($deployment_uuid, $base_comamnd)); $commands->push(executeInDocker($deployment_uuid, $base_comamnd));
} else { } else {
$server = $this->destination->server;
$commands->push('mkdir -p /root/.ssh');
transfer_file_to_server($private_key, '/root/.ssh/id_rsa', $server);
$commands->push('chmod 600 /root/.ssh/id_rsa');
$commands->push($base_comamnd); $commands->push($base_comamnd);
} }
@ -1212,6 +1218,7 @@ public function generateGitImportCommands(string $deployment_uuid, int $pull_req
if (is_null($private_key)) { if (is_null($private_key)) {
throw new RuntimeException('Private key not found. Please add a private key to the application and try again.'); throw new RuntimeException('Private key not found. Please add a private key to the application and try again.');
} }
$private_key = base64_encode($private_key);
$escapedCustomRepository = escapeshellarg($customRepository); $escapedCustomRepository = escapeshellarg($customRepository);
$git_clone_command_base = "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" {$git_clone_command} {$escapedCustomRepository} {$escapedBaseDir}"; $git_clone_command_base = "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" {$git_clone_command} {$escapedCustomRepository} {$escapedBaseDir}";
if ($only_checkout) { if ($only_checkout) {
@ -1219,18 +1226,18 @@ public function generateGitImportCommands(string $deployment_uuid, int $pull_req
} else { } else {
$git_clone_command = $this->setGitImportSettings($deployment_uuid, $git_clone_command_base); $git_clone_command = $this->setGitImportSettings($deployment_uuid, $git_clone_command_base);
} }
$commands = collect([]);
if ($exec_in_docker) { if ($exec_in_docker) {
$commands->push(executeInDocker($deployment_uuid, 'mkdir -p /root/.ssh')); $commands = collect([
// SSH key transfer handled by ApplicationDeploymentJob, assume key is already in container executeInDocker($deployment_uuid, 'mkdir -p /root/.ssh'),
$commands->push(executeInDocker($deployment_uuid, 'chmod 600 /root/.ssh/id_rsa')); executeInDocker($deployment_uuid, "echo '{$private_key}' | base64 -d | tee /root/.ssh/id_rsa > /dev/null"),
executeInDocker($deployment_uuid, 'chmod 600 /root/.ssh/id_rsa'),
]);
} else { } else {
$server = $this->destination->server; $commands = collect([
$commands->push('mkdir -p /root/.ssh'); 'mkdir -p /root/.ssh',
transfer_file_to_server($private_key, '/root/.ssh/id_rsa', $server); "echo '{$private_key}' | base64 -d | tee /root/.ssh/id_rsa > /dev/null",
$commands->push('chmod 600 /root/.ssh/id_rsa'); 'chmod 600 /root/.ssh/id_rsa',
]);
} }
if ($pull_request_id !== 0) { if ($pull_request_id !== 0) {
if ($git_type === 'gitlab') { if ($git_type === 'gitlab') {
@ -1563,7 +1570,19 @@ public function isWatchPathsTriggered(Collection $modified_files): bool
if (is_null($this->watch_paths)) { if (is_null($this->watch_paths)) {
return false; return false;
} }
$watch_paths = collect(explode("\n", $this->watch_paths)); $watch_paths = collect(explode("\n", $this->watch_paths))
->map(function (string $path): string {
return trim($path);
})
->filter(function (string $path): bool {
return strlen($path) > 0;
});
// If no valid patterns after filtering, don't trigger
if ($watch_paths->isEmpty()) {
return false;
}
$matches = $modified_files->filter(function ($file) use ($watch_paths) { $matches = $modified_files->filter(function ($file) use ($watch_paths) {
return $watch_paths->contains(function ($glob) use ($file) { return $watch_paths->contains(function ($glob) use ($file) {
return fnmatch($glob, $file); return fnmatch($glob, $file);

View file

@ -159,7 +159,8 @@ public function saveStorageOnServer()
$chmod = data_get($this, 'chmod'); $chmod = data_get($this, 'chmod');
$chown = data_get($this, 'chown'); $chown = data_get($this, 'chown');
if ($content) { if ($content) {
transfer_file_to_server($content, $path, $server); $content = base64_encode($content);
$commands->push("echo '$content' | base64 -d | tee $path > /dev/null");
} else { } else {
$commands->push("touch $path"); $commands->push("touch $path");
} }
@ -174,9 +175,7 @@ public function saveStorageOnServer()
$commands->push("mkdir -p $path > /dev/null 2>&1 || true"); $commands->push("mkdir -p $path > /dev/null 2>&1 || true");
} }
if ($commands->count() > 0) { return instant_remote_process($commands, $server);
return instant_remote_process($commands, $server);
}
} }
// Accessor for convenient access // Accessor for convenient access

View file

@ -309,7 +309,10 @@ public function setupDefaultRedirect()
$conf = Yaml::dump($dynamic_conf, 12, 2); $conf = Yaml::dump($dynamic_conf, 12, 2);
} }
$conf = $banner.$conf; $conf = $banner.$conf;
transfer_file_to_server($conf, $default_redirect_file, $this); $base64 = base64_encode($conf);
instant_remote_process([
"echo '$base64' | base64 -d | tee $default_redirect_file > /dev/null",
], $this);
} }
if ($proxy_type === 'CADDY') { if ($proxy_type === 'CADDY') {
@ -443,10 +446,11 @@ public function setupDynamicProxyConfiguration()
"# Do not edit it manually (only if you know what are you doing).\n\n". "# Do not edit it manually (only if you know what are you doing).\n\n".
$yaml; $yaml;
$base64 = base64_encode($yaml);
instant_remote_process([ instant_remote_process([
"mkdir -p $dynamic_config_path", "mkdir -p $dynamic_config_path",
"echo '$base64' | base64 -d | tee $file > /dev/null",
], $this); ], $this);
transfer_file_to_server($yaml, $file, $this);
} }
} elseif ($this->proxyType() === 'CADDY') { } elseif ($this->proxyType() === 'CADDY') {
$file = "$dynamic_config_path/coolify.caddy"; $file = "$dynamic_config_path/coolify.caddy";
@ -469,7 +473,10 @@ public function setupDynamicProxyConfiguration()
} }
reverse_proxy coolify:8080 reverse_proxy coolify:8080
}"; }";
transfer_file_to_server($caddy_file, $file, $this); $base64 = base64_encode($caddy_file);
instant_remote_process([
"echo '$base64' | base64 -d | tee $file > /dev/null",
], $this);
$this->reloadCaddy(); $this->reloadCaddy();
} }
} }
@ -1075,6 +1082,7 @@ public function sendUnreachableNotification()
public function validateConnection(bool $justCheckingNewKey = false) public function validateConnection(bool $justCheckingNewKey = false)
{ {
ray('validateConnection', $this->id);
$this->disableSshMux(); $this->disableSshMux();
if ($this->skipServer()) { if ($this->skipServer()) {
@ -1312,6 +1320,7 @@ private function disableSshMux(): void
public function generateCaCertificate() public function generateCaCertificate()
{ {
try { try {
ray('Generating CA certificate for server', $this->id);
SslHelper::generateSslCertificate( SslHelper::generateSslCertificate(
commonName: 'Coolify CA Certificate', commonName: 'Coolify CA Certificate',
serverId: $this->id, serverId: $this->id,
@ -1319,6 +1328,7 @@ public function generateCaCertificate()
validityDays: 10 * 365 validityDays: 10 * 365
); );
$caCertificate = SslCertificate::where('server_id', $this->id)->where('is_ca_certificate', true)->first(); $caCertificate = SslCertificate::where('server_id', $this->id)->where('is_ca_certificate', true)->first();
ray('CA certificate generated', $caCertificate);
if ($caCertificate) { if ($caCertificate) {
$certificateContent = $caCertificate->ssl_certificate; $certificateContent = $caCertificate->ssl_certificate;
$caCertPath = config('constants.coolify.base_config_path').'/ssl/'; $caCertPath = config('constants.coolify.base_config_path').'/ssl/';

View file

@ -1280,10 +1280,8 @@ public function saveComposeConfigs()
if ($envs->count() === 0) { if ($envs->count() === 0) {
$commands[] = 'touch .env'; $commands[] = 'touch .env';
} else { } else {
$envs_content = $envs->implode("\n"); $envs_base64 = base64_encode($envs->implode("\n"));
transfer_file_to_server($envs_content, $this->workdir().'/.env', $this->server); $commands[] = "echo '$envs_base64' | base64 -d | tee .env > /dev/null";
return;
} }
instant_remote_process($commands, $this->server); instant_remote_process($commands, $this->server);

View file

@ -1069,9 +1069,9 @@ function validateComposeFile(string $compose, int $server_id): string|Throwable
} }
} }
} }
$compose_content = Yaml::dump($yaml_compose); $base64_compose = base64_encode(Yaml::dump($yaml_compose));
transfer_file_to_server($compose_content, "/tmp/{$uuid}.yml", $server);
instant_remote_process([ instant_remote_process([
"echo {$base64_compose} | base64 -d | tee /tmp/{$uuid}.yml > /dev/null",
"chmod 600 /tmp/{$uuid}.yml", "chmod 600 /tmp/{$uuid}.yml",
"docker compose -f /tmp/{$uuid}.yml config --no-interpolate --no-path-resolution -q", "docker compose -f /tmp/{$uuid}.yml config --no-interpolate --no-path-resolution -q",
"rm /tmp/{$uuid}.yml", "rm /tmp/{$uuid}.yml",

View file

@ -29,31 +29,11 @@ function remote_process(
$type = $type ?? ActivityTypes::INLINE->value; $type = $type ?? ActivityTypes::INLINE->value;
$command = $command instanceof Collection ? $command->toArray() : $command; $command = $command instanceof Collection ? $command->toArray() : $command;
// Process commands and handle file transfers
$processed_commands = [];
foreach ($command as $cmd) {
if (is_array($cmd) && isset($cmd['transfer_file'])) {
// Handle file transfer command
$transfer_data = $cmd['transfer_file'];
$content = $transfer_data['content'];
$destination = $transfer_data['destination'];
// Execute file transfer immediately
transfer_file_to_server($content, $destination, $server, ! $ignore_errors);
// Add a comment to the command log for visibility
$processed_commands[] = "# File transferred via SCP: $destination";
} else {
// Regular string command
$processed_commands[] = $cmd;
}
}
if ($server->isNonRoot()) { if ($server->isNonRoot()) {
$processed_commands = parseCommandsByLineForSudo(collect($processed_commands), $server); $command = parseCommandsByLineForSudo(collect($command), $server);
} }
$command_string = implode("\n", $processed_commands); $command_string = implode("\n", $command);
if (Auth::check()) { if (Auth::check()) {
$teams = Auth::user()->teams->pluck('id'); $teams = Auth::user()->teams->pluck('id');
@ -200,30 +180,10 @@ function instant_remote_process(Collection|array $command, Server $server, bool
{ {
$command = $command instanceof Collection ? $command->toArray() : $command; $command = $command instanceof Collection ? $command->toArray() : $command;
// Process commands and handle file transfers
$processed_commands = [];
foreach ($command as $cmd) {
if (is_array($cmd) && isset($cmd['transfer_file'])) {
// Handle file transfer command
$transfer_data = $cmd['transfer_file'];
$content = $transfer_data['content'];
$destination = $transfer_data['destination'];
// Execute file transfer immediately
transfer_file_to_server($content, $destination, $server, $throwError);
// Add a comment to the command log for visibility
$processed_commands[] = "# File transferred via SCP: $destination";
} else {
// Regular string command
$processed_commands[] = $cmd;
}
}
if ($server->isNonRoot() && ! $no_sudo) { if ($server->isNonRoot() && ! $no_sudo) {
$processed_commands = parseCommandsByLineForSudo(collect($processed_commands), $server); $command = parseCommandsByLineForSudo(collect($command), $server);
} }
$command_string = implode("\n", $processed_commands); $command_string = implode("\n", $command);
return \App\Helpers\SshRetryHandler::retry( return \App\Helpers\SshRetryHandler::retry(
function () use ($server, $command_string) { function () use ($server, $command_string) {

View file

@ -69,11 +69,12 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Appli
$fileVolume->content = $content; $fileVolume->content = $content;
$fileVolume->is_directory = false; $fileVolume->is_directory = false;
$fileVolume->save(); $fileVolume->save();
$content = base64_encode($content);
$dir = str($fileLocation)->dirname(); $dir = str($fileLocation)->dirname();
instant_remote_process([ instant_remote_process([
"mkdir -p $dir", "mkdir -p $dir",
"echo '$content' | base64 -d | tee $fileLocation",
], $server); ], $server);
transfer_file_to_server($content, $fileLocation, $server);
} elseif ($isFile === 'NOK' && $isDir === 'NOK' && $fileVolume->is_directory && $isInit) { } elseif ($isFile === 'NOK' && $isDir === 'NOK' && $fileVolume->is_directory && $isInit) {
// Does not exists (no dir or file), flagged as directory, is init // Does not exists (no dir or file), flagged as directory, is init
$fileVolume->content = null; $fileVolume->content = null;

View file

@ -1125,30 +1125,77 @@ function get_public_ips()
function isAnyDeploymentInprogress() function isAnyDeploymentInprogress()
{ {
$runningJobs = ApplicationDeploymentQueue::where('horizon_job_worker', gethostname())->where('status', ApplicationDeploymentStatus::IN_PROGRESS->value)->get(); $runningJobs = ApplicationDeploymentQueue::where('horizon_job_worker', gethostname())->where('status', ApplicationDeploymentStatus::IN_PROGRESS->value)->get();
$basicDetails = $runningJobs->map(function ($job) {
return [ if ($runningJobs->isEmpty()) {
'id' => $job->id, echo "No deployments in progress.\n";
'created_at' => $job->created_at, exit(0);
'application_id' => $job->application_id, }
'server_id' => $job->server_id,
'horizon_job_id' => $job->horizon_job_id,
'status' => $job->status,
];
});
echo 'Running jobs: '.json_encode($basicDetails)."\n";
$horizonJobIds = []; $horizonJobIds = [];
$deploymentDetails = [];
foreach ($runningJobs as $runningJob) { foreach ($runningJobs as $runningJob) {
$horizonJobStatus = getJobStatus($runningJob->horizon_job_id); $horizonJobStatus = getJobStatus($runningJob->horizon_job_id);
if ($horizonJobStatus === 'unknown' || $horizonJobStatus === 'reserved') { if ($horizonJobStatus === 'unknown' || $horizonJobStatus === 'reserved') {
$horizonJobIds[] = $runningJob->horizon_job_id; $horizonJobIds[] = $runningJob->horizon_job_id;
// Get application and team information
$application = Application::find($runningJob->application_id);
$teamMembers = [];
$deploymentUrl = '';
if ($application) {
// Get team members through the application's project
$team = $application->team();
if ($team) {
$teamMembers = $team->members()->pluck('email')->toArray();
}
// Construct the full deployment URL
if ($runningJob->deployment_url) {
$baseUrl = base_url();
$deploymentUrl = $baseUrl.$runningJob->deployment_url;
}
}
$deploymentDetails[] = [
'id' => $runningJob->id,
'application_name' => $runningJob->application_name ?? 'Unknown',
'server_name' => $runningJob->server_name ?? 'Unknown',
'deployment_url' => $deploymentUrl,
'team_members' => $teamMembers,
'created_at' => $runningJob->created_at->format('Y-m-d H:i:s'),
'horizon_job_id' => $runningJob->horizon_job_id,
];
} }
} }
if (count($horizonJobIds) === 0) { if (count($horizonJobIds) === 0) {
echo "No deployments in progress.\n"; echo "No active deployments in progress (all jobs completed or failed).\n";
exit(0); exit(0);
} }
$horizonJobIds = collect($horizonJobIds)->unique()->toArray();
echo 'There are '.count($horizonJobIds)." deployments in progress.\n"; // Display enhanced deployment information
echo "\n=== Running Deployments ===\n";
echo 'Total active deployments: '.count($horizonJobIds)."\n\n";
foreach ($deploymentDetails as $index => $deployment) {
echo 'Deployment #'.($index + 1).":\n";
echo ' Application: '.$deployment['application_name']."\n";
echo ' Server: '.$deployment['server_name']."\n";
echo ' Started: '.$deployment['created_at']."\n";
if ($deployment['deployment_url']) {
echo ' URL: '.$deployment['deployment_url']."\n";
}
if (! empty($deployment['team_members'])) {
echo ' Team members: '.implode(', ', $deployment['team_members'])."\n";
} else {
echo " Team members: No team members found\n";
}
echo ' Horizon Job ID: '.$deployment['horizon_job_id']."\n";
echo "\n";
}
exit(1); exit(1);
} }

View file

@ -2,8 +2,8 @@
return [ return [
'coolify' => [ 'coolify' => [
'version' => '4.0.0-beta.427', 'version' => '4.0.0-beta.428',
'helper_version' => '1.0.10', 'helper_version' => '1.0.11',
'realtime_version' => '1.0.10', 'realtime_version' => '1.0.10',
'self_hosted' => env('SELF_HOSTED', true), 'self_hosted' => env('SELF_HOSTED', true),
'autoupdate' => env('AUTOUPDATE'), 'autoupdate' => env('AUTOUPDATE'),

View file

@ -7,7 +7,7 @@
"dependencies": { "dependencies": {
"@xterm/addon-fit": "0.10.0", "@xterm/addon-fit": "0.10.0",
"@xterm/xterm": "5.5.0", "@xterm/xterm": "5.5.0",
"axios": "1.8.4", "axios": "1.12.0",
"cookie": "1.0.2", "cookie": "1.0.2",
"dotenv": "16.5.0", "dotenv": "16.5.0",
"node-pty": "1.0.0", "node-pty": "1.0.0",
@ -36,13 +36,13 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/axios": { "node_modules/axios": {
"version": "1.8.4", "version": "1.12.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.0.tgz",
"integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", "integrity": "sha512-oXTDccv8PcfjZmPGlWsPSwtOJCZ/b6W5jAMCNcfwJbCzDckwG0jrYJFaWH1yvivfCXjVzV/SPDEhMB3Q+DSurg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"follow-redirects": "^1.15.6", "follow-redirects": "^1.15.6",
"form-data": "^4.0.0", "form-data": "^4.0.4",
"proxy-from-env": "^1.1.0" "proxy-from-env": "^1.1.0"
} }
}, },

View file

@ -5,7 +5,7 @@
"@xterm/addon-fit": "0.10.0", "@xterm/addon-fit": "0.10.0",
"@xterm/xterm": "5.5.0", "@xterm/xterm": "5.5.0",
"cookie": "1.0.2", "cookie": "1.0.2",
"axios": "1.8.4", "axios": "1.12.0",
"dotenv": "16.5.0", "dotenv": "16.5.0",
"node-pty": "1.0.0", "node-pty": "1.0.0",
"ws": "8.18.1" "ws": "8.18.1"

View file

@ -1,13 +1,13 @@
{ {
"coolify": { "coolify": {
"v4": { "v4": {
"version": "4.0.0-beta.427"
},
"nightly": {
"version": "4.0.0-beta.428" "version": "4.0.0-beta.428"
}, },
"nightly": {
"version": "4.0.0-beta.429"
},
"helper": { "helper": {
"version": "1.0.10" "version": "1.0.11"
}, },
"realtime": { "realtime": {
"version": "1.0.10" "version": "1.0.10"

View file

@ -1,13 +1,13 @@
{ {
"coolify": { "coolify": {
"v4": { "v4": {
"version": "4.0.0-beta.427"
},
"nightly": {
"version": "4.0.0-beta.428" "version": "4.0.0-beta.428"
}, },
"nightly": {
"version": "4.0.0-beta.429"
},
"helper": { "helper": {
"version": "1.0.10" "version": "1.0.11"
}, },
"realtime": { "realtime": {
"version": "1.0.10" "version": "1.0.10"