From c058c0a766df82b680e2e2200ba48cc2357afd1e Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 16 Jan 2024 08:38:39 +0100 Subject: [PATCH 01/22] Update release version to 4.0.0-beta.197 --- README.md | 4 ++++ config/sentry.php | 2 +- config/version.php | 2 +- versions.json | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fe59db3d0..cef3fdc81 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,10 @@ Contact us [here](https://coolify.io/docs/contact). coollabsio%2Fcoolify | Trendshift +# Repo Activity + +![Alt](https://repobeats.axiom.co/api/embed/eab1c8066f9c59d0ad37b76c23ebb5ccac4278ae.svg "Repobeats analytics image") + # Star History [![Star History Chart](https://api.star-history.com/svg?repos=coollabsio/coolify&type=Date)](https://star-history.com/#coollabsio/coolify&Date) diff --git a/config/sentry.php b/config/sentry.php index eedf1c992..f1f17f030 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -7,7 +7,7 @@ return [ // The release version of your application // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) - 'release' => '4.0.0-beta.196', + 'release' => '4.0.0-beta.197', // When left empty or `null` the Laravel environment will be used 'environment' => config('app.env'), diff --git a/config/version.php b/config/version.php index 5aa8bcb7a..32e5b5873 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ Date: Tue, 16 Jan 2024 11:32:56 +0100 Subject: [PATCH 02/22] fix: change proxy view --- app/Livewire/Server/Proxy.php | 1 - resources/views/livewire/server/proxy/show.blade.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/Livewire/Server/Proxy.php b/app/Livewire/Server/Proxy.php index 79ddb203b..5825cf3fb 100644 --- a/app/Livewire/Server/Proxy.php +++ b/app/Livewire/Server/Proxy.php @@ -33,7 +33,6 @@ class Proxy extends Component { $this->server->proxy = null; $this->server->save(); - $this->dispatch('proxyStatusUpdated'); } public function select_proxy($proxy_type) diff --git a/resources/views/livewire/server/proxy/show.blade.php b/resources/views/livewire/server/proxy/show.blade.php index a10113754..5d9523885 100644 --- a/resources/views/livewire/server/proxy/show.blade.php +++ b/resources/views/livewire/server/proxy/show.blade.php @@ -3,7 +3,7 @@
- @if ($server->proxyType() !== 'NONE' && $server->isFunctional()) + @if ($server->isFunctional()) @endif
From d721f4809ad91bb899c098bbffe63f9b863496a6 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 16 Jan 2024 12:20:40 +0100 Subject: [PATCH 03/22] fix ui --- .../project/service/configuration.blade.php | 16 ++++++++-------- .../livewire/project/service/storage.blade.php | 5 ----- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/resources/views/livewire/project/service/configuration.blade.php b/resources/views/livewire/project/service/configuration.blade.php index 62b02ec88..157a4fa16 100644 --- a/resources/views/livewire/project/service/configuration.blade.php +++ b/resources/views/livewire/project/service/configuration.blade.php @@ -7,6 +7,14 @@ @click.prevent="activeTab = 'service-stack'; window.location.hash = 'service-stack'" href="#">Service Stack + Environment + Variables + Storages Logs - Storages Webhooks - Environment - Variables @endif - @if ( - $resource->persistentStorages()->get()->count() == 0 && - $resource->fileStorages()->get()->count() == 0) -
No storages found.
- @endif @endif
From 7a0e415ecf2201d7ced3cadbeed5a51d2125580e Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 16 Jan 2024 12:28:59 +0100 Subject: [PATCH 04/22] fix: checkbox click --- resources/views/components/forms/checkbox.blade.php | 11 ++++++----- resources/views/livewire/project/new/select.blade.php | 2 +- resources/views/livewire/server/form.blade.php | 8 ++++---- resources/views/livewire/server/new/by-ip.blade.php | 10 +++++----- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/resources/views/components/forms/checkbox.blade.php b/resources/views/components/forms/checkbox.blade.php index 98141b044..1265dc132 100644 --- a/resources/views/components/forms/checkbox.blade.php +++ b/resources/views/components/forms/checkbox.blade.php @@ -1,5 +1,5 @@ -
-
here." label="Include Swarm Clusters" />
@endif --}} diff --git a/resources/views/livewire/server/form.blade.php b/resources/views/livewire/server/form.blade.php index 858cfb5da..4ea218aad 100644 --- a/resources/views/livewire/server/form.blade.php +++ b/resources/views/livewire/server/form.blade.php @@ -59,20 +59,20 @@ @endif @if ($server->settings->is_swarm_worker) @else @endif @if ($server->settings->is_swarm_manager) @else @endif @endif diff --git a/resources/views/livewire/server/new/by-ip.blade.php b/resources/views/livewire/server/new/by-ip.blade.php index d1a8f4c15..11b6badf0 100644 --- a/resources/views/livewire/server/new/by-ip.blade.php +++ b/resources/views/livewire/server/new/by-ip.blade.php @@ -26,23 +26,23 @@ @endforeach
-
Swarm support is in alpha version. Read the docs here.
+
Swarm support is in alpha version. Read the docs here.
@if ($is_swarm_worker) @else @endif @if ($is_swarm_manager) @else @endif @if ($is_swarm_worker && count($swarm_managers) > 0) From a42c8da3444d9d4831202f46b0663a0b1e02e411 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 16 Jan 2024 15:19:14 +0100 Subject: [PATCH 05/22] =?UTF-8?q?fix:=20proxy=20ui=20view=20feat:=20build?= =?UTF-8?q?=20server=20=F0=9F=8C=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Actions/Proxy/StartProxy.php | 2 +- app/Console/Kernel.php | 8 +- app/Events/ProxyStatusChanged.php | 34 ++ app/Jobs/ApplicationDeploymentJob.php | 364 ++++++++---------- app/Livewire/ActivityMonitor.php | 32 +- app/Livewire/Project/Application/Advanced.php | 1 + app/Livewire/Project/Application/General.php | 1 - app/Livewire/Project/Application/Heading.php | 6 +- app/Livewire/Project/New/Select.php | 6 +- app/Livewire/Project/Shared/Destination.php | 2 +- app/Livewire/Server/Form.php | 2 + app/Livewire/Server/New/ByIp.php | 14 +- app/Livewire/Server/Proxy/Deploy.php | 24 +- app/Models/Server.php | 11 +- ...4_01_16_115005_add_build_server_enable.php | 28 ++ database/seeders/ServerSettingSeeder.php | 2 +- .../components/applications/navbar.blade.php | 119 ------ .../views/components/server/navbar.blade.php | 20 +- .../project/application/advanced.blade.php | 25 +- .../application/configuration.blade.php | 19 +- .../project/application/general.blade.php | 21 +- .../project/application/heading.blade.php | 122 +++++- .../project/shared/destination.blade.php | 5 +- .../views/livewire/server/form.blade.php | 51 +-- .../views/livewire/server/new/by-ip.blade.php | 12 +- 25 files changed, 522 insertions(+), 409 deletions(-) create mode 100644 app/Events/ProxyStatusChanged.php create mode 100644 database/migrations/2024_01_16_115005_add_build_server_enable.php delete mode 100644 resources/views/components/applications/navbar.blade.php diff --git a/app/Actions/Proxy/StartProxy.php b/app/Actions/Proxy/StartProxy.php index a99e47b25..daee357d5 100644 --- a/app/Actions/Proxy/StartProxy.php +++ b/app/Actions/Proxy/StartProxy.php @@ -2,6 +2,7 @@ namespace App\Actions\Proxy; +use App\Events\ProxyStatusChanged; use App\Models\Server; use Illuminate\Support\Str; use Lorisleiva\Actions\Concerns\AsAction; @@ -13,7 +14,6 @@ class StartProxy public function handle(Server $server, bool $async = true): string|Activity { try { - $proxyType = $server->proxyType(); $commands = collect([]); $proxy_path = get_proxy_path(); diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 7dbead831..ac4626af1 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -60,10 +60,10 @@ class Kernel extends ConsoleKernel $servers = Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4'); $own = Team::find(0)->servers; $servers = $servers->merge($own); - $containerServers = $servers->where('settings.is_swarm_worker', false); + $containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false); } else { $servers = Server::all()->where('ip', '!=', '1.2.3.4'); - $containerServers = $servers->where('settings.is_swarm_worker', false); + $containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false); } foreach ($containerServers as $server) { $schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer(); @@ -111,7 +111,8 @@ class Kernel extends ConsoleKernel } } - private function check_scheduled_tasks($schedule) { + private function check_scheduled_tasks($schedule) + { $scheduled_tasks = ScheduledTask::all(); if ($scheduled_tasks->isEmpty()) { ray('no scheduled tasks'); @@ -134,7 +135,6 @@ class Kernel extends ConsoleKernel task: $scheduled_task ))->cron($scheduled_task->frequency)->onOneServer(); } - } protected function commands(): void diff --git a/app/Events/ProxyStatusChanged.php b/app/Events/ProxyStatusChanged.php new file mode 100644 index 000000000..42d276424 --- /dev/null +++ b/app/Events/ProxyStatusChanged.php @@ -0,0 +1,34 @@ +user()->currentTeam()->id ?? null; + } + if (is_null($teamId)) { + throw new \Exception("Team id is null"); + } + $this->teamId = $teamId; + } + + public function broadcastOn(): array + { + return [ + new PrivateChannel("team.{$this->teamId}"), + ]; + } +} diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 5755b5a57..16ff244c2 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -56,7 +56,13 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted private GithubApp|GitlabApp|string $source = 'other'; private StandaloneDocker|SwarmDocker $destination; + // Deploy to Server private Server $server; + // Build Server + private Server $build_server; + private bool $use_build_server = false; + // Save original server between phases + private Server $original_server; private Server $mainServer; private ?ApplicationPreview $preview = null; private ?string $git_type = null; @@ -196,6 +202,25 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted // Check custom port ['repository' => $this->customRepository, 'port' => $this->customPort] = $this->application->customRepository(); + if (data_get($this->application, 'settings.is_build_server_enabled')) { + $teamId = data_get($this->application, 'environment.project.team.id'); + $buildServers = Server::where('team_id', $teamId)->whereRelation('settings', 'is_build_server', true)->get(); + if ($buildServers->count() === 0) { + $this->application_deployment_queue->addLogEntry("Build server feature activated, but no suitable build server found. Using the deployment server."); + $this->build_server = $this->server; + $this->original_server = $this->server; + } else { + $this->application_deployment_queue->addLogEntry("Build server feature activated and found a suitable build server. Using it to build your application - if needed."); + $this->build_server = $buildServers->random(); + $this->original_server = $this->server; + $this->use_build_server = true; + } + } else { + // Set build server & original_server to the same as deployment server + $this->build_server = $this->server; + $this->original_server = $this->server; + } + ray($this->build_server); try { if ($this->restart_only && $this->application->build_pack !== 'dockerimage') { $this->just_restart(); @@ -225,7 +250,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted if ($this->server->isProxyShouldRun()) { dispatch(new ContainerStatusJob($this->server)); } - if ($this->application->docker_registry_image_name && $this->application->build_pack !== 'dockerimage' && !$this->application->destination->server->isSwarm()) { + // Otherwise built image needs to be pushed before from the build server. + if (!$this->use_build_server) { $this->push_to_docker_registry(); } $this->next(ApplicationDeploymentStatus::FINISHED->value); @@ -234,23 +260,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted $this->fail($e); throw $e; } finally { - if (isset($this->docker_compose_base64)) { - $readme = generate_readme_file($this->application->name, $this->application_deployment_queue->updated_at); - $composeFileName = "$this->configuration_dir/docker-compose.yml"; - if ($this->pull_request_id !== 0) { - $composeFileName = "$this->configuration_dir/docker-compose-pr-{$this->pull_request_id}.yml"; - } - $this->execute_remote_command( - [ - "mkdir -p $this->configuration_dir" - ], - [ - "echo '{$this->docker_compose_base64}' | base64 -d > $composeFileName", - ], - [ - "echo '{$readme}' > $this->configuration_dir/README.md", - ] - ); + if ($this->use_build_server) { + $this->server = $this->build_server; + } else { + $this->write_deployment_configurations(); } $this->execute_remote_command( [ @@ -269,42 +282,72 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted ApplicationStatusChanged::dispatch(data_get($this->application, 'environment.project.team.id')); } } - private function push_to_docker_registry() + private function write_deployment_configurations() { - try { - instant_remote_process(["docker images --format '{{json .}}' {$this->production_image_name}"], $this->server); + if (isset($this->docker_compose_base64)) { + if ($this->use_build_server) { + $this->server = $this->original_server; + } + $readme = generate_readme_file($this->application->name, $this->application_deployment_queue->updated_at); + $composeFileName = "$this->configuration_dir/docker-compose.yml"; + if ($this->pull_request_id !== 0) { + $composeFileName = "$this->configuration_dir/docker-compose-pr-{$this->pull_request_id}.yml"; + } $this->execute_remote_command( [ - "echo '\n----------------------------------------'", + "mkdir -p $this->configuration_dir" ], - ["echo -n 'Pushing image to docker registry ({$this->production_image_name}).'"], [ - executeInDocker($this->deployment_uuid, "docker push {$this->production_image_name}"), 'hidden' => true + "echo '{$this->docker_compose_base64}' | base64 -d > $composeFileName", ], + [ + "echo '{$readme}' > $this->configuration_dir/README.md", + ] ); - if ($this->application->docker_registry_image_tag) { - // Tag image with latest + if ($this->use_build_server) { + $this->server = $this->build_server; + } + } + } + private function push_to_docker_registry($forceFail = false) + { + ray((str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged())); + if ( + $this->application->docker_registry_image_name && + $this->application->build_pack !== 'dockerimage' && + !$this->application->destination->server->isSwarm() && + !$this->restart_only && + !(str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) + ) { + try { + instant_remote_process(["docker images --format '{{json .}}' {$this->production_image_name}"], $this->server); + $this->application_deployment_queue->addLogEntry("----------------------------------------"); + $this->application_deployment_queue->addLogEntry("Pushing image to docker registry ({$this->production_image_name})."); $this->execute_remote_command( - ['echo -n "Tagging and pushing image with latest tag."'], [ - executeInDocker($this->deployment_uuid, "docker tag {$this->production_image_name} {$this->application->docker_registry_image_name}:{$this->application->docker_registry_image_tag}"), 'ignore_errors' => true, 'hidden' => true - ], - [ - executeInDocker($this->deployment_uuid, "docker push {$this->application->docker_registry_image_name}:{$this->application->docker_registry_image_tag}"), 'ignore_errors' => true, 'hidden' => true + executeInDocker($this->deployment_uuid, "docker push {$this->production_image_name}"), 'hidden' => true ], ); + if ($this->application->docker_registry_image_tag) { + // Tag image with latest + $this->application_deployment_queue->addLogEntry("Tagging and pushing image with latest tag."); + $this->execute_remote_command( + [ + executeInDocker($this->deployment_uuid, "docker tag {$this->production_image_name} {$this->application->docker_registry_image_name}:{$this->application->docker_registry_image_tag}"), 'ignore_errors' => true, 'hidden' => true + ], + [ + executeInDocker($this->deployment_uuid, "docker push {$this->application->docker_registry_image_name}:{$this->application->docker_registry_image_tag}"), 'ignore_errors' => true, 'hidden' => true + ], + ); + } + $this->application_deployment_queue->addLogEntry("Image pushed to docker registry.'"); + } catch (Exception $e) { + $this->application_deployment_queue->addLogEntry("Failed to push image to docker registry. Please check debug logs for more information.'"); + if ($forceFail) { + throw $e; + } + ray($e); } - $this->execute_remote_command([ - "echo -n 'Image pushed to docker registry.'" - ]); - } catch (Exception $e) { - if ($this->application->destination->server->isSwarm()) { - throw $e; - } - $this->execute_remote_command( - ["echo -n 'Failed to push image to docker registry. Please check debug logs for more information.'"], - ); - ray($e); } } private function generate_image_names() @@ -340,20 +383,14 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted } private function just_restart() { - $this->execute_remote_command( - [ - "echo 'Starting deployment of {$this->customRepository}:{$this->application->git_branch}.'" - ], - ); + $this->application_deployment_queue->addLogEntry("Starting deployment of {$this->customRepository}:{$this->application->git_branch}.'"); $this->prepare_builder_image(); $this->check_git_if_build_needed(); $this->set_base_dir(); $this->generate_image_names(); $this->check_image_locally_or_remotely(); if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty()) { - $this->execute_remote_command([ - "echo 'Image found ({$this->production_image_name}) with the same Git Commit SHA. Restarting container.'", - ]); + $this->application_deployment_queue->addLogEntry("Image found ({$this->production_image_name}) with the same Git Commit SHA. Restarting container."); $this->create_workdir(); $this->generate_compose_file(); $this->rolling_update(); @@ -397,16 +434,15 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted private function deploy_simple_dockerfile() { + if ($this->use_build_server) { + $this->server = $this->build_server; + } $dockerfile_base64 = base64_encode($this->application->dockerfile); - $this->execute_remote_command( - [ - "echo 'Starting deployment of {$this->application->name}.'" - ], - ); + $this->application_deployment_queue->addLogEntry("Starting deployment of {$this->application->name}."); $this->prepare_builder_image(); $this->execute_remote_command( [ - executeInDocker($this->deployment_uuid, "echo '$dockerfile_base64' | base64 -d > $this->workdir$this->dockerfile_location") + executeInDocker($this->deployment_uuid, "echo '$dockerfile_base64' | base64 -d > {$this->workdir}{$this->dockerfile_location}") ], ); $this->generate_image_names(); @@ -422,11 +458,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted $this->dockerImage = $this->application->docker_registry_image_name; $this->dockerImageTag = $this->application->docker_registry_image_tag; ray("echo 'Starting deployment of {$this->dockerImage}:{$this->dockerImageTag}.'"); - $this->execute_remote_command( - [ - "echo 'Starting deployment of {$this->dockerImage}:{$this->dockerImageTag}.'" - ], - ); + $this->application_deployment_queue->addLogEntry("Starting deployment of {$this->dockerImage}:{$this->dockerImageTag}."); $this->generate_image_names(); $this->prepare_builder_image(); $this->generate_compose_file(); @@ -496,24 +528,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted "docker network connect {$networkId} coolify-proxy || true", "hidden" => true, "ignore_errors" => true ]); } - if (isset($this->docker_compose_base64)) { - $readme = generate_readme_file($this->application->name, $this->application_deployment_queue->updated_at); - $composeFileName = "$this->configuration_dir/docker-compose.yml"; - if ($this->pull_request_id !== 0) { - $composeFileName = "$this->configuration_dir/docker-compose-pr-{$this->pull_request_id}.yml"; - } - $this->execute_remote_command( - [ - "mkdir -p $this->configuration_dir" - ], - [ - "echo '{$this->docker_compose_base64}' | base64 -d > $composeFileName", - ], - [ - "echo '{$readme}' > $this->configuration_dir/README.md", - ] - ); - } + $this->write_deployment_configurations(); // Start compose file if ($this->docker_compose_custom_start_command) { $this->execute_remote_command( @@ -528,14 +543,13 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted } private function deploy_dockerfile_buildpack() { + if ($this->use_build_server) { + $this->server = $this->build_server; + } if (data_get($this->application, 'dockerfile_location')) { $this->dockerfile_location = $this->application->dockerfile_location; } - $this->execute_remote_command( - [ - "echo 'Starting deployment of {$this->customRepository}:{$this->application->git_branch}.'" - ], - ); + $this->application_deployment_queue->addLogEntry("Starting deployment of {$this->customRepository}:{$this->application->git_branch}."); $this->prepare_builder_image(); $this->check_git_if_build_needed(); $this->clone_repository(); @@ -555,11 +569,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted } private function deploy_nixpacks_buildpack() { - $this->execute_remote_command( - [ - "echo 'Starting deployment of {$this->customRepository}:{$this->application->git_branch}.'" - ], - ); + if ($this->use_build_server) { + $this->server = $this->build_server; + } + $this->application_deployment_queue->addLogEntry("Starting deployment of {$this->customRepository}:{$this->application->git_branch}."); $this->prepare_builder_image(); $this->check_git_if_build_needed(); $this->set_base_dir(); @@ -568,17 +581,13 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted $this->check_image_locally_or_remotely(); if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) { $this->create_workdir(); - $this->execute_remote_command([ - "echo 'No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.'", - ]); + $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped."); $this->generate_compose_file(); $this->rolling_update(); return; } if ($this->application->isConfigurationChanged()) { - $this->execute_remote_command([ - "echo 'Configuration changed. Rebuilding image.'", - ]); + $this->application_deployment_queue->addLogEntry("Configuration changed. Rebuilding image."); } } $this->clone_repository(); @@ -592,11 +601,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted } private function deploy_static_buildpack() { - $this->execute_remote_command( - [ - "echo 'Starting deployment of {$this->customRepository}:{$this->application->git_branch}.'" - ], - ); + if ($this->use_build_server) { + $this->server = $this->build_server; + } + $this->application_deployment_queue->addLogEntry("Starting deployment of {$this->customRepository}:{$this->application->git_branch}."); $this->prepare_builder_image(); $this->check_git_if_build_needed(); $this->set_base_dir(); @@ -619,18 +627,14 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted $nixpacks_php_root_dir = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first(); } if ($nixpacks_php_fallback_path?->value === '/index.php' && $nixpacks_php_root_dir?->value === '/app/public' && $this->newVersionIsHealthy === false) { - $this->execute_remote_command( - [ - "echo 'There was a change in how Laravel is deployed. Please update your environment variables to match the new deployment method. More details here: https://coolify.io/docs/frameworks/laravel#requirements'", 'type' => 'err' - ], - ); + $this->application_deployment_queue->addLogEntry("There was a change in how Laravel is deployed. Please update your environment variables to match the new deployment method. More details here: https://coolify.io/docs/frameworks/laravel#requirements", 'stderr'); } } private function rolling_update() { if ($this->server->isSwarm()) { if ($this->build_pack !== 'dockerimage') { - $this->push_to_docker_registry(); + $this->push_to_docker_registry(forceFail: true); } $this->application_deployment_queue->addLogEntry("Rolling update started."); $this->execute_remote_command( @@ -640,22 +644,19 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted ); $this->application_deployment_queue->addLogEntry("Rolling update completed."); } else { + if ($this->use_build_server) { + $this->push_to_docker_registry(forceFail: true); + $this->write_deployment_configurations(); + $this->server = $this->original_server; + } if (count($this->application->ports_mappings_array) > 0) { - $this->execute_remote_command( - [ - "echo '\n----------------------------------------'", - ], - ["echo -n 'Application has ports mapped to the host system, rolling update is not supported.'"], - ); + $this->application_deployment_queue->addLogEntry("----------------------------------------"); + $this->application_deployment_queue->addLogEntry("Application has ports mapped to the host system, rolling update is not supported."); $this->stop_running_container(force: true); $this->start_by_compose_file(); } else { - $this->execute_remote_command( - [ - "echo '\n----------------------------------------'", - ], - ["echo -n 'Rolling update started.'"], - ); + $this->application_deployment_queue->addLogEntry("----------------------------------------"); + $this->application_deployment_queue->addLogEntry("Rolling update started."); $this->start_by_compose_file(); $this->health_check(); $this->stop_running_container(); @@ -676,17 +677,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted // ray('New container name: ', $this->container_name); if ($this->container_name) { $counter = 1; - $this->execute_remote_command( - [ - "echo 'Waiting for healthcheck to pass on the new container.'" - ] - ); + $this->application_deployment_queue->addLogEntry("Waiting for healthcheck to pass on the new container."); if ($this->full_healthcheck_url) { - $this->execute_remote_command( - [ - "echo 'Healthcheck URL (inside the container): {$this->full_healthcheck_url}'" - ] - ); + $this->application_deployment_queue->addLogEntry("Healthcheck URL (inside the container): {$this->full_healthcheck_url}"); } while ($counter < $this->application->health_check_retries) { $this->execute_remote_command( @@ -698,19 +691,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted ], ); - $this->execute_remote_command( - [ - "echo 'Attempt {$counter} of {$this->application->health_check_retries} | Healthcheck status: {$this->saved_outputs->get('health_check')}'" - ], - ); + $this->application_deployment_queue->addLogEntry("Attempt {$counter} of {$this->application->health_check_retries} | Healthcheck status: {$this->saved_outputs->get('health_check')}"); if (Str::of($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'healthy') { $this->newVersionIsHealthy = true; $this->application->update(['status' => 'running']); - $this->execute_remote_command( - [ - "echo 'New container is healthy.'" - ], - ); + $this->application_deployment_queue->addLogEntry("New container is healthy."); break; } if (Str::of($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'unhealthy') { @@ -725,11 +710,12 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted } private function deploy_pull_request() { + if ($this->use_build_server) { + $this->server = $this->build_server; + } $this->newVersionIsHealthy = true; $this->generate_image_names(); - $this->execute_remote_command([ - "echo 'Starting pull request (#{$this->pull_request_id}) deployment of {$this->customRepository}:{$this->application->git_branch}.'", - ]); + $this->application_deployment_queue->addLogEntry("Starting pull request (#{$this->pull_request_id}) deployment of {$this->customRepository}:{$this->application->git_branch}."); $this->prepare_builder_image(); $this->clone_repository(); $this->set_base_dir(); @@ -754,10 +740,16 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted ], ); } else { - $this->execute_remote_command( - ["echo -n 'Starting preview deployment.'"], - [executeInDocker($this->deployment_uuid, "SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->workdir} up -d"), "hidden" => true], - ); + $this->application_deployment_queue->addLogEntry("Starting preview deployment."); + if ($this->use_build_server) { + $this->execute_remote_command( + ["SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->configuration_dir} -f {$this->configuration_dir}{$this->docker_compose_location} up --build -d", "hidden" => true], + ); + } else { + $this->execute_remote_command( + [executeInDocker($this->deployment_uuid, "SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up --build -d"), "hidden" => true], + ); + } } } private function create_workdir() @@ -774,16 +766,20 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted // Get user home directory $this->serverUserHomeDir = instant_remote_process(["echo \$HOME"], $this->server); $this->dockerConfigFileExists = instant_remote_process(["test -f {$this->serverUserHomeDir}/.docker/config.json && echo 'OK' || echo 'NOK'"], $this->server); - - if ($this->dockerConfigFileExists === 'OK') { - $runCommand = "docker run -d --network {$this->destination->network} --name {$this->deployment_uuid} --rm -v {$this->serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}"; + if ($this->use_build_server) { + if ($this->dockerConfigFileExists === 'NOK') { + throw new RuntimeException('Docker config file (~/.docker/config.json) not found on the build server. Please run "docker login" to login to the docker registry on the server.'); + } + $runCommand = "docker run -d --name {$this->deployment_uuid} --rm -v {$this->serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}"; } else { - $runCommand = "docker run -d --network {$this->destination->network} --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}"; + if ($this->dockerConfigFileExists === 'OK') { + $runCommand = "docker run -d --network {$this->destination->network} --name {$this->deployment_uuid} --rm -v {$this->serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}"; + } else { + $runCommand = "docker run -d --network {$this->destination->network} --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}"; + } } + $this->application_deployment_queue->addLogEntry("Preparing container with helper image: $helperImage."); $this->execute_remote_command( - [ - "echo -n 'Preparing container with helper image: $helperImage.'", - ], [ $runCommand, "hidden" => true, @@ -801,19 +797,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted $destination = StandaloneDocker::find($destination_id); $server = $destination->server; if ($server->team_id !== $this->mainServer->team_id) { - $this->execute_remote_command( - [ - "echo -n 'Skipping deployment to {$server->name}. Not in the same team?!'", - ], - ); + $this->application_deployment_queue->addLogEntry("Skipping deployment to {$server->name}. Not in the same team?!"); continue; } $this->server = $server; - $this->execute_remote_command( - [ - "echo -n 'Deploying to {$this->server->name}.'", - ], - ); + $this->application_deployment_queue->addLogEntry("Deploying to {$this->server->name}."); $this->prepare_builder_image(); $this->generate_image_names(); $this->rolling_update(); @@ -821,11 +809,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted } private function set_base_dir() { - $this->execute_remote_command( - [ - "echo -n 'Setting base directory to {$this->workdir}.'" - ], - ); + $this->application_deployment_queue->addLogEntry("Setting base directory to {$this->workdir}."); } private function check_git_if_build_needed() { @@ -898,10 +882,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted private function generate_nixpacks_confs() { $nixpacks_command = $this->nixpacks_build_cmd(); + $this->application_deployment_queue->addLogEntry("Generating nixpacks configuration with: $nixpacks_command"); $this->execute_remote_command( - [ - "echo -n 'Generating nixpacks configuration with: $nixpacks_command'", - ], [executeInDocker($this->deployment_uuid, $nixpacks_command), "save" => "nixpacks_plan", "hidden" => true], [executeInDocker($this->deployment_uuid, "nixpacks detect {$this->workdir}"), "save" => "nixpacks_type", "hidden" => true], ); @@ -911,13 +893,14 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted if ($this->saved_outputs->get('nixpacks_plan')) { $this->nixpacks_plan = $this->saved_outputs->get('nixpacks_plan'); if ($this->nixpacks_plan) { + $this->application_deployment_queue->addLogEntry("Found application type: {$this->nixpacks_type}."); + $this->application_deployment_queue->addLogEntry("If you need further customization, please check the documentation of Nixpacks: https://nixpacks.com/docs/providers/{$this->nixpacks_type}"); $parsed = Toml::Parse($this->nixpacks_plan); // Do any modifications here $this->generate_env_variables(); $merged_envs = $this->env_args->merge(collect(data_get($parsed, 'variables', []))); data_set($parsed, 'variables', $merged_envs->toArray()); $this->nixpacks_plan = json_encode($parsed, JSON_PRETTY_PRINT); - ray($this->nixpacks_plan); } } } @@ -1234,9 +1217,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted } private function pull_latest_image($image) { + $this->application_deployment_queue->addLogEntry("Pulling latest image ($image) from the registry."); $this->execute_remote_command( - ["echo -n 'Pulling latest image ($image) from the registry.'"], - [ executeInDocker($this->deployment_uuid, "docker pull {$image}"), "hidden" => true ] @@ -1244,25 +1226,18 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted } private function build_image() { + $this->application_deployment_queue->addLogEntry("----------------------------------------"); if ($this->application->build_pack === 'static') { - $this->execute_remote_command([ - "echo -n 'Static deployment. Copying static assets to the image.'", - ]); + $this->application_deployment_queue->addLogEntry("Static deployment. Copying static assets to the image."); } else { - $this->execute_remote_command( - [ - "echo -n 'Building docker image started.'", - ], - ["echo -n 'To check the current progress, click on Show Debug Logs.'"] - ); + $this->application_deployment_queue->addLogEntry("Building docker image started."); + $this->application_deployment_queue->addLogEntry("To check the current progress, click on Show Debug Logs."); } if ($this->application->settings->is_static || $this->application->build_pack === 'static') { if ($this->application->static_image) { $this->pull_latest_image($this->application->static_image); - $this->execute_remote_command( - ["echo -n 'Continue with the building process.'"], - ); + $this->application_deployment_queue->addLogEntry("Continuing with the building process."); } if ($this->application->build_pack === 'static') { $dockerfile = base64_encode("FROM {$this->application->static_image} @@ -1405,9 +1380,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); } } } - $this->execute_remote_command([ - "echo -n 'Building docker image completed.'", - ]); + $this->application_deployment_queue->addLogEntry("Building docker image completed."); } private function stop_running_container(bool $force = false) @@ -1463,13 +1436,13 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); [executeInDocker($this->deployment_uuid, "SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true], ); } else { - if ($this->docker_compose_location) { + if ($this->use_build_server) { $this->execute_remote_command( - [executeInDocker($this->deployment_uuid, "SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up --build -d"), "hidden" => true], + ["SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->configuration_dir} -f {$this->configuration_dir}{$this->docker_compose_location} up --build -d", "hidden" => true], ); } else { $this->execute_remote_command( - [executeInDocker($this->deployment_uuid, "SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true], + [executeInDocker($this->deployment_uuid, "SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up --build -d"), "hidden" => true], ); } } @@ -1535,13 +1508,12 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); public function failed(Throwable $exception): void { - $this->execute_remote_command( - ["echo 'Oops something is not okay, are you okay? 😢'", 'type' => 'err'], - ["echo '{$exception->getMessage()}'", 'type' => 'err'], - ); + $this->application_deployment_queue->addLogEntry("Oops something is not okay, are you okay? 😢", 'stderr'); + $this->application_deployment_queue->addLogEntry($exception->getMessage(), 'stderr'); + if ($this->application->build_pack !== 'dockercompose') { + $this->application_deployment_queue->addLogEntry("Deployment failed. Removing the new version of your application.", 'stderr'); $this->execute_remote_command( - ["echo -n 'Deployment failed. Removing the new version of your application.'", 'type' => 'err'], [executeInDocker($this->deployment_uuid, "docker rm -f $this->container_name >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true] ); } diff --git a/app/Livewire/ActivityMonitor.php b/app/Livewire/ActivityMonitor.php index 703899b65..8f887b3c5 100644 --- a/app/Livewire/ActivityMonitor.php +++ b/app/Livewire/ActivityMonitor.php @@ -3,6 +3,7 @@ namespace App\Livewire; use App\Enums\ProcessStatus; +use App\Models\User; use Livewire\Component; use Spatie\Activitylog\Models\Activity; @@ -10,14 +11,16 @@ class ActivityMonitor extends Component { public ?string $header = null; public $activityId; + public $eventToDispatch = 'activityFinished'; public $isPollingActive = false; protected $activity; protected $listeners = ['newMonitorActivity']; - public function newMonitorActivity($activityId) + public function newMonitorActivity($activityId, $eventToDispatch = 'activityFinished') { $this->activityId = $activityId; + $this->eventToDispatch = $eventToDispatch; $this->hydrateActivity(); @@ -35,13 +38,28 @@ class ActivityMonitor extends Component // $this->setStatus(ProcessStatus::IN_PROGRESS); $exit_code = data_get($this->activity, 'properties.exitCode'); if ($exit_code !== null) { - if ($exit_code === 0) { - // $this->setStatus(ProcessStatus::FINISHED); - } else { - // $this->setStatus(ProcessStatus::ERROR); - } + // if ($exit_code === 0) { + // // $this->setStatus(ProcessStatus::FINISHED); + // } else { + // // $this->setStatus(ProcessStatus::ERROR); + // } $this->isPollingActive = false; - $this->dispatch('activityFinished'); + if ($exit_code === 0) { + if ($this->eventToDispatch !== null) { + if (str($this->eventToDispatch)->startsWith('App\\Events\\')) { + $causer_id = data_get($this->activity, 'causer_id'); + $user = User::find($causer_id); + if ($user) { + foreach($user->teams as $team) { + $teamId = $team->id; + $this->eventToDispatch::dispatch($teamId); + } + } + return; + } + $this->dispatch($this->eventToDispatch); + } + } } } diff --git a/app/Livewire/Project/Application/Advanced.php b/app/Livewire/Project/Application/Advanced.php index 9ad485f85..a2b7afa6a 100644 --- a/app/Livewire/Project/Application/Advanced.php +++ b/app/Livewire/Project/Application/Advanced.php @@ -16,6 +16,7 @@ class Advanced extends Component 'application.settings.is_force_https_enabled' => 'boolean|required', 'application.settings.is_log_drain_enabled' => 'boolean|required', 'application.settings.is_gpu_enabled' => 'boolean|required', + 'application.settings.is_build_server_enabled' => 'boolean|required', 'application.settings.gpu_driver' => 'string|required', 'application.settings.gpu_count' => 'string|required', 'application.settings.gpu_device_ids' => 'string|required', diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php index 0e93d389c..21608494a 100644 --- a/app/Livewire/Project/Application/General.php +++ b/app/Livewire/Project/Application/General.php @@ -227,7 +227,6 @@ class General extends Component if ($this->ports_exposes !== $this->application->ports_exposes) { $this->resetDefaultLabels(false); } - if (data_get($this->application, 'build_pack') === 'dockerimage') { $this->validate([ 'application.docker_registry_image_name' => 'required', diff --git a/app/Livewire/Project/Application/Heading.php b/app/Livewire/Project/Application/Heading.php index 4a138eaca..2dba750c8 100644 --- a/app/Livewire/Project/Application/Heading.php +++ b/app/Livewire/Project/Application/Heading.php @@ -74,7 +74,11 @@ class Heading extends Component return; } if ($this->application->destination->server->isSwarm() && is_null($this->application->docker_registry_image_name)) { - $this->dispatch('error', 'Please set a Docker image name first.'); + $this->dispatch('error', 'To deploy to a Swarm cluster you must set a Docker image name first.'); + return; + } + if (data_get($this->application, 'settings.is_build_server_enabled') && is_null($this->application->docker_registry_image_name)) { + $this->dispatch('error', 'To use a build server you must set a Docker image name first.
More information here: documentation'); return; } $this->setDeploymentUuid(); diff --git a/app/Livewire/Project/New/Select.php b/app/Livewire/Project/New/Select.php index 3133efc97..1c49ac7ad 100644 --- a/app/Livewire/Project/New/Select.php +++ b/app/Livewire/Project/New/Select.php @@ -104,7 +104,7 @@ class Select extends Component if ($this->includeSwarm) { $this->servers = $this->allServers; } else { - $this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false); + $this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false)->where('settings.is_build_server', false); } } public function setType(string $type) @@ -120,13 +120,13 @@ class Select extends Component case 'mongodb': $this->isDatabase = true; $this->includeSwarm = false; - $this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false); + $this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false)->where('settings.is_build_server', false); break; } if (str($type)->startsWith('one-click-service') || str($type)->startsWith('docker-compose-empty')) { $this->isDatabase = true; $this->includeSwarm = false; - $this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false); + $this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false)->where('settings.is_build_server', false); } if ($type === "existing-postgresql") { $this->current_step = $type; diff --git a/app/Livewire/Project/Shared/Destination.php b/app/Livewire/Project/Shared/Destination.php index e6d2d5a33..5c99630ee 100644 --- a/app/Livewire/Project/Shared/Destination.php +++ b/app/Livewire/Project/Shared/Destination.php @@ -8,5 +8,5 @@ class Destination extends Component { public $resource; public $servers = []; - public $additionalServers = []; + public $additional_servers = []; } diff --git a/app/Livewire/Server/Form.php b/app/Livewire/Server/Form.php index d832c83d8..82c5fb683 100644 --- a/app/Livewire/Server/Form.php +++ b/app/Livewire/Server/Form.php @@ -26,6 +26,7 @@ class Form extends Component 'server.settings.is_reachable' => 'required', 'server.settings.is_swarm_manager' => 'required|boolean', 'server.settings.is_swarm_worker' => 'required|boolean', + 'server.settings.is_build_server' => 'required|boolean', 'wildcard_domain' => 'nullable|url', ]; protected $validationAttributes = [ @@ -38,6 +39,7 @@ class Form extends Component 'server.settings.is_reachable' => 'Is reachable', 'server.settings.is_swarm_manager' => 'Swarm Manager', 'server.settings.is_swarm_worker' => 'Swarm Worker', + 'server.settings.is_build_server' => 'Build Server', ]; public function mount() diff --git a/app/Livewire/Server/New/ByIp.php b/app/Livewire/Server/New/ByIp.php index 5238931a8..cd0166c54 100644 --- a/app/Livewire/Server/New/ByIp.php +++ b/app/Livewire/Server/New/ByIp.php @@ -25,6 +25,8 @@ class ByIp extends Component public bool $is_swarm_worker = false; public $selected_swarm_cluster = null; + public bool $is_build_server = false; + public $swarm_managers = []; protected $rules = [ 'name' => 'required|string', @@ -34,6 +36,7 @@ class ByIp extends Component 'port' => 'required|integer', 'is_swarm_manager' => 'required|boolean', 'is_swarm_worker' => 'required|boolean', + 'is_build_server' => 'required|boolean', ]; protected $validationAttributes = [ 'name' => 'Name', @@ -43,6 +46,7 @@ class ByIp extends Component 'port' => 'Port', 'is_swarm_manager' => 'Swarm Manager', 'is_swarm_worker' => 'Swarm Worker', + 'is_build_server' => 'Build Server', ]; public function mount() @@ -89,8 +93,14 @@ class ByIp extends Component $payload['swarm_cluster'] = $this->selected_swarm_cluster; } $server = Server::create($payload); - $server->settings->is_swarm_manager = $this->is_swarm_manager; - $server->settings->is_swarm_worker = $this->is_swarm_worker; + if ($this->is_build_server) { + $this->is_swarm_manager = false; + $this->is_swarm_worker = false; + } else { + $server->settings->is_swarm_manager = $this->is_swarm_manager; + $server->settings->is_swarm_worker = $this->is_swarm_worker; + } + $server->settings->is_build_server = $this->is_build_server; $server->settings->save(); $server->addInitialNetwork(); return redirect()->route('server.show', $server->uuid); diff --git a/app/Livewire/Server/Proxy/Deploy.php b/app/Livewire/Server/Proxy/Deploy.php index 4c748b7fa..0dd314cd6 100644 --- a/app/Livewire/Server/Proxy/Deploy.php +++ b/app/Livewire/Server/Proxy/Deploy.php @@ -4,6 +4,7 @@ namespace App\Livewire\Server\Proxy; use App\Actions\Proxy\CheckProxy; use App\Actions\Proxy\StartProxy; +use App\Events\ProxyStatusChanged; use App\Models\Server; use Livewire\Component; @@ -14,7 +15,17 @@ class Deploy extends Component public ?string $currentRoute = null; public ?string $serverIp = null; - protected $listeners = ['proxyStatusUpdated', 'traefikDashboardAvailable', 'serverRefresh' => 'proxyStatusUpdated', "checkProxy", "startProxy"]; + public function getListeners() + { + $teamId = auth()->user()->currentTeam()->id; + return [ + "echo-private:team.{$teamId},ProxyStatusChanged" => 'proxyStarted', + 'proxyStatusUpdated', + 'traefikDashboardAvailable', + 'serverRefresh' => 'proxyStatusUpdated', + "checkProxy", "startProxy" + ]; + } public function mount() { @@ -29,13 +40,15 @@ class Deploy extends Component { $this->traefikDashboardAvailable = $data; } + public function proxyStarted() + { + CheckProxy::run($this->server, true); + $this->dispatch('success', 'Proxy started.'); + } public function proxyStatusUpdated() { $this->server->refresh(); } - public function ip() - { - } public function checkProxy() { try { @@ -50,7 +63,7 @@ class Deploy extends Component { try { $activity = StartProxy::run($this->server); - $this->dispatch('newMonitorActivity', $activity->id); + $this->dispatch('newMonitorActivity', $activity->id, ProxyStatusChanged::class); } catch (\Throwable $e) { return handleError($e, $this); } @@ -77,6 +90,5 @@ class Deploy extends Component } catch (\Throwable $e) { return handleError($e, $this); } - } } diff --git a/app/Models/Server.php b/app/Models/Server.php index 65b992b5a..a29293a02 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -71,7 +71,7 @@ class Server extends BaseModel static public function isUsable() { - return Server::ownedByCurrentTeam()->whereRelation('settings', 'is_reachable', true)->whereRelation('settings', 'is_usable', true)->whereRelation('settings', 'is_swarm_worker', false); + return Server::ownedByCurrentTeam()->whereRelation('settings', 'is_reachable', true)->whereRelation('settings', 'is_usable', true)->whereRelation('settings', 'is_swarm_worker', false)->whereRelation('settings', 'is_build_server', false); } static public function destinationsByServer(string $server_id) @@ -328,7 +328,7 @@ class Server extends BaseModel } public function isProxyShouldRun() { - if ($this->proxyType() === ProxyTypes::NONE->value) { + if ($this->proxyType() === ProxyTypes::NONE->value || $this->settings->is_build_server) { return false; } // foreach ($this->applications() as $application) { @@ -436,7 +436,7 @@ class Server extends BaseModel } $this->settings->is_usable = true; $this->settings->save(); - $this->validateCoolifyNetwork(isSwarm: false); + $this->validateCoolifyNetwork(isSwarm: false, isBuildServer: $this->settings->is_build_server); return true; } public function validateDockerSwarm() @@ -466,8 +466,11 @@ class Server extends BaseModel $this->settings->save(); return true; } - public function validateCoolifyNetwork($isSwarm = false) + public function validateCoolifyNetwork($isSwarm = false, $isBuildServer = false) { + if ($isBuildServer) { + return; + } if ($isSwarm) { return instant_remote_process(["docker network create --attachable --driver overlay coolify-overlay >/dev/null 2>&1 || true"], $this, false); } else { diff --git a/database/migrations/2024_01_16_115005_add_build_server_enable.php b/database/migrations/2024_01_16_115005_add_build_server_enable.php new file mode 100644 index 000000000..771c0e4ee --- /dev/null +++ b/database/migrations/2024_01_16_115005_add_build_server_enable.php @@ -0,0 +1,28 @@ +boolean('is_build_server_enabled')->default(false); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('application_settings', function (Blueprint $table) { + $table->dropColumn('is_build_server_enabled'); + }); + } +}; diff --git a/database/seeders/ServerSettingSeeder.php b/database/seeders/ServerSettingSeeder.php index 6d8c68188..af4a2694b 100644 --- a/database/seeders/ServerSettingSeeder.php +++ b/database/seeders/ServerSettingSeeder.php @@ -14,7 +14,7 @@ class ServerSettingSeeder extends Seeder { $server_2 = Server::find(0)->load(['settings']); $server_2->settings->wildcard_domain = 'http://127.0.0.1.sslip.io'; - $server_2->settings->is_build_server = true; + $server_2->settings->is_build_server = false; $server_2->settings->is_usable = true; $server_2->settings->is_reachable = true; $server_2->settings->save(); diff --git a/resources/views/components/applications/navbar.blade.php b/resources/views/components/applications/navbar.blade.php deleted file mode 100644 index db69feb6b..000000000 --- a/resources/views/components/applications/navbar.blade.php +++ /dev/null @@ -1,119 +0,0 @@ - diff --git a/resources/views/components/server/navbar.blade.php b/resources/views/components/server/navbar.blade.php index 3eac82ab9..a6a2dd74a 100644 --- a/resources/views/components/server/navbar.blade.php +++ b/resources/views/components/server/navbar.blade.php @@ -2,38 +2,42 @@

Server

- @if ($server->proxyType() !== 'NONE' && $server->isFunctional() && !$server->isSwarmWorker()) + @if ( + $server->proxyType() !== 'NONE' && + $server->isFunctional() && + !$server->isSwarmWorker() && + !$server->settings->is_build_server) @endif
{{ data_get($server, 'name') }}
diff --git a/resources/views/livewire/project/application/advanced.blade.php b/resources/views/livewire/project/application/advanced.blade.php index f30f40974..1805f77a5 100644 --- a/resources/views/livewire/project/application/advanced.blade.php +++ b/resources/views/livewire/project/application/advanced.blade.php @@ -4,32 +4,41 @@

Advanced

Advanced configuration for your application.
-
- @if (!$application->settings->is_raw_compose_deployment_enabled) - - @endif +
+

General

+ helper="Use a build server to build your application. You can configure your build server in the Server settings." + instantSave id="application.settings.is_build_server_enabled" label="Use a Build Server?" /> @if ($application->git_based()) + @endif + +

Logs

+ @if (!$application->settings->is_raw_compose_deployment_enabled) + + @endif +

Git

+ @if ($application->git_based()) @endif +

GPU

@if ($application->build_pack !== 'dockercompose')
+ instantSave id="application.settings.is_gpu_enabled" label="Attach GPU" /> @if ($application->settings->is_gpu_enabled) Save @endif diff --git a/resources/views/livewire/project/application/configuration.blade.php b/resources/views/livewire/project/application/configuration.blade.php index dae96831d..3a43a7769 100644 --- a/resources/views/livewire/project/application/configuration.blade.php +++ b/resources/views/livewire/project/application/configuration.blade.php @@ -18,9 +18,11 @@ href="#">Environment Variables @endif - Scheduled Tasks - + @if ($application->build_pack !== 'static' && $application->build_pack !== 'dockercompose') + Storages + + @endif @if ($application->git_based()) Source @@ -28,11 +30,12 @@ Server - @if ($application->build_pack !== 'static' && $application->build_pack !== 'dockercompose') - Storages - - @endif + + Scheduled Tasks + + Webhooks diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php index 6df481e71..87c8fe619 100644 --- a/resources/views/livewire/project/application/general.blade.php +++ b/resources/views/livewire/project/application/general.blade.php @@ -64,25 +64,25 @@
@endif @if ($application->build_pack !== 'dockercompose') -
- - Generate Domain - -
- @endif +
+ + Generate Domain + +
+ @endif @if ($application->build_pack !== 'dockercompose')

Docker Registry

@if ($application->destination->server->isSwarm()) @if ($application->build_pack !== 'dockerimage')
Docker Swarm requires the image to be available in a registry. More info here.
@endif @else @if ($application->build_pack !== 'dockerimage')
Push the built image to a docker registry. More info here.
+ href="https://coolify.io/docs/docker/registry" target="_blank">here.
@endif @endif
@@ -190,7 +190,8 @@ Reload Compose File @if ($application->settings->is_raw_compose_deployment_enabled) + label="Docker Compose Content (applicationId: {{ $application->id }})" + helper="You need to modify the docker compose file." /> @else diff --git a/resources/views/livewire/project/application/heading.blade.php b/resources/views/livewire/project/application/heading.blade.php index 2b9f189c2..aea5bb8b8 100644 --- a/resources/views/livewire/project/application/heading.blade.php +++ b/resources/views/livewire/project/application/heading.blade.php @@ -1,4 +1,124 @@ diff --git a/resources/views/livewire/project/shared/destination.blade.php b/resources/views/livewire/project/shared/destination.blade.php index f06e806e0..469439254 100644 --- a/resources/views/livewire/project/shared/destination.blade.php +++ b/resources/views/livewire/project/shared/destination.blade.php @@ -1,7 +1,8 @@

Server

-
The destination server where your application will be deployed to.
-
+
Server related configurations.
+

Destination Server & Network

+
On server {{ data_get($resource, 'destination.server.name') }} diff --git a/resources/views/livewire/server/form.blade.php b/resources/views/livewire/server/form.blade.php index 4ea218aad..deb5998a4 100644 --- a/resources/views/livewire/server/form.blade.php +++ b/resources/views/livewire/server/form.blade.php @@ -35,7 +35,7 @@
- @if (!$server->settings->is_swarm_worker) + @if (!$server->settings->is_swarm_worker && !$server->settings->is_build_server) @endif @@ -51,29 +51,34 @@
@if (!$server->isLocalhost()) - - @if ($server->isSwarm()) -
Swarm support is in alpha version.
- @endif - @if ($server->settings->is_swarm_worker) - + @if ($server->settings->is_build_server) + @else - - @endif - @if ($server->settings->is_swarm_manager) - - @else - + + @if ($server->isSwarm()) +
Swarm support is in alpha version.
+ @endif + @if ($server->settings->is_swarm_worker) + + @else + + @endif + @if ($server->settings->is_swarm_manager) + + @else + + @endif @endif @endif
diff --git a/resources/views/livewire/server/new/by-ip.blade.php b/resources/views/livewire/server/new/by-ip.blade.php index 11b6badf0..4629fbfd3 100644 --- a/resources/views/livewire/server/new/by-ip.blade.php +++ b/resources/views/livewire/server/new/by-ip.blade.php @@ -26,8 +26,14 @@ @endforeach
+
+

Swarm Support

+
Swarm support is in alpha version. Read the docs here.
+ @if ($is_swarm_worker || $is_build_server) @@ -36,7 +42,7 @@ helper="For more information, please read the documentation here." label="Is it a Swarm Manager?" /> @endif - @if ($is_swarm_manager) + @if ($is_swarm_manager|| $is_build_server) From 2b1e35980f83e22fa0306a48d7e1d4adc4ffe981 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 16 Jan 2024 15:26:44 +0100 Subject: [PATCH 06/22] empty nixpacks type result in error --- app/Jobs/ApplicationDeploymentJob.php | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 16ff244c2..3ebe7f578 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -265,13 +265,13 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted } else { $this->write_deployment_configurations(); } - $this->execute_remote_command( - [ - "docker rm -f {$this->deployment_uuid} >/dev/null 2>&1", - "hidden" => true, - "ignore_errors" => true, - ] - ); + // $this->execute_remote_command( + // [ + // "docker rm -f {$this->deployment_uuid} >/dev/null 2>&1", + // "hidden" => true, + // "ignore_errors" => true, + // ] + // ); $this->execute_remote_command( [ "docker image prune -f >/dev/null 2>&1", @@ -889,6 +889,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted ); if ($this->saved_outputs->get('nixpacks_type')) { $this->nixpacks_type = $this->saved_outputs->get('nixpacks_type'); + if (str($this->nixpacks_type)->isEmpty()) { + throw new RuntimeException('Nixpacks failed to detect the application type. Please check the documentation of Nixpacks: https://nixpacks.com/docs/providers'); + } } if ($this->saved_outputs->get('nixpacks_plan')) { $this->nixpacks_plan = $this->saved_outputs->get('nixpacks_plan'); From 381e24bea56788b6d5b5a7d1795f22f28ff5003e Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 16 Jan 2024 15:45:19 +0100 Subject: [PATCH 07/22] fix: git pull command for deploy key based previews --- app/Livewire/Project/Application/Previews.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Livewire/Project/Application/Previews.php b/app/Livewire/Project/Application/Previews.php index dffb9461c..57cb43302 100644 --- a/app/Livewire/Project/Application/Previews.php +++ b/app/Livewire/Project/Application/Previews.php @@ -49,8 +49,9 @@ class Previews extends Component queue_application_deployment( application_id: $this->application->id, deployment_uuid: $this->deployment_uuid, - force_rebuild: true, + force_rebuild: false, pull_request_id: $pull_request_id, + git_type: $found->git_type ?? null, ); return redirect()->route('project.application.deployment.show', [ 'project_uuid' => $this->parameters['project_uuid'], From 94e2d951c4058ecf188b5c13e905284573a5be85 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 16 Jan 2024 15:45:54 +0100 Subject: [PATCH 08/22] Revert commented out code and execute remote command to remove Docker container --- app/Jobs/ApplicationDeploymentJob.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 3ebe7f578..1a243c359 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -265,13 +265,13 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted } else { $this->write_deployment_configurations(); } - // $this->execute_remote_command( - // [ - // "docker rm -f {$this->deployment_uuid} >/dev/null 2>&1", - // "hidden" => true, - // "ignore_errors" => true, - // ] - // ); + $this->execute_remote_command( + [ + "docker rm -f {$this->deployment_uuid} >/dev/null 2>&1", + "hidden" => true, + "ignore_errors" => true, + ] + ); $this->execute_remote_command( [ "docker image prune -f >/dev/null 2>&1", From 8c60dd5523fb9497fb50cb3c462448ebf0a1eb79 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 17 Jan 2024 11:21:47 +0100 Subject: [PATCH 09/22] typo --- resources/views/emails/server-revived.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/emails/server-revived.blade.php b/resources/views/emails/server-revived.blade.php index f58e644aa..62dc8173d 100644 --- a/resources/views/emails/server-revived.blade.php +++ b/resources/views/emails/server-revived.blade.php @@ -1,3 +1,3 @@ -Your server ({{ $name }}) was offline for a while, but it is back online now. All automations & integrationsare turned on again. +Your server ({{ $name }}) was offline for a while, but it is back online now. All automations & integrations are turned on again. From b6ce2e9122825eb8d8b9587691deb21d160a2fe2 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 17 Jan 2024 11:39:45 +0100 Subject: [PATCH 10/22] typo --- resources/views/emails/server-lost-connection.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/emails/server-lost-connection.blade.php b/resources/views/emails/server-lost-connection.blade.php index f0b1046f2..0bb30d091 100644 --- a/resources/views/emails/server-lost-connection.blade.php +++ b/resources/views/emails/server-lost-connection.blade.php @@ -3,7 +3,7 @@ Coolify cannot connect to your server ({{ $name }}). Please check your server an All automations & integrations are turned off! -IMPORTANT: We automatically try to revive your server. If your server is back online, we will automatically turn onall automations & integrations. +IMPORTANT: We automatically try to revive your server. If your server is back online, we will automatically turn on all automations & integrations. If you have any questions, please contact us. From a635e5148668d3299fa94255a440d9cc8d0a5482 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 17 Jan 2024 11:52:56 +0100 Subject: [PATCH 11/22] fix: server status job --- app/Console/Kernel.php | 2 +- app/Jobs/ApplicationDeploymentJob.php | 2 +- app/Jobs/ContainerStatusJob.php | 4 ++-- app/Jobs/ServerStatusJob.php | 3 +++ app/Models/Server.php | 4 ++++ 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index ac4626af1..50db6d681 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -72,7 +72,7 @@ class Kernel extends ConsoleKernel } } foreach ($servers as $server) { - $schedule->job(new ServerStatusJob($server))->everyFiveMinutes()->onOneServer(); + $schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer(); } } private function instance_auto_update($schedule) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 1a243c359..d9a7176a8 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -204,7 +204,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted if (data_get($this->application, 'settings.is_build_server_enabled')) { $teamId = data_get($this->application, 'environment.project.team.id'); - $buildServers = Server::where('team_id', $teamId)->whereRelation('settings', 'is_build_server', true)->get(); + $buildServers = Server::buildServers($teamId)->get(); if ($buildServers->count() === 0) { $this->application_deployment_queue->addLogEntry("Build server feature activated, but no suitable build server found. Using the deployment server."); $this->build_server = $this->server; diff --git a/app/Jobs/ContainerStatusJob.php b/app/Jobs/ContainerStatusJob.php index c28e266dd..b9fa3443f 100644 --- a/app/Jobs/ContainerStatusJob.php +++ b/app/Jobs/ContainerStatusJob.php @@ -44,8 +44,8 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted public function handle() { - if (!$this->server->isServerReady($this->tries)) { - return 'Server is not reachable.'; + if (!$this->server->isFunctional()) { + return 'Server is not ready.'; }; try { if ($this->server->isSwarm()) { diff --git a/app/Jobs/ServerStatusJob.php b/app/Jobs/ServerStatusJob.php index 2a3b41a2a..6272d442d 100644 --- a/app/Jobs/ServerStatusJob.php +++ b/app/Jobs/ServerStatusJob.php @@ -38,6 +38,9 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted public function handle() { try { + if (!$this->server->isServerReady($this->tries)) { + throw new \RuntimeException('Server is not ready.'); + }; if ($this->server->isFunctional()) { $this->cleanup(notify: false); } diff --git a/app/Models/Server.php b/app/Models/Server.php index a29293a02..1e41a808c 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -141,6 +141,10 @@ class Server extends BaseModel { return $this->ip === 'host.docker.internal' || $this->id === 0; } + static public function buildServers($teamId) + { + return Server::whereTeamId($teamId)->whereRelation('settings', 'is_reachable', true)->whereRelation('settings', 'is_build_server', true); + } public function skipServer() { if ($this->ip === '1.2.3.4') { From 4abcb2d5b9d5bbc4e45d353d778b5610144d6659 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 17 Jan 2024 12:23:58 +0100 Subject: [PATCH 12/22] Update notification labels in Discord and Telegram settings --- .../views/livewire/notifications/discord-settings.blade.php | 2 +- .../views/livewire/notifications/telegram-settings.blade.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/views/livewire/notifications/discord-settings.blade.php b/resources/views/livewire/notifications/discord-settings.blade.php index 9499d8c21..4bb1d15b4 100644 --- a/resources/views/livewire/notifications/discord-settings.blade.php +++ b/resources/views/livewire/notifications/discord-settings.blade.php @@ -13,7 +13,7 @@ @endif
- +
- +
Date: Wed, 17 Jan 2024 14:02:54 +0100 Subject: [PATCH 13/22] Update server unreachable notifications to include automatic revival --- app/Notifications/Server/Unreachable.php | 4 ++-- resources/views/emails/server-lost-connection.blade.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Notifications/Server/Unreachable.php b/app/Notifications/Server/Unreachable.php index fe9b46a11..bfd862993 100644 --- a/app/Notifications/Server/Unreachable.php +++ b/app/Notifications/Server/Unreachable.php @@ -52,13 +52,13 @@ class Unreachable extends Notification implements ShouldQueue public function toDiscord(): string { - $message = "Coolify: Your server '{$this->server->name}' is unreachable. All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server. If your server is back online, we will automatically turn on all automations & integrations."; + $message = "Coolify: Your server '{$this->server->name}' is unreachable. All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server and turn on all automations & integrations."; return $message; } public function toTelegram(): array { return [ - "message" => "Coolify: Your server '{$this->server->name}' is unreachable. All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server. If your server is back online, we will automatically turn on all automations & integrations." + "message" => "Coolify: Your server '{$this->server->name}' is unreachable. All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server and turn on all automations & integrations." ]; } } diff --git a/resources/views/emails/server-lost-connection.blade.php b/resources/views/emails/server-lost-connection.blade.php index 0bb30d091..fcd0a913d 100644 --- a/resources/views/emails/server-lost-connection.blade.php +++ b/resources/views/emails/server-lost-connection.blade.php @@ -3,7 +3,7 @@ Coolify cannot connect to your server ({{ $name }}). Please check your server an All automations & integrations are turned off! -IMPORTANT: We automatically try to revive your server. If your server is back online, we will automatically turn on all automations & integrations. +IMPORTANT: We automatically try to revive your server and turn on all automations & integrations. If you have any questions, please contact us. From c5f3398b73a8b12caf97d7279257408d3c481ba8 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 17 Jan 2024 14:06:41 +0100 Subject: [PATCH 14/22] Update build server and swarm support messages --- .../views/livewire/project/application/advanced.blade.php | 4 ++-- resources/views/livewire/server/form.blade.php | 2 +- resources/views/livewire/server/new/by-ip.blade.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/views/livewire/project/application/advanced.blade.php b/resources/views/livewire/project/application/advanced.blade.php index 1805f77a5..e376f0a72 100644 --- a/resources/views/livewire/project/application/advanced.blade.php +++ b/resources/views/livewire/project/application/advanced.blade.php @@ -7,8 +7,8 @@

General

+ helper="Use a build server to build your application. You can configure your build server in the Server settings. This is experimental." + instantSave id="application.settings.is_build_server_enabled" label="Use a Build Server? (experimental)" /> @if ($application->git_based()) diff --git a/resources/views/livewire/server/form.blade.php b/resources/views/livewire/server/form.blade.php index deb5998a4..f801c6444 100644 --- a/resources/views/livewire/server/form.blade.php +++ b/resources/views/livewire/server/form.blade.php @@ -59,7 +59,7 @@ helper="If you are using Cloudflare Tunnels, enable this. It will proxy all ssh requests to your server through Cloudflare.
Coolify does not install/setup Cloudflare (cloudflared) on your server." id="server.settings.is_cloudflare_tunnel" label="Cloudflare Tunnel" /> @if ($server->isSwarm()) -
Swarm support is in alpha version.
+
Swarm support is experimental.
@endif @if ($server->settings->is_swarm_worker)

Swarm Support

-
Swarm support is in alpha version. Read the docs Swarm support is experimental. Read the docs here.
@if ($is_swarm_worker || $is_build_server) From 8a913954729ae599e3bbafcc0d6552c7d51a6a65 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 17 Jan 2024 14:11:46 +0100 Subject: [PATCH 15/22] Update server model to use 'coolify' instead of 'coolify-overlay' for the name field --- app/Models/Server.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/Server.php b/app/Models/Server.php index 1e41a808c..948177ae3 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -112,7 +112,7 @@ class Server extends BaseModel ]); } else { StandaloneDocker::create([ - 'name' => 'coolify-overlay', + 'name' => 'coolify', 'network' => 'coolify', 'server_id' => $this->id, ]); From c620bb58edc1767a8c52f8f04d2a8562f12a9099 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 17 Jan 2024 15:41:32 +0100 Subject: [PATCH 16/22] Add build pack selection and show/hide static site options --- .../Project/New/PublicGitRepository.php | 21 ++++++++++-- .../new/public-git-repository.blade.php | 33 ++++++++++++++----- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/app/Livewire/Project/New/PublicGitRepository.php b/app/Livewire/Project/New/PublicGitRepository.php index 6599d1188..840a5bd78 100644 --- a/app/Livewire/Project/New/PublicGitRepository.php +++ b/app/Livewire/Project/New/PublicGitRepository.php @@ -30,18 +30,22 @@ class PublicGitRepository extends Component public GithubApp|GitlabApp|string $git_source = 'other'; public string $git_host; public string $git_repository; + public $build_pack; + public bool $show_is_static = true; protected $rules = [ 'repository_url' => 'required|url', 'port' => 'required|numeric', 'is_static' => 'required|boolean', 'publish_directory' => 'nullable|string', + 'build_pack' => 'required|string', ]; protected $validationAttributes = [ 'repository_url' => 'repository', 'port' => 'port', 'is_static' => 'static', 'publish_directory' => 'publish directory', + 'build_pack' => 'build pack', ]; public function mount() @@ -53,7 +57,18 @@ class PublicGitRepository extends Component $this->parameters = get_route_parameters(); $this->query = request()->query(); } - + public function updatedBuildPack() + { + if ($this->build_pack === 'nixpacks') { + $this->show_is_static = true; + } else if ($this->build_pack === 'static') { + $this->show_is_static = false; + $this->is_static = false; + } else { + $this->show_is_static = false; + $this->is_static = false; + } + } public function instantSave() { if ($this->is_static) { @@ -157,6 +172,7 @@ class PublicGitRepository extends Component 'environment_id' => $environment->id, 'destination_id' => $destination->id, 'destination_type' => $destination_class, + 'build_pack' => $this->build_pack, ]; } else { $application_init = [ @@ -170,7 +186,8 @@ class PublicGitRepository extends Component 'destination_id' => $destination->id, 'destination_type' => $destination_class, 'source_id' => $this->git_source->id, - 'source_type' => $this->git_source->getMorphClass() + 'source_type' => $this->git_source->getMorphClass(), + 'build_pack' => $this->build_pack, ]; } diff --git a/resources/views/livewire/project/new/public-git-repository.blade.php b/resources/views/livewire/project/new/public-git-repository.blade.php index 08a886777..5efeaa392 100644 --- a/resources/views/livewire/project/new/public-git-repository.blade.php +++ b/resources/views/livewire/project/new/public-git-repository.blade.php @@ -10,6 +10,15 @@ Check repository
+ @if (!$branch_found) +
+

