Merge branch 'main' into feat/scheduled-tasks-cron

This commit is contained in:
Stuart Rowlands 2024-01-05 15:08:20 +10:00
commit e233ec05b5
21 changed files with 264 additions and 93 deletions

View File

@ -19,10 +19,12 @@ # Donations
Special thanks to our biggest sponsors, [CCCareers](https://cccareers.org/) and [Appwrite](https://appwrite.io)! Special thanks to our biggest sponsors, [CCCareers](https://cccareers.org/) and [Appwrite](https://appwrite.io)!
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="appwrite logo" width="200"/></a> <a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="cccareers logo" width="200"/></a>
<a href="https://appwrite.io" target="_blank"><img src="./other/logos/appwrite.svg" alt="appwrite logo" width="200"/></a> <a href="https://appwrite.io" target="_blank"><img src="./other/logos/appwrite.svg" alt="appwrite logo" width="200"/></a>
## Github Sponsors ($15+) ## Github Sponsors ($15+)
<a href="https://bc.direct"><img width="60px" alt="BC Direct" src="https://github.com/coollabsio/coolify/assets/5845193/a4063c41-95ed-4a32-8814-cd1475572e37"/></a>
<a href="https://github.com/automazeio"><img src="https://github.com/automazeio.png" width="60px" alt="Corentin Clichy" /></a>
<a href="https://github.com/corentinclichy"><img src="https://github.com/corentinclichy.png" width="60px" alt="Corentin Clichy" /></a> <a href="https://github.com/corentinclichy"><img src="https://github.com/corentinclichy.png" width="60px" alt="Corentin Clichy" /></a>
<a href="https://github.com/Niki2k1"><img src="https://github.com/Niki2k1.png" width="60px" alt="Niklas Lausch" /></a> <a href="https://github.com/Niki2k1"><img src="https://github.com/Niki2k1.png" width="60px" alt="Niklas Lausch" /></a>
<a href="https://github.com/pixelinfinito"><img src="https://github.com/pixelinfinito.png" width="60px" alt="Pixel Infinito" /></a> <a href="https://github.com/pixelinfinito"><img src="https://github.com/pixelinfinito.png" width="60px" alt="Pixel Infinito" /></a>
@ -32,7 +34,6 @@ ## Github Sponsors ($15+)
<a href="https://github.com/Illyism"><img src="https://github.com/Illyism.png" width="60px" alt="Ilias Ism" /></a> <a href="https://github.com/Illyism"><img src="https://github.com/Illyism.png" width="60px" alt="Ilias Ism" /></a>
<a href="https://github.com/urtho"><img src="https://github.com/urtho.png" width="60px" alt="Paweł Pierścionek" /></a> <a href="https://github.com/urtho"><img src="https://github.com/urtho.png" width="60px" alt="Paweł Pierścionek" /></a>
<a href="https://github.com/monocursive"><img src="https://github.com/monocursive.png" width="60px" alt="Michael Mazurczak" /></a> <a href="https://github.com/monocursive"><img src="https://github.com/monocursive.png" width="60px" alt="Michael Mazurczak" /></a>
<a href="https://github.com/cccareers"><img src="https://github.com/cccareers.png" width="60px" alt="Creating Coding Careers" /></a>
## Organizations ## Organizations
<a href="https://opencollective.com/coollabsio/organization/0/website"><img src="https://opencollective.com/coollabsio/organization/0/avatar.svg"></a> <a href="https://opencollective.com/coollabsio/organization/0/website"><img src="https://opencollective.com/coollabsio/organization/0/avatar.svg"></a>

View File

@ -21,14 +21,14 @@ public function handle(Service $service)
foreach ($storages as $storage) { foreach ($storages as $storage) {
$storagesToDelete->push($storage); $storagesToDelete->push($storage);
} }
$application->delete(); $application->forceDelete();
} }
foreach ($service->databases()->get() as $database) { foreach ($service->databases()->get() as $database) {
$storages = $database->persistentStorages()->get(); $storages = $database->persistentStorages()->get();
foreach ($storages as $storage) { foreach ($storages as $storage) {
$storagesToDelete->push($storage); $storagesToDelete->push($storage);
} }
$database->delete(); $database->forceDelete();
} }
foreach ($storagesToDelete as $storage) { foreach ($storagesToDelete as $storage) {
$commands[] = "docker volume rm -f $storage->name"; $commands[] = "docker volume rm -f $storage->name";

View File

@ -55,7 +55,8 @@ public function handle()
} }
} }
} }
private function restore_coolify_db_backup() { private function restore_coolify_db_backup()
{
try { try {
$database = StandalonePostgresql::withTrashed()->find(0); $database = StandalonePostgresql::withTrashed()->find(0);
if ($database && $database->trashed()) { if ($database && $database->trashed()) {
@ -73,9 +74,8 @@ private function restore_coolify_db_backup() {
'team_id' => 0, 'team_id' => 0,
]); ]);
} }
} }
} catch(\Throwable $e) { } catch (\Throwable $e) {
echo "Error in restoring coolify db backup: {$e->getMessage()}\n"; echo "Error in restoring coolify db backup: {$e->getMessage()}\n";
} }
} }
@ -138,6 +138,89 @@ private function cleanup_in_progress_application_deployments()
} }
private function cleanup_stucked_resources() private function cleanup_stucked_resources()
{ {
try {
$applications = Application::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($applications as $application) {
echo "Deleting stucked application: {$application->name}\n";
$application->forceDelete();
}
} catch (\Throwable $e) {
echo "Error in cleaning stucked application: {$e->getMessage()}\n";
}
try {
$postgresqls = StandalonePostgresql::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($postgresqls as $postgresql) {
echo "Deleting stucked postgresql: {$postgresql->name}\n";
$postgresql->forceDelete();
}
} catch (\Throwable $e) {
echo "Error in cleaning stucked postgresql: {$e->getMessage()}\n";
}
try {
$redis = StandaloneRedis::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($redis as $redis) {
echo "Deleting stucked redis: {$redis->name}\n";
$redis->forceDelete();
}
} catch (\Throwable $e) {
echo "Error in cleaning stucked redis: {$e->getMessage()}\n";
}
try {
$mongodbs = StandaloneMongodb::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($mongodbs as $mongodb) {
echo "Deleting stucked mongodb: {$mongodb->name}\n";
$mongodb->forceDelete();
}
} catch (\Throwable $e) {
echo "Error in cleaning stucked mongodb: {$e->getMessage()}\n";
}
try {
$mysqls = StandaloneMysql::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($mysqls as $mysql) {
echo "Deleting stucked mysql: {$mysql->name}\n";
$mysql->forceDelete();
}
} catch (\Throwable $e) {
echo "Error in cleaning stucked mysql: {$e->getMessage()}\n";
}
try {
$mariadbs = StandaloneMariadb::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($mariadbs as $mariadb) {
echo "Deleting stucked mariadb: {$mariadb->name}\n";
$mariadb->forceDelete();
}
} catch (\Throwable $e) {
echo "Error in cleaning stucked mariadb: {$e->getMessage()}\n";
}
try {
$services = Service::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($services as $service) {
echo "Deleting stucked service: {$service->name}\n";
$service->forceDelete();
}
} catch (\Throwable $e) {
echo "Error in cleaning stucked service: {$e->getMessage()}\n";
}
try {
$serviceApps = ServiceApplication::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($serviceApps as $serviceApp) {
echo "Deleting stucked serviceapp: {$serviceApp->name}\n";
$serviceApp->forceDelete();
}
} catch (\Throwable $e) {
echo "Error in cleaning stucked serviceapp: {$e->getMessage()}\n";
}
try {
$serviceDbs = ServiceDatabase::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($serviceDbs as $serviceDb) {
echo "Deleting stucked serviceapp: {$serviceDb->name}\n";
$serviceDb->forceDelete();
}
} catch (\Throwable $e) {
echo "Error in cleaning stucked serviceapp: {$e->getMessage()}\n";
}
// Cleanup any resources that are not attached to any environment or destination or server // Cleanup any resources that are not attached to any environment or destination or server
try { try {
$applications = Application::all(); $applications = Application::all();

View File

@ -164,11 +164,15 @@ public function handle(): void
$ips = collect([]); $ips = collect([]);
if (count($allContainers) > 0) { if (count($allContainers) > 0) {
$allContainers = $allContainers[0]; $allContainers = $allContainers[0];
$allContainers = collect($allContainers)->sort()->values();
foreach ($allContainers as $container) { foreach ($allContainers as $container) {
$containerName = data_get($container, 'Name'); $containerName = data_get($container, 'Name');
if ($containerName === 'coolify-proxy') { if ($containerName === 'coolify-proxy') {
continue; continue;
} }
if (preg_match('/-(\d{12})/',$containerName)) {
continue;
}
$containerIp = data_get($container, 'IPv4Address'); $containerIp = data_get($container, 'IPv4Address');
if ($containerName && $containerIp) { if ($containerName && $containerIp) {
$containerIp = str($containerIp)->before('/'); $containerIp = str($containerIp)->before('/');
@ -446,8 +450,12 @@ private function deploy_docker_compose_buildpack()
$this->generate_image_names(); $this->generate_image_names();
$this->cleanup_git(); $this->cleanup_git();
$this->application->loadComposeFile(isInit: false); $this->application->loadComposeFile(isInit: false);
$composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id); if ($this->application->settings->is_raw_compose_deployment_enabled) {
$yaml = Yaml::dump($composeFile->toArray(), 10); $yaml = $composeFile = $this->application->docker_compose_raw;
} else {
$composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id);
$yaml = Yaml::dump($composeFile->toArray(), 10);
}
$this->docker_compose_base64 = base64_encode($yaml); $this->docker_compose_base64 = base64_encode($yaml);
$this->execute_remote_command([ $this->execute_remote_command([
executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}{$this->docker_compose_location}"), "hidden" => true executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}{$this->docker_compose_location}"), "hidden" => true
@ -871,11 +879,11 @@ private function generate_nixpacks_confs()
private function nixpacks_build_cmd() private function nixpacks_build_cmd()
{ {
$this->generate_env_variables(); $this->generate_env_variables();
$cacheKey = $this->application->uuid; // $cacheKey = $this->application->uuid;
if ($this->pull_request_id !== 0) { // if ($this->pull_request_id !== 0) {
$cacheKey = "{$this->application->uuid}-pr-{$this->pull_request_id}"; // $cacheKey = "{$this->application->uuid}-pr-{$this->pull_request_id}";
} // }
$nixpacks_command = "nixpacks build --cache-key '{$cacheKey}' -o {$this->workdir} {$this->env_args} --no-error-without-start"; $nixpacks_command = "nixpacks build {$this->env_args} --no-error-without-start";
if ($this->application->build_command) { if ($this->application->build_command) {
$nixpacks_command .= " --build-cmd \"{$this->application->build_command}\""; $nixpacks_command .= " --build-cmd \"{$this->application->build_command}\"";
} }
@ -885,7 +893,7 @@ private function nixpacks_build_cmd()
if ($this->application->install_command) { if ($this->application->install_command) {
$nixpacks_command .= " --install-cmd \"{$this->application->install_command}\""; $nixpacks_command .= " --install-cmd \"{$this->application->install_command}\"";
} }
$nixpacks_command .= " {$this->workdir}"; $nixpacks_command .= " -o {$this->workdir} {$this->workdir}";
return $nixpacks_command; return $nixpacks_command;
} }
@ -1129,7 +1137,8 @@ private function generate_environment_variables($ports)
// Add PORT if not exists, use the first port as default // Add PORT if not exists, use the first port as default
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('PORT'))->isEmpty()) { if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('PORT'))->isEmpty()) {
$environment_variables->push("PORT={$ports[0]}"); $environment_variables->push("PORT={$ports[0]}");
} if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('SOURCE_COMMIT'))->isEmpty()) { }
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('SOURCE_COMMIT'))->isEmpty()) {
if (!is_null($this->commit)) { if (!is_null($this->commit)) {
$environment_variables->push("SOURCE_COMMIT={$this->commit}"); $environment_variables->push("SOURCE_COMMIT={$this->commit}");
} }
@ -1192,6 +1201,9 @@ private function build_image()
if ($this->application->settings->is_static || $this->application->build_pack === 'static') { if ($this->application->settings->is_static || $this->application->build_pack === 'static') {
if ($this->application->static_image) { if ($this->application->static_image) {
$this->pull_latest_image($this->application->static_image); $this->pull_latest_image($this->application->static_image);
$this->execute_remote_command(
["echo -n 'Continue with the building process.'"],
);
} }
if ($this->application->build_pack === 'static') { if ($this->application->build_pack === 'static') {
$dockerfile = base64_encode("FROM {$this->application->static_image} $dockerfile = base64_encode("FROM {$this->application->static_image}
@ -1218,9 +1230,23 @@ private function build_image()
} }
}"); }");
} else { } else {
$this->execute_remote_command([ if ($this->application->build_pack === 'nixpacks') {
executeInDocker($this->deployment_uuid, "docker build $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true $this->execute_remote_command(
]); [
executeInDocker($this->deployment_uuid, "cp {$this->workdir}/Dockerfile {$this->workdir}/.nixpacks/Dockerfile")
],
);
}
if ($this->force_rebuild) {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build --no-cache $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
]);
} else {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
]);
}
// }
$dockerfile = base64_encode("FROM {$this->application->static_image} $dockerfile = base64_encode("FROM {$this->application->static_image}
WORKDIR /usr/share/nginx/html/ WORKDIR /usr/share/nginx/html/
@ -1263,9 +1289,23 @@ private function build_image()
executeInDocker($this->deployment_uuid, "docker build --pull $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true executeInDocker($this->deployment_uuid, "docker build --pull $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
]); ]);
} else { } else {
$this->execute_remote_command([ if ($this->application->build_pack === 'nixpacks') {
executeInDocker($this->deployment_uuid, "docker build $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true $this->execute_remote_command(
]); [
executeInDocker($this->deployment_uuid, "cp {$this->workdir}/Dockerfile {$this->workdir}/.nixpacks/Dockerfile")
],
);
}
if ($this->force_rebuild) {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build --no-cache $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
]);
} else {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
]);
}
} }
} }
$this->execute_remote_command([ $this->execute_remote_command([
@ -1291,6 +1331,9 @@ private function stop_running_container(bool $force = false)
}); });
} else { } else {
$this->application_deployment_queue->addLogEntry("New container is not healthy, rolling back to the old container."); $this->application_deployment_queue->addLogEntry("New container is not healthy, rolling back to the old container.");
$this->application_deployment_queue->update([
'status' => ApplicationDeploymentStatus::FAILED->value,
]);
$this->execute_remote_command( $this->execute_remote_command(
[executeInDocker($this->deployment_uuid, "docker rm -f $this->container_name >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true], [executeInDocker($this->deployment_uuid, "docker rm -f $this->container_name >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true],
); );

View File

@ -66,6 +66,7 @@ class General extends Component
'application.settings.is_static' => 'boolean|required', 'application.settings.is_static' => 'boolean|required',
'application.docker_compose_custom_start_command' => 'nullable', 'application.docker_compose_custom_start_command' => 'nullable',
'application.docker_compose_custom_build_command' => 'nullable', 'application.docker_compose_custom_build_command' => 'nullable',
'application.settings.is_raw_compose_deployment_enabled' => 'boolean|required',
]; ];
protected $validationAttributes = [ protected $validationAttributes = [
'application.name' => 'name', 'application.name' => 'name',
@ -98,6 +99,7 @@ class General extends Component
'application.settings.is_static' => 'Is static', 'application.settings.is_static' => 'Is static',
'application.docker_compose_custom_start_command' => 'Docker compose custom start command', 'application.docker_compose_custom_start_command' => 'Docker compose custom start command',
'application.docker_compose_custom_build_command' => 'Docker compose custom build command', 'application.docker_compose_custom_build_command' => 'Docker compose custom build command',
'application.settings.is_raw_compose_deployment_enabled' => 'Is raw compose deployment enabled',
]; ];
public function mount() public function mount()
{ {
@ -114,6 +116,11 @@ public function mount()
} }
$this->isConfigurationChanged = $this->application->isConfigurationChanged(); $this->isConfigurationChanged = $this->application->isConfigurationChanged();
$this->customLabels = $this->application->parseContainerLabels(); $this->customLabels = $this->application->parseContainerLabels();
if (!$this->customLabels && $this->application->destination->server->proxyType() === 'TRAEFIK_V2') {
$this->customLabels = str(implode(",", generateLabelsApplication($this->application)))->replace(',', "\n");
$this->application->custom_labels = base64_encode($this->customLabels);
$this->application->save();
}
$this->initialDockerComposeLocation = $this->application->docker_compose_location; $this->initialDockerComposeLocation = $this->application->docker_compose_location;
$this->checkLabelUpdates(); $this->checkLabelUpdates();
} }
@ -199,7 +206,12 @@ public function updatedApplicationFqdn()
public function submit($showToaster = true) public function submit($showToaster = true)
{ {
try { try {
ray($this->initialDockerComposeLocation, $this->application->docker_compose_location); if (!$this->customLabels && $this->application->destination->server->proxyType() === 'TRAEFIK_V2') {
$this->customLabels = str(implode(",", generateLabelsApplication($this->application)))->replace(',', "\n");
$this->application->custom_labels = base64_encode($this->customLabels);
$this->application->save();
}
if ($this->application->build_pack === 'dockercompose' && $this->initialDockerComposeLocation !== $this->application->docker_compose_location) { if ($this->application->build_pack === 'dockercompose' && $this->initialDockerComposeLocation !== $this->application->docker_compose_location) {
$this->loadComposeFile(); $this->loadComposeFile();
} }
@ -207,6 +219,7 @@ public function submit($showToaster = true)
if ($this->ports_exposes !== $this->application->ports_exposes) { if ($this->ports_exposes !== $this->application->ports_exposes) {
$this->resetDefaultLabels(false); $this->resetDefaultLabels(false);
} }
if (data_get($this->application, 'build_pack') === 'dockerimage') { if (data_get($this->application, 'build_pack') === 'dockerimage') {
$this->validate([ $this->validate([
'application.docker_registry_image_name' => 'required', 'application.docker_registry_image_name' => 'required',

View File

@ -430,7 +430,7 @@ public function isLogDrainEnabled()
public function isConfigurationChanged($save = false) public function isConfigurationChanged($save = false)
{ {
$newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command . $this->build_command . $this->start_command . $this->port_exposes . $this->port_mappings . $this->base_directory . $this->publish_directory . $this->health_check_path . $this->health_check_port . $this->health_check_host . $this->health_check_method . $this->health_check_return_code . $this->health_check_scheme . $this->health_check_response_text . $this->health_check_interval . $this->health_check_timeout . $this->health_check_retries . $this->health_check_start_period . $this->health_check_enabled . $this->limits_memory . $this->limits_swap . $this->limits_swappiness . $this->limits_reservation . $this->limits_cpus . $this->limits_cpuset . $this->limits_cpu_shares . $this->dockerfile . $this->dockerfile_location . $this->custom_labels; $newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command . $this->build_command . $this->start_command . $this->port_exposes . $this->port_mappings . $this->base_directory . $this->publish_directory . $this->health_check_path . $this->health_check_port . $this->health_check_host . $this->health_check_method . $this->health_check_return_code . $this->health_check_scheme . $this->health_check_response_text . $this->health_check_interval . $this->health_check_timeout . $this->health_check_retries . $this->health_check_start_period . $this->health_check_enabled . $this->limits_memory . $this->limits_swap . $this->limits_swappiness . $this->limits_reservation . $this->limits_cpus . $this->limits_cpuset . $this->limits_cpu_shares . $this->dockerfile . $this->dockerfile_location . $this->custom_labels;
if ($this->pull_request_id === 0) { if ($this->pull_request_id === 0 || $this->pull_request_id === null) {
$newConfigHash .= json_encode($this->environment_variables->all()); $newConfigHash .= json_encode($this->environment_variables->all());
} else { } else {
$newConfigHash .= json_encode($this->environment_variables_preview->all()); $newConfigHash .= json_encode($this->environment_variables_preview->all());

View File

@ -41,11 +41,11 @@ function queue_application_deployment(int $application_id, string $deployment_uu
dispatch(new ApplicationDeploymentNewJob( dispatch(new ApplicationDeploymentNewJob(
deployment: $deployment, deployment: $deployment,
application: Application::find($application_id) application: Application::find($application_id)
))->onConnection('long-running')->onQueue('long-running'); ));
} else { } else {
dispatch(new ApplicationDeploymentJob( dispatch(new ApplicationDeploymentJob(
application_deployment_queue_id: $deployment->id, application_deployment_queue_id: $deployment->id,
))->onConnection('long-running')->onQueue('long-running'); ));
} }
} }
@ -57,11 +57,11 @@ function queue_next_deployment(Application $application, bool $isNew = false)
dispatch(new ApplicationDeploymentNewJob( dispatch(new ApplicationDeploymentNewJob(
deployment: $next_found, deployment: $next_found,
application: $application application: $application
))->onConnection('long-running')->onQueue('long-running'); ));
} else { } else {
dispatch(new ApplicationDeploymentJob( dispatch(new ApplicationDeploymentJob(
application_deployment_queue_id: $next_found->id, application_deployment_queue_id: $next_found->id,
))->onConnection('long-running')->onQueue('long-running'); ));
} }
} }
} }

View File

@ -1035,7 +1035,9 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
} }
data_set($service, 'labels', $serviceLabels->toArray()); data_set($service, 'labels', $serviceLabels->toArray());
data_forget($service, 'is_database'); data_forget($service, 'is_database');
data_set($service, 'restart', RESTART_MODE); if (!data_get($service, 'restart')) {
data_set($service, 'restart', RESTART_MODE);
}
data_set($service, 'container_name', $containerName); data_set($service, 'container_name', $containerName);
data_forget($service, 'volumes.*.content'); data_forget($service, 'volumes.*.content');
data_forget($service, 'volumes.*.isDirectory'); data_forget($service, 'volumes.*.isDirectory');
@ -1399,6 +1401,11 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$key = $value; $key = $value;
$defaultValue = null; $defaultValue = null;
} }
$foundEnv = EnvironmentVariable::where([
'key' => $key,
'application_id' => $resource->id,
'is_preview' => false,
])->first();
if ($foundEnv) { if ($foundEnv) {
$defaultValue = data_get($foundEnv, 'value'); $defaultValue = data_get($foundEnv, 'value');
} }
@ -1468,7 +1475,9 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
} }
data_set($service, 'labels', $serviceLabels->toArray()); data_set($service, 'labels', $serviceLabels->toArray());
data_forget($service, 'is_database'); data_forget($service, 'is_database');
data_set($service, 'restart', RESTART_MODE); if (!data_get($service, 'restart')) {
data_set($service, 'restart', RESTART_MODE);
}
data_set($service, 'container_name', $containerName); data_set($service, 'container_name', $containerName);
data_forget($service, 'volumes.*.content'); data_forget($service, 'volumes.*.content');
data_forget($service, 'volumes.*.isDirectory'); data_forget($service, 'volumes.*.isDirectory');

