From a42c8da3444d9d4831202f46b0663a0b1e02e411 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 16 Jan 2024 15:19:14 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20proxy=20ui=20view=20feat:=20build=20serv?= =?UTF-8?q?er=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)