Public repositories: https://...

+

Private repositories: git@...

+

Preselect branch: https://github.com/coollabsio/coolify-examples/tree/static to + select 'static' branch.

+
+ @endif @if ($branch_found) @if ($rate_limit_remaining && $rate_limit_reset)
@@ -21,12 +30,20 @@
@if ($git_source === 'other') - @else - @endif + + + + + + +
+ @if ($show_is_static) @if ($is_static) @@ -34,14 +51,14 @@ @endif -
-
- -
+
+ +
+ @endif
- Save New Application + Continue @endif
From 1e158badfc586b30b3a1fd0d96533ebe1a825b4f Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 17 Jan 2024 15:41:38 +0100 Subject: [PATCH 17/22] Update button text in by-ip.blade.php --- resources/views/livewire/server/new/by-ip.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/livewire/server/new/by-ip.blade.php b/resources/views/livewire/server/new/by-ip.blade.php index 5a847c44a..81d4b18c3 100644 --- a/resources/views/livewire/server/new/by-ip.blade.php +++ b/resources/views/livewire/server/new/by-ip.blade.php @@ -66,7 +66,7 @@ @endif
- Save Server + Continue @endif From af01bc3e775704ea4be1047ea3ef2a06fd131e8a Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 17 Jan 2024 15:48:01 +0100 Subject: [PATCH 18/22] fix: service deletion bug! --- app/Actions/Service/DeleteService.php | 4 +++- app/Jobs/DeleteResourceJob.php | 9 +++++++-- app/Models/Server.php | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/Actions/Service/DeleteService.php b/app/Actions/Service/DeleteService.php index 2d2c10ccb..c42a8ec4a 100644 --- a/app/Actions/Service/DeleteService.php +++ b/app/Actions/Service/DeleteService.php @@ -10,8 +10,10 @@ class DeleteService use AsAction; public function handle(Service $service) { - StopService::run($service); $server = data_get($service, 'server'); + if ($server->isFunctional()) { + StopService::run($service); + } $storagesToDelete = collect([]); $service->environment_variables()->delete(); diff --git a/app/Jobs/DeleteResourceJob.php b/app/Jobs/DeleteResourceJob.php index cf7c6462a..a29c87c09 100644 --- a/app/Jobs/DeleteResourceJob.php +++ b/app/Jobs/DeleteResourceJob.php @@ -31,11 +31,16 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted { try { $server = $this->resource->destination->server; + $this->resource->delete(); if (!$server->isFunctional()) { - $this->resource->forceDelete(); + if ($this->resource->type() === 'service') { + ray('dispatching delete service'); + DeleteService::dispatch($this->resource); + } else { + $this->resource->forceDelete(); + } return 'Server is not functional'; } - $this->resource->delete(); switch ($this->resource->type()) { case 'application': StopApplication::run($this->resource); diff --git a/app/Models/Server.php b/app/Models/Server.php index 948177ae3..dcbb0dde3 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -198,7 +198,7 @@ class Server extends BaseModel foreach ($this->databases() as $database) { $database->update(['status' => 'exited']); } - foreach ($this->services() as $service) { + foreach ($this->services()->get() as $service) { $apps = $service->applications()->get(); $dbs = $service->databases()->get(); foreach ($apps as $app) { From e060409a76534cb144be5889d292402312db7cd5 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 18 Jan 2024 11:24:07 +0100 Subject: [PATCH 19/22] fix: links --- app/Livewire/Server/Form.php | 6 +++--- resources/views/livewire/boarding/index.blade.php | 4 ++-- .../views/livewire/project/application/general.blade.php | 2 +- resources/views/livewire/project/shared/webhooks.blade.php | 4 ++-- resources/views/livewire/realtime-connection.blade.php | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/Livewire/Server/Form.php b/app/Livewire/Server/Form.php index 82c5fb683..b8cabd58b 100644 --- a/app/Livewire/Server/Form.php +++ b/app/Livewire/Server/Form.php @@ -78,7 +78,7 @@ class Form extends Component $this->server->settings->is_usable = true; $this->server->settings->save(); } else { - $this->dispatch('error', 'Server is not reachable.
Please validate your configuration and connection.

Check this documentation for further help.'); + $this->dispatch('error', 'Server is not reachable.
Please validate your configuration and connection.

Check this documentation for further help.'); return; } } @@ -87,12 +87,12 @@ class Form extends Component try { $uptime = $this->server->validateConnection(); if (!$uptime) { - $install && $this->dispatch('error', 'Server is not reachable.
Please validate your configuration and connection.

Check this documentation for further help.'); + $install && $this->dispatch('error', 'Server is not reachable.
Please validate your configuration and connection.

Check this documentation for further help.'); return; } $supported_os_type = $this->server->validateOS(); if (!$supported_os_type) { - $install && $this->dispatch('error', 'Server OS type is not supported for automated installation. Please install Docker manually before continuing: documentation.'); + $install && $this->dispatch('error', 'Server OS type is not supported for automated installation. Please install Docker manually before continuing: documentation.'); return; } $dockerInstalled = $this->server->validateDockerEngine(); diff --git a/resources/views/livewire/boarding/index.blade.php b/resources/views/livewire/boarding/index.blade.php index 179881e03..835e961e8 100644 --- a/resources/views/livewire/boarding/index.blade.php +++ b/resources/views/livewire/boarding/index.blade.php @@ -57,7 +57,7 @@ 'root' or skip the boarding process and add a new private key manually to Coolify and to the server.
- Check this documentation for further help. + Check this documentation for further help. Check again @@ -242,7 +242,7 @@

This will install the latest Docker Engine on your server, configure a few things to be able to run optimal.

Minimum Docker Engine version is: 22

To manually install Docker Engine, check this + href="https://docs.docker.com/engine/install/#server">this documentation.

diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php index 87c8fe619..94a229f43 100644 --- a/resources/views/livewire/project/application/general.blade.php +++ b/resources/views/livewire/project/application/general.blade.php @@ -43,7 +43,7 @@ @if ($application->build_pack === 'dockercompose') + 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 documentation." /> @if (count($parsedServices) > 0 && !$application->settings->is_raw_compose_deployment_enabled) @foreach (data_get($parsedServices, 'services') as $serviceName => $service) @if (!isDatabaseImage(data_get($service, 'image'))) diff --git a/resources/views/livewire/project/shared/webhooks.blade.php b/resources/views/livewire/project/shared/webhooks.blade.php index 927674f50..a90b01b58 100644 --- a/resources/views/livewire/project/shared/webhooks.blade.php +++ b/resources/views/livewire/project/shared/webhooks.blade.php @@ -2,11 +2,11 @@

Webhooks

+ helper="For more details goto our docs." />
@if ($resource->type() !== 'service') diff --git a/resources/views/livewire/realtime-connection.blade.php b/resources/views/livewire/realtime-connection.blade.php index 75cedea3e..e18c267ec 100644 --- a/resources/views/livewire/realtime-connection.blade.php +++ b/resources/views/livewire/realtime-connection.blade.php @@ -13,7 +13,7 @@ $wire.showNotification = true; @endif console.error( - 'Coolify could not connect to the new realtime service introduced in beta.154. This will cause unusual problems on the UI if not fixed! Please check the related documentation (https://coolify.io/docs/cloudflare-tunnels) or get help on Discord (https://coollabs.io/discord).)' + 'Coolify could not connect to the new realtime service introduced in beta.154. This will cause unusual problems on the UI if not fixed! Please check the related documentation (https://coolify.io/docs/cloudflare/tunnels) or get help on Discord (https://coollabs.io/discord).)' ); clearInterval(checkPusherInterval); } @@ -26,7 +26,7 @@ $wire.showNotification = true; @endif console.error( - 'Coolify could not connect to the new realtime service introduced in beta.154. This will cause unusual problems on the UI if not fixed! Please check the related documentation (https://coolify.io/docs/cloudflare-tunnels) or get help on Discord (https://coollabs.io/discord).)' + 'Coolify could not connect to the new realtime service introduced in beta.154. This will cause unusual problems on the UI if not fixed! Please check the related documentation (https://coolify.io/docs/cloudflare/tunnels) or get help on Discord (https://coollabs.io/discord).)' ); clearInterval(checkPusherInterval); } @@ -38,7 +38,7 @@ WARNING: Coolify could not connect to the new realtime service introduced in beta.154.
This will cause unusual problems on the UI if not fixed!

Please check the - related documentation or get + related documentation or get help on Discord.
Acknowledge the problem and disable this popup From 77558b37da31a6bc9854a66fb5a4abe43d39306b Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 18 Jan 2024 11:40:13 +0100 Subject: [PATCH 20/22] refactor build server --- app/Livewire/Project/Application/General.php | 2 ++ app/Livewire/Project/Application/Heading.php | 2 +- .../project/application/advanced.blade.php | 3 --- .../project/application/general.blade.php | 18 ++++++++++++------ 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php index 21608494a..2e02aac60 100644 --- a/app/Livewire/Project/Application/General.php +++ b/app/Livewire/Project/Application/General.php @@ -67,6 +67,7 @@ class General extends Component 'application.docker_compose_custom_start_command' => 'nullable', 'application.docker_compose_custom_build_command' => 'nullable', 'application.settings.is_raw_compose_deployment_enabled' => 'boolean|required', + 'application.settings.is_build_server_enabled' => 'boolean|required', ]; protected $validationAttributes = [ 'application.name' => 'name', @@ -100,6 +101,7 @@ class General extends Component 'application.docker_compose_custom_start_command' => 'Docker compose custom start command', 'application.docker_compose_custom_build_command' => 'Docker compose custom build command', 'application.settings.is_raw_compose_deployment_enabled' => 'Is raw compose deployment enabled', + 'application.settings.is_build_server_enabled' => 'Is build server enabled', ]; public function mount() { diff --git a/app/Livewire/Project/Application/Heading.php b/app/Livewire/Project/Application/Heading.php index 2dba750c8..95dcbdf73 100644 --- a/app/Livewire/Project/Application/Heading.php +++ b/app/Livewire/Project/Application/Heading.php @@ -78,7 +78,7 @@ class Heading extends Component return; } if (data_get($this->application, 'settings.is_build_server_enabled') && is_null($this->application->docker_registry_image_name)) { - $this->dispatch('error', 'To use a build server you must set a Docker image name first.
More information here: documentation'); + $this->dispatch('error', 'To use a build server you must set a Docker image name first.
More information here: documentation'); return; } $this->setDeploymentUuid(); diff --git a/resources/views/livewire/project/application/advanced.blade.php b/resources/views/livewire/project/application/advanced.blade.php index e376f0a72..29ebba166 100644 --- a/resources/views/livewire/project/application/advanced.blade.php +++ b/resources/views/livewire/project/application/advanced.blade.php @@ -6,9 +6,6 @@
Advanced configuration for your application.

General

- @if ($application->git_based()) diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php index 94a229f43..324a684a6 100644 --- a/resources/views/livewire/project/application/general.blade.php +++ b/resources/views/livewire/project/application/general.blade.php @@ -72,18 +72,20 @@
@endif @if ($application->build_pack !== 'dockercompose') -

Docker Registry

+
+

Docker Registry

+ @if ($application->build_pack !== 'dockerimage' && !$application->destination->server->isSwarm()) + + @endif +
@if ($application->destination->server->isSwarm()) @if ($application->build_pack !== 'dockerimage')
Docker Swarm requires the image to be available in a registry. More info here.
@endif - @else - @if ($application->build_pack !== 'dockerimage') -
Push the built image to a docker registry. More info here.
- @endif @endif
@if ($application->build_pack === 'dockerimage') @@ -160,6 +162,10 @@
@else +
From 4631c73809b742aa3e33de80d58fa1ecf18efa45 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 18 Jan 2024 12:05:48 +0100 Subject: [PATCH 21/22] Update general.blade.php with build server option and network section --- .../project/application/general.blade.php | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php index 324a684a6..a16953c7e 100644 --- a/resources/views/livewire/project/application/general.blade.php +++ b/resources/views/livewire/project/application/general.blade.php @@ -72,7 +72,7 @@
@endif @if ($application->build_pack !== 'dockercompose') -
+

Docker Registry

@if ($application->build_pack !== 'dockerimage' && !$application->destination->server->isSwarm()) build_pack !== 'dockerimage') -

Build

+

Build

+ @if ($application->build_pack !== 'dockercompose') +
+ +
+ @endif @if ($application->could_set_build_commands()) @if ($application->build_pack === 'nixpacks') -
Nixpacks will detect the required configuration automatically. - Framework Specific Docs -
+
@@ -133,6 +139,9 @@
+
Nixpacks will detect the required configuration automatically. + Framework Specific Docs +
@endif @endif @if ($application->build_pack === 'dockercompose') @@ -162,10 +171,6 @@
@else -
@@ -210,7 +215,7 @@ @endif @if ($application->build_pack !== 'dockercompose') -

Network

+

Network

@if ($application->settings->is_static || $application->build_pack === 'static') From 68fe886fb0f41bcde51bfd43b466d2eaf173434a Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 18 Jan 2024 12:14:11 +0100 Subject: [PATCH 22/22] Add restart functionality to proxy deployment --- app/Livewire/Server/Proxy/Deploy.php | 8 ++++++++ .../livewire/server/proxy/deploy.blade.php | 20 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/app/Livewire/Server/Proxy/Deploy.php b/app/Livewire/Server/Proxy/Deploy.php index 0dd314cd6..8349325d0 100644 --- a/app/Livewire/Server/Proxy/Deploy.php +++ b/app/Livewire/Server/Proxy/Deploy.php @@ -49,6 +49,14 @@ class Deploy extends Component { $this->server->refresh(); } + public function restart() { + try { + $this->stop(); + $this->dispatch('checkProxy'); + } catch (\Throwable $e) { + return handleError($e, $this); + } + } public function checkProxy() { try { diff --git a/resources/views/livewire/server/proxy/deploy.blade.php b/resources/views/livewire/server/proxy/deploy.blade.php index d016c1c8e..0bc0ca1ba 100644 --- a/resources/views/livewire/server/proxy/deploy.blade.php +++ b/resources/views/livewire/server/proxy/deploy.blade.php @@ -7,6 +7,15 @@

+ + +

This proxy will be stopped and started. It is not reversible.
All resources will be unavailable + during the restart. +
Please think + again. +

+
+
@if ($server->isFunctional() && data_get($server, 'proxy.type') !== 'NONE') @if (data_get($server, 'proxy.status') === 'running')
@@ -18,6 +27,17 @@ @endif + + + + + + + + Restart Proxy +