View File

@ -184,19 +184,8 @@
'connection' => 'redis', 'connection' => 'redis',
'queue' => ['default'], 'queue' => ['default'],
'balance' => 'auto', 'balance' => 'auto',
'autoScalingStrategy' => 'time', // 'autoScalingStrategy' => 'time',
'maxProcesses' => 1, // 'maxProcesses' => 1,
'maxTime' => 0,
'maxJobs' => 0,
'memory' => 128,
'tries' => 1,
'timeout' => 300,
'nice' => 0,
],
'long-running' => [
'connection' => 'redis',
'queue' => ['long-running'],
'balance' => 'auto',
'maxTime' => 0, 'maxTime' => 0,
'maxJobs' => 0, 'maxJobs' => 0,
'memory' => 128, 'memory' => 128,
@ -209,27 +198,15 @@
'environments' => [ 'environments' => [
'production' => [ 'production' => [
's6' => [ 's6' => [
'autoScalingStrategy' => 'size',
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 2),
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1),
],
'long-running' => [
'autoScalingStrategy' => 'size', 'autoScalingStrategy' => 'size',
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 6), 'maxProcesses' => env('HORIZON_MAX_PROCESSES', 6),
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1), 'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1), 'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1),
], ],
],
],
'local' => [ 'local' => [
's6' => [ 's6' => [
'autoScalingStrategy' => 'size',
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 2),
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1),
],
'long-running' => [
'autoScalingStrategy' => 'size', 'autoScalingStrategy' => 'size',
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 6), 'maxProcesses' => env('HORIZON_MAX_PROCESSES', 6),
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1), 'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),

