From 5d89df554a916622eaf5fad7132e9260e4838c74 Mon Sep 17 00:00:00 2001 From: Joao Patricio Date: Fri, 31 Mar 2023 11:26:56 +0100 Subject: [PATCH 1/7] Small ajustments. --- bootstrap/helpers.php | 2 +- database/seeders/ServerSeeder.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bootstrap/helpers.php b/bootstrap/helpers.php index b613cf109..98d63522a 100644 --- a/bootstrap/helpers.php +++ b/bootstrap/helpers.php @@ -63,7 +63,7 @@ if (!function_exists('savePrivateKey')) { } if (!function_exists('generateSshCommand')) { - function generateSshCommand(string $private_key_location, string $server_ip, string $user, string $port, string $command, bool $isMux = true) + function generateSshCommand(string $private_key_location, string $server_ip, string $user, string $port, string $command, bool $isMux = false) { $delimiter = 'EOF-COOLIFY-SSH'; Storage::disk('local')->makeDirectory('.ssh'); diff --git a/database/seeders/ServerSeeder.php b/database/seeders/ServerSeeder.php index 548856ba7..632d947d4 100644 --- a/database/seeders/ServerSeeder.php +++ b/database/seeders/ServerSeeder.php @@ -38,7 +38,7 @@ class ServerSeeder extends Seeder 'name' => "localhost", 'description' => "This is the local machine", 'user' => 'root', - 'ip' => "172.17.0.1", + 'ip' => "coolify-testing-host", 'team_id' => $root_team->id, 'private_key_id' => $private_key_1->id, ]); From ff3d0b29e3d16bdce1864021d008cad8ad4ef57f Mon Sep 17 00:00:00 2001 From: Joao Patricio Date: Fri, 31 Mar 2023 12:32:07 +0100 Subject: [PATCH 2/7] Abstracted deployment code into a job. --- .../RemoteProcess/DispatchRemoteProcess.php | 1 - app/Http/Controllers/ProjectController.php | 8 +- app/Http/Livewire/DeployApplication.php | 205 +-------------- app/Http/Livewire/PollActivity.php | 10 +- app/Jobs/DeployApplicationJob.php | 248 ++++++++++++++++++ bootstrap/helpers.php | 23 +- .../views/livewire/poll-activity.blade.php | 7 +- resources/views/project/deployment.blade.php | 2 +- 8 files changed, 289 insertions(+), 215 deletions(-) create mode 100644 app/Jobs/DeployApplicationJob.php diff --git a/app/Actions/RemoteProcess/DispatchRemoteProcess.php b/app/Actions/RemoteProcess/DispatchRemoteProcess.php index 95a55bc91..c1f0e8036 100644 --- a/app/Actions/RemoteProcess/DispatchRemoteProcess.php +++ b/app/Actions/RemoteProcess/DispatchRemoteProcess.php @@ -3,7 +3,6 @@ namespace App\Actions\RemoteProcess; use App\Data\RemoteProcessArgs; -use App\Jobs\DeployRemoteProcess; use App\Jobs\ExecuteRemoteProcess; use Spatie\Activitylog\Models\Activity; diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php index f9bc0e8bb..50612aaa4 100644 --- a/app/Http/Controllers/ProjectController.php +++ b/app/Http/Controllers/ProjectController.php @@ -17,6 +17,7 @@ class ProjectController extends Controller } return view('project.environments', ['project' => $project]); } + public function resources() { $project = session('currentTeam')->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); @@ -29,6 +30,7 @@ class ProjectController extends Controller } return view('project.resources', ['project' => $project, 'environment' => $environment]); } + public function application() { $project = session('currentTeam')->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); @@ -62,7 +64,9 @@ class ProjectController extends Controller if (!$application) { return redirect()->route('home'); } - $activity = $application->get_deployment($deployment_uuid); - return view('project.deployment', ['activity' => $activity]); + + return view('project.deployment', [ + 'deployment_uuid' => $deployment_uuid, + ]); } } diff --git a/app/Http/Livewire/DeployApplication.php b/app/Http/Livewire/DeployApplication.php index 342e2e2f3..5ad4e7f00 100644 --- a/app/Http/Livewire/DeployApplication.php +++ b/app/Http/Livewire/DeployApplication.php @@ -3,6 +3,7 @@ namespace App\Http\Livewire; use App\Jobs\ContainerStatusJob; +use App\Jobs\DeployApplicationJob; use App\Models\Application; use App\Models\CoolifyInstanceSettings; use DateTimeImmutable; @@ -35,210 +36,22 @@ class DeployApplication extends Component $this->application = Application::where('uuid', $this->application_uuid)->first(); $this->destination = $this->application->destination->getMorphClass()::where('id', $this->application->destination->id)->first(); } + public function render() { return view('livewire.deploy-application'); } - private function execute_in_builder(string $command) - { - return $this->command[] = "docker exec {$this->deployment_uuid} bash -c '{$command}'"; - // if ($this->application->settings->is_debug) { - // } else { - // return $this->command[] = "docker exec {$this->deployment_uuid} bash -c '{$command}'"; - // } - } - private function start_builder_container() - { - $this->command[] = "docker run --pull=always -d --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/coollabsio/coolify-builder >/dev/null 2>&1"; - } - private function generate_docker_compose() - { - $docker_compose = [ - 'version' => '3.8', - 'services' => [ - $this->application->uuid => [ - 'image' => "{$this->application->uuid}:TAG", - 'container_name' => $this->application->uuid, - 'restart' => 'always', - 'labels' => $this->set_labels_for_applications(), - 'expose' => $this->application->ports_exposes, - 'networks' => [ - $this->destination->network, - ], - 'healthcheck' => [ - 'test' => [ - 'CMD-SHELL', - $this->generate_healthcheck_commands() - ], - 'interval' => $this->application->health_check_interval . 's', - 'timeout' => $this->application->health_check_timeout . 's', - 'retries' => $this->application->health_check_retries, - 'start_period' => $this->application->health_check_start_period . 's' - ], - ] - ], - 'networks' => [ - $this->destination->network => [ - 'external' => false, - 'name' => $this->destination->network, - 'attachable' => true, - ] - ] - ]; - if (count($this->application->ports_mappings) > 0) { - $docker_compose['services'][$this->application->uuid]['ports'] = $this->application->ports_mappings; - } - // if (count($volumes) > 0) { - // $docker_compose['services'][$this->application->uuid]['volumes'] = $volumes; - // } - // if (count($volume_names) > 0) { - // $docker_compose['volumes'] = $volume_names; - // } - return Yaml::dump($docker_compose); - } - private function set_labels_for_applications() - { - $labels = []; - $labels[] = 'coolify.managed=true'; - $labels[] = 'coolify.version=' . config('coolify.version'); - $labels[] = 'coolify.applicationId=' . $this->application->id; - $labels[] = 'coolify.type=application'; - $labels[] = 'coolify.name=' . $this->application->name; - if ($this->application->fqdn) { - $labels[] = "traefik.http.routers.container.rule=Host(`{$this->application->fqdn}`)"; - } - return $labels; - } - private function generate_healthcheck_commands() - { - if (!$this->application->health_check_port) { - $this->application->health_check_port = $this->application->ports_exposes[0]; - } - if ($this->application->health_check_path) { - $generated_healthchecks_commands = [ - "curl -X {$this->application->health_check_method} -f {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$this->application->health_check_port}{$this->application->health_check_path}" - ]; - } else { - $generated_healthchecks_commands = []; - foreach ($this->application->ports_exposes as $key => $port) { - $generated_healthchecks_commands = [ - "curl -X {$this->application->health_check_method} -f {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$port}/" - ]; - if (count($this->application->ports_exposes) != $key + 1) { - $generated_healthchecks_commands[] = '&&'; - } - } - } - return implode(' ', $generated_healthchecks_commands); - } - private function generate_jwt_token_for_github() - { - $signingKey = InMemory::plainText($this->source->privateKey->private_key); - $algorithm = new Sha256(); - $tokenBuilder = (new Builder(new JoseEncoder(), ChainedFormatter::default())); - $now = new DateTimeImmutable(); - $now = $now->setTime($now->format('H'), $now->format('i')); - $issuedToken = $tokenBuilder - ->issuedBy($this->source->app_id) - ->issuedAt($now) - ->expiresAt($now->modify('+10 minutes')) - ->getToken($algorithm, $signingKey) - ->toString(); - $token = Http::withHeaders([ - 'Authorization' => "Bearer $issuedToken", - 'Accept' => 'application/vnd.github.machine-man-preview+json' - ])->post("{$this->source->api_url}/app/installations/{$this->source->installation_id}/access_tokens"); - if ($token->failed()) { - throw new \Exception("Failed to get access token for $this->application_name from " . $this->source_name . " with error: " . $token->json()['message']); - } - return $token->json()['token']; - } + + public function deploy() { - $coolify_instance_settings = CoolifyInstanceSettings::find(1); - $this->destination = $this->application->destination->getMorphClass()::where('id', $this->application->destination->id)->first(); - $this->source = $this->application->source->getMorphClass()::where('id', $this->application->source->id)->first(); - - $source_html_url = data_get($this->application, 'source.html_url'); - $url = parse_url(filter_var($source_html_url, FILTER_SANITIZE_URL)); - $source_html_url_host = $url['host']; - $source_html_url_scheme = $url['scheme']; - - // Get Wildcard Domain - $project_wildcard_domain = data_get($this->application, 'environment.project.settings.wildcard_domain'); - $global_wildcard_domain = data_get($coolify_instance_settings, 'wildcard_domain'); - $wildcard_domain = $project_wildcard_domain ?? $global_wildcard_domain ?? null; - // Create Deployment ID $this->deployment_uuid = new Cuid2(7); - // Set wildcard domain - if (!$this->application->settings->is_bot && !$this->application->fqdn && $wildcard_domain) { - $this->application->fqdn = $this->application->uuid . '.' . $wildcard_domain; - $this->application->save(); - } - $workdir = "/artifacts/{$this->deployment_uuid}"; - - // Start build process - $this->command[] = "echo 'Starting deployment of {$this->application->git_repository}:{$this->application->git_branch}...'"; - $this->command[] = "echo -n 'Pulling latest version of the builder image (ghcr.io/coollabsio/coolify-builder)... '"; - $this->start_builder_container(); - $this->command[] = "echo 'Done.'"; - $this->command[] = "echo -n 'Importing {$this->application->git_repository}:{$this->application->git_branch} to {$workdir}... '"; - if ($this->application->source->getMorphClass() == 'App\Models\GithubApp') { - if ($this->source->is_public) { - $this->execute_in_builder("git clone -q -b {$this->application->git_branch} {$this->source->html_url}/{$this->application->git_repository}.git {$workdir}"); - } else { - $github_access_token = $this->generate_jwt_token_for_github(); - $this->execute_in_builder("git clone -q -b {$this->application->git_branch} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$this->application->git_repository}.git {$workdir}"); - } - } - $this->command[] = "echo 'Done.'"; - // Export git commit to a file - $this->command[] = "echo -n 'Checking commit sha... '"; - $this->execute_in_builder("cd {$workdir} && git rev-parse HEAD > {$workdir}/.git-commit"); - $this->command[] = "echo 'Done.'"; - // Remove .git folder - $this->command[] = "echo -n 'Removing .git folder... '"; - $this->execute_in_builder("rm -fr {$workdir}/.git"); - $this->command[] = "echo 'Done.'"; - // Create docker-compose.yml && replace TAG with git commit - $docker_compose_base64 = base64_encode($this->generate_docker_compose($this->application)); - $this->execute_in_builder("echo '{$docker_compose_base64}' | base64 -d > {$workdir}/docker-compose.yml"); - $this->execute_in_builder("sed -i \"s/TAG/$(cat {$workdir}/.git-commit)/g\" {$workdir}/docker-compose.yml"); - - $this->command[] = "echo -n 'Generating nixpacks configuration... '"; - if (str_starts_with($this->application->base_image, 'apache') || str_starts_with($this->application->base_image, 'nginx')) { - // @TODO: Add static site builds - } else { - $nixpacks_command = "nixpacks build -o {$workdir} --no-error-without-start"; - if ($this->application->install_command) { - $nixpacks_command .= " --install-cmd '{$this->application->install_command}'"; - } - if ($this->application->build_command) { - $nixpacks_command .= " --build-cmd '{$this->application->build_command}'"; - } - if ($this->application->start_command) { - $nixpacks_command .= " --start-cmd '{$this->application->start_command}'"; - } - $nixpacks_command .= " {$workdir}"; - $this->execute_in_builder($nixpacks_command); - $this->execute_in_builder("cp {$workdir}/.nixpacks/Dockerfile {$workdir}/Dockerfile"); - $this->execute_in_builder("rm -f {$workdir}/.nixpacks/Dockerfile"); - } - $this->command[] = "echo 'Done.'"; - $this->command[] = "echo -n 'Building image... '"; - - $this->execute_in_builder("docker build -f {$workdir}/Dockerfile --build-arg SOURCE_COMMIT=$(cat {$workdir}/.git-commit) --progress plain -t {$this->application->uuid}:$(cat {$workdir}/.git-commit) {$workdir}"); - $this->command[] = "echo 'Done.'"; - $this->execute_in_builder("docker rm -f {$this->application->uuid} >/dev/null 2>&1"); - - $this->command[] = "echo -n 'Deploying... '"; - - $this->execute_in_builder("docker compose --project-directory {$workdir} up -d"); - $this->command[] = "echo 'Done. 🎉'"; - $this->command[] = "docker stop -t 0 {$this->deployment_uuid} >/dev/null"; - $this->activity = remoteProcess($this->command, $this->destination->server, $this->deployment_uuid, $this->application); + dispatch(new DeployApplicationJob( + deployment_uuid: $this->deployment_uuid, + application_uuid: $this->application_uuid, + )); $currentUrl = url()->previous(); $deploymentUrl = "$currentUrl/deployment/$this->deployment_uuid"; @@ -251,10 +64,12 @@ class DeployApplication extends Component $this->application->status = 'exited'; $this->application->save(); } + public function pollingStatus() { $this->application->refresh(); } + public function checkStatus() { $output = runRemoteCommandSync($this->destination->server, ["docker ps -a --format '{{.State}}' --filter 'name={$this->application->uuid}'"]); diff --git a/app/Http/Livewire/PollActivity.php b/app/Http/Livewire/PollActivity.php index 3a7822d8a..8092f57a5 100644 --- a/app/Http/Livewire/PollActivity.php +++ b/app/Http/Livewire/PollActivity.php @@ -3,15 +3,23 @@ namespace App\Http\Livewire; use Livewire\Component; +use Spatie\Activitylog\Models\Activity; class PollActivity extends Component { public $activity; public $isKeepAliveOn = true; + public $deployment_uuid; public function polling() { - $this->activity?->refresh(); + if ( is_null($this->activity) && isset($this->deployment_uuid)) { + $this->activity = Activity::where('properties->deployment_uuid', '=', $this->deployment_uuid) + ->first(); + } else { + $this->activity?->refresh(); + } + if (data_get($this->activity, 'properties.exitCode') !== null) { $this->isKeepAliveOn = false; } diff --git a/app/Jobs/DeployApplicationJob.php b/app/Jobs/DeployApplicationJob.php new file mode 100644 index 000000000..e7bdd60d7 --- /dev/null +++ b/app/Jobs/DeployApplicationJob.php @@ -0,0 +1,248 @@ +application = Application::query() + ->where('uuid', $this->application_uuid) + ->firstOrFail(); + + $coolify_instance_settings = CoolifyInstanceSettings::find(1); + + $this->destination = $this->application->destination->getMorphClass()::where('id', $this->application->destination->id)->first(); + $this->source = $this->application->source->getMorphClass()::where('id', $this->application->source->id)->first(); + + $source_html_url = data_get($this->application, 'source.html_url'); + $url = parse_url(filter_var($source_html_url, FILTER_SANITIZE_URL)); + $source_html_url_host = $url['host']; + $source_html_url_scheme = $url['scheme']; + + // Get Wildcard Domain + $project_wildcard_domain = data_get($this->application, 'environment.project.settings.wildcard_domain'); + $global_wildcard_domain = data_get($coolify_instance_settings, 'wildcard_domain'); + $wildcard_domain = $project_wildcard_domain ?? $global_wildcard_domain ?? null; + + // Set wildcard domain + if (!$this->application->settings->is_bot && !$this->application->fqdn && $wildcard_domain) { + $this->application->fqdn = $this->application->uuid . '.' . $wildcard_domain; + $this->application->save(); + } + $workdir = "/artifacts/{$this->deployment_uuid}"; + + // Start build process + $this->command[] = "echo 'Starting deployment of {$this->application->git_repository}:{$this->application->git_branch}...'"; + $this->command[] = "echo -n 'Pulling latest version of the builder image (ghcr.io/coollabsio/coolify-builder)... '"; + $this->start_builder_container(); + $this->command[] = "echo 'Done.'"; + $this->command[] = "echo -n 'Importing {$this->application->git_repository}:{$this->application->git_branch} to {$workdir}... '"; + if ($this->application->source->getMorphClass() == 'App\Models\GithubApp') { + if ($this->source->is_public) { + $this->execute_in_builder("git clone -q -b {$this->application->git_branch} {$this->source->html_url}/{$this->application->git_repository}.git {$workdir}"); + } else { + $github_access_token = $this->generate_jwt_token_for_github(); + $this->execute_in_builder("git clone -q -b {$this->application->git_branch} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$this->application->git_repository}.git {$workdir}"); + } + } + $this->command[] = "echo 'Done.'"; + // Export git commit to a file + $this->command[] = "echo -n 'Checking commit sha... '"; + $this->execute_in_builder("cd {$workdir} && git rev-parse HEAD > {$workdir}/.git-commit"); + $this->command[] = "echo 'Done.'"; + // Remove .git folder + $this->command[] = "echo -n 'Removing .git folder... '"; + $this->execute_in_builder("rm -fr {$workdir}/.git"); + $this->command[] = "echo 'Done.'"; + // Create docker-compose.yml && replace TAG with git commit + $docker_compose_base64 = base64_encode($this->generate_docker_compose($this->application)); + $this->execute_in_builder("echo '{$docker_compose_base64}' | base64 -d > {$workdir}/docker-compose.yml"); + $this->execute_in_builder("sed -i \"s/TAG/$(cat {$workdir}/.git-commit)/g\" {$workdir}/docker-compose.yml"); + + + $this->command[] = "echo -n 'Generating nixpacks configuration... '"; + if (str_starts_with($this->application->base_image, 'apache') || str_starts_with($this->application->base_image, 'nginx')) { + // @TODO: Add static site builds + } else { + $nixpacks_command = "nixpacks build -o {$workdir} --no-error-without-start"; + if ($this->application->install_command) { + $nixpacks_command .= " --install-cmd '{$this->application->install_command}'"; + } + if ($this->application->build_command) { + $nixpacks_command .= " --build-cmd '{$this->application->build_command}'"; + } + if ($this->application->start_command) { + $nixpacks_command .= " --start-cmd '{$this->application->start_command}'"; + } + $nixpacks_command .= " {$workdir}"; + $this->execute_in_builder($nixpacks_command); + $this->execute_in_builder("cp {$workdir}/.nixpacks/Dockerfile {$workdir}/Dockerfile"); + $this->execute_in_builder("rm -f {$workdir}/.nixpacks/Dockerfile"); + } + $this->command[] = "echo 'Done.'"; + $this->command[] = "echo -n 'Building image... '"; + + $this->execute_in_builder("docker build -f {$workdir}/Dockerfile --build-arg SOURCE_COMMIT=$(cat {$workdir}/.git-commit) --progress plain -t {$this->application->uuid}:$(cat {$workdir}/.git-commit) {$workdir}"); + $this->command[] = "echo 'Done.'"; + $this->execute_in_builder("docker rm -f {$this->application->uuid} >/dev/null 2>&1"); + + $this->command[] = "echo -n 'Deploying... '"; + + $this->execute_in_builder("docker compose --project-directory {$workdir} up -d"); + $this->command[] = "echo 'Done. 🎉'"; + $this->command[] = "docker stop -t 0 {$this->deployment_uuid} >/dev/null"; + + remoteProcess($this->command, $this->destination->server, $this->deployment_uuid, $this->application); + } + + private function start_builder_container() + { + $this->command[] = "docker run --pull=always -d --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/coollabsio/coolify-builder >/dev/null 2>&1"; + } + + private function execute_in_builder(string $command) + { + return $this->command[] = "docker exec {$this->deployment_uuid} bash -c '{$command}'"; + } + + private function generate_docker_compose() + { + $docker_compose = [ + 'version' => '3.8', + 'services' => [ + $this->application->uuid => [ + 'image' => "{$this->application->uuid}:TAG", + 'container_name' => $this->application->uuid, + 'restart' => 'always', + 'labels' => $this->set_labels_for_applications(), + 'expose' => $this->application->ports_exposes, + 'networks' => [ + $this->destination->network, + ], + 'healthcheck' => [ + 'test' => [ + 'CMD-SHELL', + $this->generate_healthcheck_commands() + ], + 'interval' => $this->application->health_check_interval . 's', + 'timeout' => $this->application->health_check_timeout . 's', + 'retries' => $this->application->health_check_retries, + 'start_period' => $this->application->health_check_start_period . 's' + ], + ] + ], + 'networks' => [ + $this->destination->network => [ + 'external' => false, + 'name' => $this->destination->network, + 'attachable' => true, + ] + ] + ]; + if (count($this->application->ports_mappings) > 0) { + $docker_compose['services'][$this->application->uuid]['ports'] = $this->application->ports_mappings; + } + // if (count($volumes) > 0) { + // $docker_compose['services'][$this->application->uuid]['volumes'] = $volumes; + // } + // if (count($volume_names) > 0) { + // $docker_compose['volumes'] = $volume_names; + // } + return Yaml::dump($docker_compose); + } + + private function generate_healthcheck_commands() + { + if (!$this->application->health_check_port) { + $this->application->health_check_port = $this->application->ports_exposes[0]; + } + if ($this->application->health_check_path) { + $generated_healthchecks_commands = [ + "curl -X {$this->application->health_check_method} -f {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$this->application->health_check_port}{$this->application->health_check_path}" + ]; + } else { + $generated_healthchecks_commands = []; + foreach ($this->application->ports_exposes as $key => $port) { + $generated_healthchecks_commands = [ + "curl -X {$this->application->health_check_method} -f {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$port}/" + ]; + if (count($this->application->ports_exposes) != $key + 1) { + $generated_healthchecks_commands[] = '&&'; + } + } + } + return implode(' ', $generated_healthchecks_commands); + } + + private function generate_jwt_token_for_github() + { + $signingKey = InMemory::plainText($this->source->privateKey->private_key); + $algorithm = new Sha256(); + $tokenBuilder = (new Builder(new JoseEncoder(), ChainedFormatter::default())); + $now = new DateTimeImmutable(); + $now = $now->setTime($now->format('H'), $now->format('i')); + $issuedToken = $tokenBuilder + ->issuedBy($this->source->app_id) + ->issuedAt($now) + ->expiresAt($now->modify('+10 minutes')) + ->getToken($algorithm, $signingKey) + ->toString(); + $token = Http::withHeaders([ + 'Authorization' => "Bearer $issuedToken", + 'Accept' => 'application/vnd.github.machine-man-preview+json' + ])->post("{$this->source->api_url}/app/installations/{$this->source->installation_id}/access_tokens"); + if ($token->failed()) { + throw new \Exception("Failed to get access token for $this->application->name from " . $this->source->name . " with error: " . $token->json()['message']); + } + return $token->json()['token']; + } + + private function set_labels_for_applications() + { + $labels = []; + $labels[] = 'coolify.managed=true'; + $labels[] = 'coolify.version=' . config('coolify.version'); + $labels[] = 'coolify.applicationId=' . $this->application->id; + $labels[] = 'coolify.type=application'; + $labels[] = 'coolify.name=' . $this->application->name; + if ($this->application->fqdn) { + $labels[] = "traefik.http.routers.container.rule=Host(`{$this->application->fqdn}`)"; + } + return $labels; + } +} diff --git a/bootstrap/helpers.php b/bootstrap/helpers.php index 98d63522a..d68d413b4 100644 --- a/bootstrap/helpers.php +++ b/bootstrap/helpers.php @@ -20,8 +20,8 @@ if (!function_exists('remoteProcess')) { function remoteProcess( array $command, Server $server, - string|null $deployment_uuid = null, - Model|null $model = null, + ?string $deployment_uuid = null, + ?Model $model = null, ): Activity { $command_string = implode("\n", $command); // @TODO: Check if the user has access to this server @@ -31,28 +31,29 @@ if (!function_exists('remoteProcess')) { return resolve(DispatchRemoteProcess::class, [ 'remoteProcessArgs' => new RemoteProcessArgs( - type: $deployment_uuid ? ActivityTypes::DEPLOYMENT->value : ActivityTypes::REMOTE_PROCESS->value, model: $model, server_ip: $server->ip, - deployment_uuid: $deployment_uuid, private_key_location: $private_key_location, + deployment_uuid: $deployment_uuid, command: <<port, user: $server->user, + type: $deployment_uuid ? ActivityTypes::DEPLOYMENT->value : ActivityTypes::REMOTE_PROCESS->value, ), ])(); } - // function checkTeam(string $team_id) - // { - // $found_team = auth()->user()->teams->pluck('id')->contains($team_id); - // if (!$found_team) { - // throw new \RuntimeException('You do not have access to this server.'); - // } - // } } +// function checkTeam(string $team_id) +// { +// $found_team = auth()->user()->teams->pluck('id')->contains($team_id); +// if (!$found_team) { +// throw new \RuntimeException('You do not have access to this server.'); +// } +// } + if (!function_exists('savePrivateKey')) { function savePrivateKey(Server $server) { diff --git a/resources/views/livewire/poll-activity.blade.php b/resources/views/livewire/poll-activity.blade.php index 389483dc1..bc939f7d4 100644 --- a/resources/views/livewire/poll-activity.blade.php +++ b/resources/views/livewire/poll-activity.blade.php @@ -1,6 +1,5 @@
- @isset($activity?->id) -
{{ data_get($activity, 'description') }}
- @endisset - {{--
{{ data_get($activity, 'properties') }}
--}} +
+        {{ data_get($activity, 'description') }}
+    
diff --git a/resources/views/project/deployment.blade.php b/resources/views/project/deployment.blade.php index c49e8112e..750b7d9e9 100644 --- a/resources/views/project/deployment.blade.php +++ b/resources/views/project/deployment.blade.php @@ -1,4 +1,4 @@

Deployment

- +
From 2d422326bdc1246227faa22957a3d9744648c665 Mon Sep 17 00:00:00 2001 From: Joao Patricio Date: Fri, 31 Mar 2023 13:30:08 +0100 Subject: [PATCH 3/7] Running remote commands Sync inside Ansyc Job. Getting Output, now need to parse it. --- app/Data/RemoteProcessArgs.php | 2 +- app/Jobs/DeployApplicationJob.php | 83 +++++++++++++++++++++++++++---- 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/app/Data/RemoteProcessArgs.php b/app/Data/RemoteProcessArgs.php index 57c46d813..cdf13cac4 100644 --- a/app/Data/RemoteProcessArgs.php +++ b/app/Data/RemoteProcessArgs.php @@ -10,7 +10,6 @@ use Spatie\LaravelData\Data; class RemoteProcessArgs extends Data { public function __construct( - public Model|null $model, public string $server_ip, public string $private_key_location, public string|null $deployment_uuid, @@ -19,6 +18,7 @@ class RemoteProcessArgs extends Data public string $user, public string $type = ActivityTypes::REMOTE_PROCESS->value, public string $status = ProcessStatus::HOLDING->value, + public ?Model $model = null, ) { } } diff --git a/app/Jobs/DeployApplicationJob.php b/app/Jobs/DeployApplicationJob.php index e7bdd60d7..1b5d83bcd 100644 --- a/app/Jobs/DeployApplicationJob.php +++ b/app/Jobs/DeployApplicationJob.php @@ -2,6 +2,9 @@ namespace App\Jobs; +use App\Actions\RemoteProcess\RunRemoteProcess; +use App\Data\RemoteProcessArgs; +use App\Enums\ActivityTypes; use App\Models\Application; use App\Models\CoolifyInstanceSettings; use DateTimeImmutable; @@ -17,6 +20,7 @@ use Lcobucci\JWT\Encoding\JoseEncoder; use Lcobucci\JWT\Signer\Key\InMemory; use Lcobucci\JWT\Signer\Rsa\Sha256; use Lcobucci\JWT\Token\Builder; +use Spatie\Activitylog\Models\Activity; use Symfony\Component\Yaml\Yaml; class DeployApplicationJob implements ShouldQueue @@ -26,6 +30,7 @@ class DeployApplicationJob implements ShouldQueue protected $application; protected $destination; protected $source; + protected Activity $activity; /** * Create a new job instance. @@ -33,20 +38,39 @@ class DeployApplicationJob implements ShouldQueue public function __construct( public string $deployment_uuid, public string $application_uuid, - ){} + ){ + $this->application = Application::query() + ->where('uuid', $this->application_uuid) + ->firstOrFail(); + $this->destination = $this->application->destination->getMorphClass()::where('id', $this->application->destination->id)->first(); + + $private_key_location = savePrivateKey($this->destination->server); + + $server = $this->destination->server; + + $remoteProcessArgs = new RemoteProcessArgs( + server_ip: $server->ip, + private_key_location: $private_key_location, + deployment_uuid: $this->deployment_uuid, + command: 'overwritten-later', + port: $server->port, + user: $server->user, + type: ActivityTypes::DEPLOYMENT->value, + ); + + $this->activity = activity() + ->performedOn($this->application) + ->withProperties($remoteProcessArgs->toArray()) + ->event(ActivityTypes::DEPLOYMENT->value) + ->log(""); + } /** * Execute the job. */ public function handle(): void { - $this->application = Application::query() - ->where('uuid', $this->application_uuid) - ->firstOrFail(); - $coolify_instance_settings = CoolifyInstanceSettings::find(1); - - $this->destination = $this->application->destination->getMorphClass()::where('id', $this->application->destination->id)->first(); $this->source = $this->application->source->getMorphClass()::where('id', $this->application->source->id)->first(); $source_html_url = data_get($this->application, 'source.html_url'); @@ -83,12 +107,37 @@ class DeployApplicationJob implements ShouldQueue $this->command[] = "echo 'Done.'"; // Export git commit to a file $this->command[] = "echo -n 'Checking commit sha... '"; - $this->execute_in_builder("cd {$workdir} && git rev-parse HEAD > {$workdir}/.git-commit"); + + $this->executeNow($this->command); + + ray($this->activity); + + return; + + // @TODO execute + + $fullOutput = $this->execute_in_builder("cd {$workdir} && git rev-parse HEAD"); + + // @TODO + // Run remote thing + // Get output + // Parse output + // Compare Commit_SHA in PHP, and decide if to stop or not + + $this->command[] = "echo 'Done.'"; // Remove .git folder $this->command[] = "echo -n 'Removing .git folder... '"; $this->execute_in_builder("rm -fr {$workdir}/.git"); $this->command[] = "echo 'Done.'"; + + + $comparison = false; // work the output + if (! $comparison) { + + return; + } + // Create docker-compose.yml && replace TAG with git commit $docker_compose_base64 = base64_encode($this->generate_docker_compose($this->application)); $this->execute_in_builder("echo '{$docker_compose_base64}' | base64 -d > {$workdir}/docker-compose.yml"); @@ -126,8 +175,6 @@ class DeployApplicationJob implements ShouldQueue $this->execute_in_builder("docker compose --project-directory {$workdir} up -d"); $this->command[] = "echo 'Done. 🎉'"; $this->command[] = "docker stop -t 0 {$this->deployment_uuid} >/dev/null"; - - remoteProcess($this->command, $this->destination->server, $this->deployment_uuid, $this->application); } private function start_builder_container() @@ -245,4 +292,20 @@ class DeployApplicationJob implements ShouldQueue } return $labels; } + + private function executeNow($command) + { + $commandText = collect($command)->implode("\n"); + + $currentProperties = $this->activity->properties; + $currentProperties->put('command', $commandText); + $this->activity->properties = $currentProperties; + $this->activity->save(); + + $remoteProcess = resolve(RunRemoteProcess::class, [ + 'activity' => $this->activity, + ]); + + $remoteProcess(); + } } From c29115146d0e66a9bc9f67a5ea814fc078a3ec19 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Fri, 31 Mar 2023 15:38:36 +0200 Subject: [PATCH 4/7] Working deployment - still need improvements. --- .../RemoteProcess/RunRemoteProcess.php | 10 +- app/Jobs/DeployApplicationJob.php | 119 +++++++----------- 2 files changed, 56 insertions(+), 73 deletions(-) diff --git a/app/Actions/RemoteProcess/RunRemoteProcess.php b/app/Actions/RemoteProcess/RunRemoteProcess.php index fbd62df91..6627c1bd7 100644 --- a/app/Actions/RemoteProcess/RunRemoteProcess.php +++ b/app/Actions/RemoteProcess/RunRemoteProcess.php @@ -14,6 +14,8 @@ class RunRemoteProcess { public Activity $activity; + public bool $hideFromOutput; + protected $timeStart; protected $currentTime; @@ -29,7 +31,7 @@ class RunRemoteProcess /** * Create a new job instance. */ - public function __construct(Activity $activity) + public function __construct(Activity $activity, bool $hideFromOutput) { if ($activity->getExtraProperty('type') !== ActivityTypes::REMOTE_PROCESS->value && $activity->getExtraProperty('type') !== ActivityTypes::DEPLOYMENT->value) { @@ -37,6 +39,7 @@ class RunRemoteProcess } $this->activity = $activity; + $this->hideFromOutput = $hideFromOutput; } public function __invoke(): ProcessResult @@ -55,7 +58,7 @@ class RunRemoteProcess $this->activity->properties = $this->activity->properties->merge([ 'exitCode' => $processResult->exitCode(), - 'stdout' => $processResult->output(), + 'stdout' => $this->hideFromOutput || $processResult->output(), 'stderr' => $processResult->errorOutput(), 'status' => $status, ]); @@ -78,6 +81,9 @@ class RunRemoteProcess protected function handleOutput(string $type, string $output) { + if ($this->hideFromOutput) { + return; + } $this->currentTime = $this->elapsedTime(); if ($type === 'out') { diff --git a/app/Jobs/DeployApplicationJob.php b/app/Jobs/DeployApplicationJob.php index 1b5d83bcd..d9549a723 100644 --- a/app/Jobs/DeployApplicationJob.php +++ b/app/Jobs/DeployApplicationJob.php @@ -31,6 +31,8 @@ class DeployApplicationJob implements ShouldQueue protected $destination; protected $source; protected Activity $activity; + protected array $command = []; + protected string $git_commit; /** * Create a new job instance. @@ -38,16 +40,16 @@ class DeployApplicationJob implements ShouldQueue public function __construct( public string $deployment_uuid, public string $application_uuid, - ){ + ) { $this->application = Application::query() ->where('uuid', $this->application_uuid) ->firstOrFail(); $this->destination = $this->application->destination->getMorphClass()::where('id', $this->application->destination->id)->first(); - $private_key_location = savePrivateKey($this->destination->server); - $server = $this->destination->server; + $private_key_location = savePrivateKey($server); + $remoteProcessArgs = new RemoteProcessArgs( server_ip: $server->ip, private_key_location: $private_key_location, @@ -91,60 +93,32 @@ class DeployApplicationJob implements ShouldQueue $workdir = "/artifacts/{$this->deployment_uuid}"; // Start build process - $this->command[] = "echo 'Starting deployment of {$this->application->git_repository}:{$this->application->git_branch}...'"; - $this->command[] = "echo -n 'Pulling latest version of the builder image (ghcr.io/coollabsio/coolify-builder)... '"; - $this->start_builder_container(); - $this->command[] = "echo 'Done.'"; - $this->command[] = "echo -n 'Importing {$this->application->git_repository}:{$this->application->git_branch} to {$workdir}... '"; + $command_git_clone[] = "echo 'Starting deployment of {$this->application->git_repository}:{$this->application->git_branch}...'"; + $command_git_clone[] = "echo -n 'Pulling latest version of the builder image (ghcr.io/coollabsio/coolify-builder)... '"; + $command_git_clone[] = "docker run --pull=always -d --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/coollabsio/coolify-builder >/dev/null 2>&1"; + $command_git_clone[] = "echo 'Done.'"; + $command_git_clone[] = "echo -n 'Importing {$this->application->git_repository}:{$this->application->git_branch} to {$workdir}... '"; if ($this->application->source->getMorphClass() == 'App\Models\GithubApp') { if ($this->source->is_public) { - $this->execute_in_builder("git clone -q -b {$this->application->git_branch} {$this->source->html_url}/{$this->application->git_repository}.git {$workdir}"); + $command_git_clone[] = $this->execute_in_builder("git clone -q -b {$this->application->git_branch} {$this->source->html_url}/{$this->application->git_repository}.git {$workdir}"); } else { $github_access_token = $this->generate_jwt_token_for_github(); - $this->execute_in_builder("git clone -q -b {$this->application->git_branch} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$this->application->git_repository}.git {$workdir}"); + $command_git_clone[] = $this->execute_in_builder("git clone -q -b {$this->application->git_branch} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$this->application->git_repository}.git {$workdir}"); } } - $this->command[] = "echo 'Done.'"; + $command_git_clone[] = "echo 'Done.'"; + $this->executeNow($command_git_clone, 'git_clone'); // Export git commit to a file - $this->command[] = "echo -n 'Checking commit sha... '"; + $this->executeNow([$this->execute_in_builder("cd {$workdir} && git rev-parse HEAD")], 'commit_sha', hideFromOutput: true); + $this->git_commit = $this->activity->properties->get('commit_sha'); - $this->executeNow($this->command); - - ray($this->activity); - - return; - - // @TODO execute - - $fullOutput = $this->execute_in_builder("cd {$workdir} && git rev-parse HEAD"); - - // @TODO - // Run remote thing - // Get output - // Parse output - // Compare Commit_SHA in PHP, and decide if to stop or not - - - $this->command[] = "echo 'Done.'"; - // Remove .git folder - $this->command[] = "echo -n 'Removing .git folder... '"; - $this->execute_in_builder("rm -fr {$workdir}/.git"); - $this->command[] = "echo 'Done.'"; - - - $comparison = false; // work the output - if (! $comparison) { - - return; - } + $this->executeNow([$this->execute_in_builder("rm -fr {$workdir}/.git")], 'remote_git', hideFromOutput: true); // Create docker-compose.yml && replace TAG with git commit - $docker_compose_base64 = base64_encode($this->generate_docker_compose($this->application)); - $this->execute_in_builder("echo '{$docker_compose_base64}' | base64 -d > {$workdir}/docker-compose.yml"); - $this->execute_in_builder("sed -i \"s/TAG/$(cat {$workdir}/.git-commit)/g\" {$workdir}/docker-compose.yml"); - - - $this->command[] = "echo -n 'Generating nixpacks configuration... '"; + $docker_compose_base64 = base64_encode($this->generate_docker_compose()); + $this->executeNow([$this->execute_in_builder("echo '{$docker_compose_base64}' | base64 -d > {$workdir}/docker-compose.yml")], 'docker_compose'); + $this->executeNow([$this->execute_in_builder("cat {$workdir}/docker-compose.yml")], 'docker_compose_content'); + $command_other_command[] = "echo -n 'Generating nixpacks configuration... '"; if (str_starts_with($this->application->base_image, 'apache') || str_starts_with($this->application->base_image, 'nginx')) { // @TODO: Add static site builds } else { @@ -159,41 +133,38 @@ class DeployApplicationJob implements ShouldQueue $nixpacks_command .= " --start-cmd '{$this->application->start_command}'"; } $nixpacks_command .= " {$workdir}"; - $this->execute_in_builder($nixpacks_command); - $this->execute_in_builder("cp {$workdir}/.nixpacks/Dockerfile {$workdir}/Dockerfile"); - $this->execute_in_builder("rm -f {$workdir}/.nixpacks/Dockerfile"); + $command_other_command[] = $this->execute_in_builder($nixpacks_command); + $command_other_command[] = $this->execute_in_builder("cp {$workdir}/.nixpacks/Dockerfile {$workdir}/Dockerfile"); + $command_other_command[] = $this->execute_in_builder("rm -f {$workdir}/.nixpacks/Dockerfile"); } - $this->command[] = "echo 'Done.'"; - $this->command[] = "echo -n 'Building image... '"; + $command_other_command[] = "echo 'Done.'"; + $command_other_command[] = "echo -n 'Building image... '"; - $this->execute_in_builder("docker build -f {$workdir}/Dockerfile --build-arg SOURCE_COMMIT=$(cat {$workdir}/.git-commit) --progress plain -t {$this->application->uuid}:$(cat {$workdir}/.git-commit) {$workdir}"); - $this->command[] = "echo 'Done.'"; - $this->execute_in_builder("docker rm -f {$this->application->uuid} >/dev/null 2>&1"); + $command_other_command[] = $this->execute_in_builder("docker build -f {$workdir}/Dockerfile --build-arg SOURCE_COMMIT={$this->git_commit} --progress plain -t {$this->application->uuid}:{$this->git_commit} {$workdir}"); + $command_other_command[] = "echo 'Done.'"; + $command_other_command[] = $this->execute_in_builder("docker rm -f {$this->application->uuid} >/dev/null 2>&1"); - $this->command[] = "echo -n 'Deploying... '"; + $command_other_command[] = "echo -n 'Deploying... '"; - $this->execute_in_builder("docker compose --project-directory {$workdir} up -d"); - $this->command[] = "echo 'Done. 🎉'"; - $this->command[] = "docker stop -t 0 {$this->deployment_uuid} >/dev/null"; - } - - private function start_builder_container() - { - $this->command[] = "docker run --pull=always -d --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/coollabsio/coolify-builder >/dev/null 2>&1"; + $command_other_command[] = $this->execute_in_builder("docker compose --project-directory {$workdir} up -d"); + $command_other_command[] = "echo 'Done. 🎉'"; + $command_other_command[] = "docker stop -t 0 {$this->deployment_uuid} >/dev/null"; + $this->executeNow($command_other_command, 'other_command'); } private function execute_in_builder(string $command) { - return $this->command[] = "docker exec {$this->deployment_uuid} bash -c '{$command}'"; + return "docker exec {$this->deployment_uuid} bash -c '{$command}'"; } private function generate_docker_compose() { + $docker_compose = [ 'version' => '3.8', 'services' => [ $this->application->uuid => [ - 'image' => "{$this->application->uuid}:TAG", + 'image' => "{$this->application->uuid}:$this->git_commit", 'container_name' => $this->application->uuid, 'restart' => 'always', 'labels' => $this->set_labels_for_applications(), @@ -293,19 +264,25 @@ class DeployApplicationJob implements ShouldQueue return $labels; } - private function executeNow($command) + private function executeNow(array $command, string $propertyName, bool $hideFromOutput = false) { $commandText = collect($command)->implode("\n"); - $currentProperties = $this->activity->properties; - $currentProperties->put('command', $commandText); - $this->activity->properties = $currentProperties; + $this->activity->properties = $this->activity->properties->merge([ + 'command' => $commandText, + ]); $this->activity->save(); $remoteProcess = resolve(RunRemoteProcess::class, [ 'activity' => $this->activity, + 'hideFromOutput' => $hideFromOutput, ]); - $remoteProcess(); + $result = $remoteProcess(); + + $this->activity->properties = $this->activity->properties->merge([ + $propertyName => trim($result->output()), + ]); + $this->activity->save(); } } From f246d06629356167e390eb861ca27c736df97f4d Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Fri, 31 Mar 2023 15:51:50 +0200 Subject: [PATCH 5/7] Wip - not working --- app/Jobs/DeployApplicationJob.php | 153 +++++++++++++++++------------- 1 file changed, 86 insertions(+), 67 deletions(-) diff --git a/app/Jobs/DeployApplicationJob.php b/app/Jobs/DeployApplicationJob.php index d9549a723..b5db82c7e 100644 --- a/app/Jobs/DeployApplicationJob.php +++ b/app/Jobs/DeployApplicationJob.php @@ -31,8 +31,8 @@ class DeployApplicationJob implements ShouldQueue protected $destination; protected $source; protected Activity $activity; - protected array $command = []; protected string $git_commit; + protected string $workdir; /** * Create a new job instance. @@ -75,11 +75,6 @@ class DeployApplicationJob implements ShouldQueue $coolify_instance_settings = CoolifyInstanceSettings::find(1); $this->source = $this->application->source->getMorphClass()::where('id', $this->application->source->id)->first(); - $source_html_url = data_get($this->application, 'source.html_url'); - $url = parse_url(filter_var($source_html_url, FILTER_SANITIZE_URL)); - $source_html_url_host = $url['host']; - $source_html_url_scheme = $url['scheme']; - // Get Wildcard Domain $project_wildcard_domain = data_get($this->application, 'environment.project.settings.wildcard_domain'); $global_wildcard_domain = data_get($coolify_instance_settings, 'wildcard_domain'); @@ -90,66 +85,49 @@ class DeployApplicationJob implements ShouldQueue $this->application->fqdn = $this->application->uuid . '.' . $wildcard_domain; $this->application->save(); } - $workdir = "/artifacts/{$this->deployment_uuid}"; + $this->workdir = "/artifacts/{$this->deployment_uuid}"; - // Start build process - $command_git_clone[] = "echo 'Starting deployment of {$this->application->git_repository}:{$this->application->git_branch}...'"; - $command_git_clone[] = "echo -n 'Pulling latest version of the builder image (ghcr.io/coollabsio/coolify-builder)... '"; - $command_git_clone[] = "docker run --pull=always -d --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/coollabsio/coolify-builder >/dev/null 2>&1"; - $command_git_clone[] = "echo 'Done.'"; - $command_git_clone[] = "echo -n 'Importing {$this->application->git_repository}:{$this->application->git_branch} to {$workdir}... '"; - if ($this->application->source->getMorphClass() == 'App\Models\GithubApp') { - if ($this->source->is_public) { - $command_git_clone[] = $this->execute_in_builder("git clone -q -b {$this->application->git_branch} {$this->source->html_url}/{$this->application->git_repository}.git {$workdir}"); - } else { - $github_access_token = $this->generate_jwt_token_for_github(); - $command_git_clone[] = $this->execute_in_builder("git clone -q -b {$this->application->git_branch} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$this->application->git_repository}.git {$workdir}"); - } - } - $command_git_clone[] = "echo 'Done.'"; - $this->executeNow($command_git_clone, 'git_clone'); - // Export git commit to a file - $this->executeNow([$this->execute_in_builder("cd {$workdir} && git rev-parse HEAD")], 'commit_sha', hideFromOutput: true); + // Pull builder image + $this->executeNow([ + "echo 'Starting deployment of {$this->application->git_repository}:{$this->application->git_branch}...", + "echo -n 'Pulling latest version of the builder image (ghcr.io/coollabsio/coolify-builder)... '", + "docker run --pull=always -d --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/coollabsio/coolify-builder >/dev/null 2>&1", + "echo 'Done.'", + ], 'docker_pull_builder_image'); + + // Import git repository + $this->executeNow([ + "echo -n 'Importing {$this->application->git_repository}:{$this->application->git_branch} to {$this->workdir}... '", + $this->gitImport(), + "echo 'Done.'" + ], 'importing_git_repository'); + + // Get git commit + $this->executeNow([$this->execute_in_builder("cd {$this->workdir} && git rev-parse HEAD")], 'commit_sha', hideFromOutput: true); $this->git_commit = $this->activity->properties->get('commit_sha'); - $this->executeNow([$this->execute_in_builder("rm -fr {$workdir}/.git")], 'remote_git', hideFromOutput: true); + $this->executeNow([ + $this->execute_in_builder("rm -fr {$this->workdir}/.git") + ], hideFromOutput: true); - // Create docker-compose.yml && replace TAG with git commit $docker_compose_base64 = base64_encode($this->generate_docker_compose()); - $this->executeNow([$this->execute_in_builder("echo '{$docker_compose_base64}' | base64 -d > {$workdir}/docker-compose.yml")], 'docker_compose'); - $this->executeNow([$this->execute_in_builder("cat {$workdir}/docker-compose.yml")], 'docker_compose_content'); - $command_other_command[] = "echo -n 'Generating nixpacks configuration... '"; - if (str_starts_with($this->application->base_image, 'apache') || str_starts_with($this->application->base_image, 'nginx')) { - // @TODO: Add static site builds - } else { - $nixpacks_command = "nixpacks build -o {$workdir} --no-error-without-start"; - if ($this->application->install_command) { - $nixpacks_command .= " --install-cmd '{$this->application->install_command}'"; - } - if ($this->application->build_command) { - $nixpacks_command .= " --build-cmd '{$this->application->build_command}'"; - } - if ($this->application->start_command) { - $nixpacks_command .= " --start-cmd '{$this->application->start_command}'"; - } - $nixpacks_command .= " {$workdir}"; - $command_other_command[] = $this->execute_in_builder($nixpacks_command); - $command_other_command[] = $this->execute_in_builder("cp {$workdir}/.nixpacks/Dockerfile {$workdir}/Dockerfile"); - $command_other_command[] = $this->execute_in_builder("rm -f {$workdir}/.nixpacks/Dockerfile"); - } - $command_other_command[] = "echo 'Done.'"; - $command_other_command[] = "echo -n 'Building image... '"; + $this->executeNow([ + $this->execute_in_builder("echo '{$docker_compose_base64}' | base64 -d > {$this->workdir}/docker-compose.yml") + ], hideFromOutput: true); - $command_other_command[] = $this->execute_in_builder("docker build -f {$workdir}/Dockerfile --build-arg SOURCE_COMMIT={$this->git_commit} --progress plain -t {$this->application->uuid}:{$this->git_commit} {$workdir}"); - $command_other_command[] = "echo 'Done.'"; - $command_other_command[] = $this->execute_in_builder("docker rm -f {$this->application->uuid} >/dev/null 2>&1"); - - $command_other_command[] = "echo -n 'Deploying... '"; - - $command_other_command[] = $this->execute_in_builder("docker compose --project-directory {$workdir} up -d"); - $command_other_command[] = "echo 'Done. 🎉'"; - $command_other_command[] = "docker stop -t 0 {$this->deployment_uuid} >/dev/null"; - $this->executeNow($command_other_command, 'other_command'); + $this->executeNow([ + "echo -n 'Generating nixpacks configuration... '", + $this->getNixpacks(), + "echo 'Done.'", + "echo -n 'Building image... '", + $this->execute_in_builder("docker build -f {$this->workdir}/Dockerfile --build-arg SOURCE_COMMIT={$this->git_commit} --progress plain -t {$this->application->uuid}:{$this->git_commit} {$this->workdir}"), + "echo 'Done.'", + $this->execute_in_builder("docker rm -f {$this->application->uuid} >/dev/null 2>&1"), + "echo -n 'Deploying... '", + $this->execute_in_builder("docker compose --project-directory {$this->workdir} up -d"), + "echo 'Done. 🎉'", + "docker stop -t 0 {$this->deployment_uuid} >/dev/null" + ]); } private function execute_in_builder(string $command) @@ -264,9 +242,10 @@ class DeployApplicationJob implements ShouldQueue return $labels; } - private function executeNow(array $command, string $propertyName, bool $hideFromOutput = false) + private function executeNow(array $command, string $propertyName = null, bool $hideFromOutput = false) { $commandText = collect($command)->implode("\n"); + dd($commandText); $this->activity->properties = $this->activity->properties->merge([ 'command' => $commandText, @@ -278,11 +257,51 @@ class DeployApplicationJob implements ShouldQueue 'hideFromOutput' => $hideFromOutput, ]); - $result = $remoteProcess(); - - $this->activity->properties = $this->activity->properties->merge([ - $propertyName => trim($result->output()), - ]); - $this->activity->save(); + if ($propertyName) { + $result = $remoteProcess(); + $this->activity->properties = $this->activity->properties->merge([ + $propertyName => trim($result->output()), + ]); + $this->activity->save(); + } else { + $remoteProcess(); + } + } + private function gitImport() + { + $source_html_url = data_get($this->application, 'source.html_url'); + $url = parse_url(filter_var($source_html_url, FILTER_SANITIZE_URL)); + $source_html_url_host = $url['host']; + $source_html_url_scheme = $url['scheme']; + if ($this->application->source->getMorphClass() == 'App\Models\GithubApp') { + if ($this->source->is_public) { + return $this->execute_in_builder("git clone -q -b {$this->application->git_branch} {$this->source->html_url}/{$this->application->git_repository}.git {$this->workdir}"); + } else { + $github_access_token = $this->generate_jwt_token_for_github(); + return $this->execute_in_builder("git clone -q -b {$this->application->git_branch} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$this->application->git_repository}.git {$this->workdir}"); + } + } + } + private function getNixpacks() + { + if (str_starts_with($this->application->base_image, 'apache') || str_starts_with($this->application->base_image, 'nginx')) { + // @TODO: Add static site builds + } else { + $nixpacks_command = "nixpacks build -o {$this->workdir} --no-error-without-start"; + if ($this->application->install_command) { + $nixpacks_command .= " --install-cmd '{$this->application->install_command}'"; + } + if ($this->application->build_command) { + $nixpacks_command .= " --build-cmd '{$this->application->build_command}'"; + } + if ($this->application->start_command) { + $nixpacks_command .= " --start-cmd '{$this->application->start_command}'"; + } + $nixpacks_command .= " {$this->workdir}"; + $command_other_command[] = $this->execute_in_builder($nixpacks_command); + $command_other_command[] = $this->execute_in_builder("cp {$this->workdir}/.nixpacks/Dockerfile {$this->workdir}/Dockerfile"); + $command_other_command[] = $this->execute_in_builder("rm -f {$this->workdir}/.nixpacks/Dockerfile"); + } + return $command_other_command; } } From 8c94bcb27bb4b9d7e272128d71bdd5732fc55085 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Fri, 31 Mar 2023 16:39:15 +0200 Subject: [PATCH 6/7] fix --- app/Jobs/DeployApplicationJob.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/Jobs/DeployApplicationJob.php b/app/Jobs/DeployApplicationJob.php index b5db82c7e..23834bec0 100644 --- a/app/Jobs/DeployApplicationJob.php +++ b/app/Jobs/DeployApplicationJob.php @@ -89,7 +89,7 @@ class DeployApplicationJob implements ShouldQueue // Pull builder image $this->executeNow([ - "echo 'Starting deployment of {$this->application->git_repository}:{$this->application->git_branch}...", + "echo 'Starting deployment of {$this->application->git_repository}:{$this->application->git_branch}...'", "echo -n 'Pulling latest version of the builder image (ghcr.io/coollabsio/coolify-builder)... '", "docker run --pull=always -d --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/coollabsio/coolify-builder >/dev/null 2>&1", "echo 'Done.'", @@ -245,7 +245,6 @@ class DeployApplicationJob implements ShouldQueue private function executeNow(array $command, string $propertyName = null, bool $hideFromOutput = false) { $commandText = collect($command)->implode("\n"); - dd($commandText); $this->activity->properties = $this->activity->properties->merge([ 'command' => $commandText, From 304087a24d7e8dcef897f6a1229e6773c49f2abe Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Fri, 31 Mar 2023 16:47:27 +0200 Subject: [PATCH 7/7] fix again --- app/Jobs/DeployApplicationJob.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/Jobs/DeployApplicationJob.php b/app/Jobs/DeployApplicationJob.php index 23834bec0..647fb1ff1 100644 --- a/app/Jobs/DeployApplicationJob.php +++ b/app/Jobs/DeployApplicationJob.php @@ -117,7 +117,9 @@ class DeployApplicationJob implements ShouldQueue $this->executeNow([ "echo -n 'Generating nixpacks configuration... '", - $this->getNixpacks(), + $this->nixpacks_build_cmd(), + $this->execute_in_builder("cp {$this->workdir}/.nixpacks/Dockerfile {$this->workdir}/Dockerfile"), + $this->execute_in_builder("rm -f {$this->workdir}/.nixpacks/Dockerfile"), "echo 'Done.'", "echo -n 'Building image... '", $this->execute_in_builder("docker build -f {$this->workdir}/Dockerfile --build-arg SOURCE_COMMIT={$this->git_commit} --progress plain -t {$this->application->uuid}:{$this->git_commit} {$this->workdir}"), @@ -281,7 +283,7 @@ class DeployApplicationJob implements ShouldQueue } } } - private function getNixpacks() + private function nixpacks_build_cmd() { if (str_starts_with($this->application->base_image, 'apache') || str_starts_with($this->application->base_image, 'nginx')) { // @TODO: Add static site builds @@ -297,10 +299,7 @@ class DeployApplicationJob implements ShouldQueue $nixpacks_command .= " --start-cmd '{$this->application->start_command}'"; } $nixpacks_command .= " {$this->workdir}"; - $command_other_command[] = $this->execute_in_builder($nixpacks_command); - $command_other_command[] = $this->execute_in_builder("cp {$this->workdir}/.nixpacks/Dockerfile {$this->workdir}/Dockerfile"); - $command_other_command[] = $this->execute_in_builder("rm -f {$this->workdir}/.nixpacks/Dockerfile"); } - return $command_other_command; + return $this->execute_in_builder($nixpacks_command); } }