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).
+# Repo Activity
+
+
+
# Star History
[](https://star-history.com/#coollabsio/coolify&Date)
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/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/Console/Kernel.php b/app/Console/Kernel.php
index 7dbead831..50db6d681 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();
@@ -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)
@@ -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..d9a7176a8 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::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;
+ $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,26 +882,28 @@ 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],
);
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');
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 +1220,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 +1229,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 +1383,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 +1439,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 +1511,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/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/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/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/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..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()
{
@@ -227,7 +229,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..95dcbdf73 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/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'],
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/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..b8cabd58b 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()
@@ -76,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;
}
}
@@ -85,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/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.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/app/Livewire/Server/Proxy/Deploy.php b/app/Livewire/Server/Proxy/Deploy.php
index 4c748b7fa..8349325d0 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,12 +40,22 @@ 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 restart() {
+ try {
+ $this->stop();
+ $this->dispatch('checkProxy');
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ }
}
public function checkProxy()
{
@@ -50,7 +71,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 +98,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..dcbb0dde3 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)
@@ -112,7 +112,7 @@ class Server extends BaseModel
]);
} else {
StandaloneDocker::create([
- 'name' => 'coolify-overlay',
+ 'name' => 'coolify',
'network' => 'coolify',
'server_id' => $this->id,
]);
@@ -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') {
@@ -194,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) {
@@ -328,7 +332,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 +440,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 +470,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/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/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 @@
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 @@
-
This proxy will be stopped and started. It is not reversible.
All resources will be unavailable
+ during the restart.
+
Please think
+ again.
+