View File

@ -33,14 +33,6 @@
'sync' => [ 'sync' => [
'driver' => 'sync', 'driver' => 'sync',
], ],
'long-running' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => 'long-running',
'retry_after' => 3600,
'block_for' => null,
'after_commit' => true,
],
'database' => [ 'database' => [
'driver' => 'database', 'driver' => 'database',
'table' => 'jobs', 'table' => 'jobs',

View File

@ -7,7 +7,7 @@
// The release version of your application // The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
'release' => '4.0.0-beta.175', 'release' => '4.0.0-beta.182',
// When left empty or `null` the Laravel environment will be used // When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'), 'environment' => config('app.env'),

View File

@ -1,3 +1,3 @@
<?php <?php
return '4.0.0-beta.175'; return '4.0.0-beta.182';

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('application_settings', function (Blueprint $table) {
$table->boolean('is_raw_compose_deployment_enabled')->default(false);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('application_settings', function (Blueprint $table) {
$table->dropColumn('is_raw_compose_deployment_enabled');
});
}
};

View File

@ -8,9 +8,9 @@ ARG DOCKER_COMPOSE_VERSION=2.21.0
# https://github.com/docker/buildx/releases # https://github.com/docker/buildx/releases
ARG DOCKER_BUILDX_VERSION=0.11.2 ARG DOCKER_BUILDX_VERSION=0.11.2
# https://github.com/buildpacks/pack/releases # https://github.com/buildpacks/pack/releases
ARG PACK_VERSION=0.31.0 ARG PACK_VERSION=0.32.1
# https://github.com/railwayapp/nixpacks/releases # https://github.com/railwayapp/nixpacks/releases
ARG NIXPACKS_VERSION=1.18.0 ARG NIXPACKS_VERSION=1.20.0
USER root USER root
WORKDIR /artifacts WORKDIR /artifacts

View File

@ -1,12 +0,0 @@
@isset($title, $action)
<div tabindex="0" x-data="{ open: false }"
class="transition border rounded cursor-pointer collapse collapse-arrow border-coolgray-200"
:class="open ? 'collapse-open' : 'collapse-close'">
<div class="flex flex-col justify-center text-sm select-text collapse-title" x-on:click="open = !open">
{{ $title }}
</div>
<div class="collapse-content">
{{ $action }}
</div>
</div>
@endisset

View File

@ -5,9 +5,10 @@
</div> </div>
<div>Advanced configuration for your application.</div> <div>Advanced configuration for your application.</div>
<div class="flex flex-col w-full pt-4"> <div class="flex flex-col w-full pt-4">
@if (!$application->settings->is_raw_compose_deployment_enabled)
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings." <x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
instantSave id="application.settings.is_log_drain_enabled" label="Drain Logs" /> instantSave id="application.settings.is_log_drain_enabled" label="Drain Logs" />
@endif
<x-forms.checkbox <x-forms.checkbox
helper="Your application will be available only on https if your domain starts with https://..." helper="Your application will be available only on https if your domain starts with https://..."
instantSave id="application.settings.is_force_https_enabled" label="Force Https" /> instantSave id="application.settings.is_force_https_enabled" label="Force Https" />

View File

@ -48,7 +48,10 @@
</div> </div>
@endif @endif
@if ($application->build_pack === 'dockercompose') @if ($application->build_pack === 'dockercompose')
@if (count($parsedServices) > 0) <x-forms.checkbox instantSave id="application.settings.is_raw_compose_deployment_enabled"
label="Raw Compose Deployment"
helper="WARNING: Advanced use cases only. Your docker compose file will be deployed as-is. Nothing is modified by Coolify. You need to configure the proxy parts. More info in the <a href='https://coolify.io/docs/docker-compose'>documentation.</a>" />
@if (count($parsedServices) > 0 && !$application->settings->is_raw_compose_deployment_enabled)
@foreach (data_get($parsedServices, 'services') as $serviceName => $service) @foreach (data_get($parsedServices, 'services') as $serviceName => $service)
@if (!isDatabaseImage(data_get($service, 'image'))) @if (!isDatabaseImage(data_get($service, 'image')))
<div class="flex items-end gap-2"> <div class="flex items-end gap-2">
@ -184,8 +187,13 @@ class="underline" href="https://coolify.io/docs/docker-registries"
@endif @endif
@if ($application->build_pack === 'dockercompose') @if ($application->build_pack === 'dockercompose')
<x-forms.button wire:click="loadComposeFile">Reload Compose File</x-forms.button> <x-forms.button wire:click="loadComposeFile">Reload Compose File</x-forms.button>
<x-forms.textarea rows="10" readonly id="application.docker_compose" @if ($application->settings->is_raw_compose_deployment_enabled)
label="Docker Compose Content" helper="You need to modify the docker compose file." /> <x-forms.textarea rows="10" readonly id="application.docker_compose_raw"
label="Docker Compose Content (applicationId: {{$application->id}})" helper="You need to modify the docker compose file." />
@else
<x-forms.textarea rows="10" readonly id="application.docker_compose"
label="Docker Compose Content" helper="You need to modify the docker compose file." />
@endif
{{-- <x-forms.textarea rows="10" readonly id="application.docker_compose_pr" {{-- <x-forms.textarea rows="10" readonly id="application.docker_compose_pr"
label="Docker PR Compose Content" helper="You need to modify the docker compose file." /> --}} label="Docker PR Compose Content" helper="You need to modify the docker compose file." /> --}}
@endif @endif

View File

@ -1,8 +1,10 @@
<x-collapsible> <div tabindex="0" x-data="{ open: false }"
<x-slot:title> class="transition border rounded cursor-pointer collapse collapse-arrow border-coolgray-200"
:class="open ? 'collapse-open' : 'collapse-close'">
<div class="flex flex-col justify-center text-sm select-text collapse-title" x-on:click="open = !open">
<div>{{ $workdir }}{{ $fs_path }} -> {{ $fileStorage->mount_path }}</div> <div>{{ $workdir }}{{ $fs_path }} -> {{ $fileStorage->mount_path }}</div>
</x-slot:title> </div>
<x-slot:action> <div class="collapse-content">
<form wire:submit='submit' class="flex flex-col gap-2"> <form wire:submit='submit' class="flex flex-col gap-2">
<div class="w-64"> <div class="w-64">
<x-forms.checkbox instantSave label="Is directory?" id="fileStorage.is_directory"></x-forms.checkbox> <x-forms.checkbox instantSave label="Is directory?" id="fileStorage.is_directory"></x-forms.checkbox>
@ -21,5 +23,5 @@
@endif @endif
{{-- @endif --}} {{-- @endif --}}
</form> </form>
</x-slot:action> </div>
</x-collapsible> </div>

View File

@ -0,0 +1,15 @@
# documentation: https://github.com/Stirling-Tools/Stirling-PDF
# slogan: Stirling is a powerful web based PDF manipulation tool
# tags: pdf, manipulation, web, tool
services:
stirling-pdf:
image: frooodle/s-pdf:latest
volumes:
- stirling-training-data:/usr/share/tesseract-ocr/5/tessdata
- stirling-configs:/configs
- stirling-custom-files:/customFiles/
- stirling-logs:/logs/
environment:
- SERVICE_FQDN_SPDF
- DOCKER_ENABLE_SECURITY=false

View File

@ -469,6 +469,17 @@
"internet" "internet"
] ]
}, },
"stirling-pdf": {
"documentation": "https:\/\/github.com\/Stirling-Tools\/Stirling-PDF",
"slogan": "Stirling is a powerful web based PDF manipulation tool",
"compose": "c2VydmljZXM6CiAgc3RpcmxpbmctcGRmOgogICAgaW1hZ2U6ICdmcm9vb2RsZS9zLXBkZjpsYXRlc3QnCiAgICB2b2x1bWVzOgogICAgICAtICdzdGlybGluZy10cmFpbmluZy1kYXRhOi91c3Ivc2hhcmUvdGVzc2VyYWN0LW9jci81L3Rlc3NkYXRhJwogICAgICAtICdzdGlybGluZy1jb25maWdzOi9jb25maWdzJwogICAgICAtICdzdGlybGluZy1jdXN0b20tZmlsZXM6L2N1c3RvbUZpbGVzLycKICAgICAgLSAnc3RpcmxpbmctbG9nczovbG9ncy8nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fU1BERgogICAgICAtIERPQ0tFUl9FTkFCTEVfU0VDVVJJVFk9ZmFsc2UK",
"tags": [
"pdf",
"manipulation",
"web",
"tool"
]
},
"trigger-with-external-database": { "trigger-with-external-database": {
"documentation": "https:\/\/trigger.dev\/docs\/documentation\/guides\/self-hosting", "documentation": "https:\/\/trigger.dev\/docs\/documentation\/guides\/self-hosting",
"slogan": "The open source Background Jobs framework for TypeScript", "slogan": "The open source Background Jobs framework for TypeScript",

View File

@ -4,7 +4,7 @@
"version": "3.12.36" "version": "3.12.36"
}, },
"v4": { "v4": {
"version": "4.0.0-beta.175" "version": "4.0.0-beta.182"
} }
} }
} }