From 4df66ebf009e524ed5db668f686432ffc89dfe80 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 28 Mar 2023 15:47:37 +0200 Subject: [PATCH 01/13] wip --- .env.example | 1 - app/Http/Livewire/DemoDeployApplication.php | 71 +++++++++++++++++++ app/Http/Livewire/RunCommand.php | 4 +- app/Models/Application.php | 8 +++ app/Models/ApplicationSetting.php | 9 +++ app/Models/CoolifyInstanceSettings.php | 11 +++ app/Models/Database.php | 4 ++ app/Models/Deployment.php | 24 +++++++ app/Models/Environment.php | 4 ++ app/Models/StandaloneDocker.php | 4 ++ app/Traits/Shared.php | 14 ++++ bootstrap/helpers.php | 15 ++-- ...create_coolify_instance_settings_table.php | 41 +++++++++++ ...03_27_081716_create_applications_table.php | 39 ++++++++++ ...1717_create_application_settings_table.php | 37 ++++++++++ ..._03_27_083622_create_deployments_table.php | 30 ++++++++ database/seeders/ApplicationSeeder.php | 24 ++++--- .../seeders/ApplicationSettingsSeeder.php | 26 +++++++ .../seeders/CoolifyInstanceSettingsSeeder.php | 28 ++++++++ database/seeders/DatabaseSeeder.php | 2 + database/seeders/ServerSeeder.php | 17 +++-- database/seeders/StandaloneDockerSeeder.php | 4 +- resources/views/home.blade.php | 3 +- .../demo-deploy-application.blade.php | 13 ++++ 24 files changed, 408 insertions(+), 25 deletions(-) create mode 100644 app/Http/Livewire/DemoDeployApplication.php create mode 100644 app/Models/ApplicationSetting.php create mode 100644 app/Models/CoolifyInstanceSettings.php create mode 100644 app/Models/Deployment.php create mode 100644 app/Traits/Shared.php create mode 100644 database/migrations/2023_03_20_112813_create_coolify_instance_settings_table.php create mode 100644 database/migrations/2023_03_27_081717_create_application_settings_table.php create mode 100644 database/migrations/2023_03_27_083622_create_deployments_table.php create mode 100644 database/seeders/ApplicationSettingsSeeder.php create mode 100644 database/seeders/CoolifyInstanceSettingsSeeder.php create mode 100644 resources/views/livewire/demo-deploy-application.blade.php diff --git a/.env.example b/.env.example index dd4f03d08..f24762fab 100644 --- a/.env.example +++ b/.env.example @@ -8,7 +8,6 @@ GROUPID= ############################################################################################################ APP_NAME=Laravel -APP_SERVICE=php APP_ENV=local APP_KEY= APP_DEBUG=true diff --git a/app/Http/Livewire/DemoDeployApplication.php b/app/Http/Livewire/DemoDeployApplication.php new file mode 100644 index 000000000..7b7308dd2 --- /dev/null +++ b/app/Http/Livewire/DemoDeployApplication.php @@ -0,0 +1,71 @@ +isKeepAliveOn = true; + + $this->coolify_instance_settings = CoolifyInstanceSettings::find(1); + $this->application = Application::where('uuid', $this->application_uuid)->first(); + $this->destination = $this->application->destination->getMorphClass()::where('id', $this->application->destination->id)->first(); + $project_wildcard_domain = data_get($this->application, 'environment.project.settings.wildcard_domain'); + $global_wildcard_domain = data_get($this->coolify_instance_settings, 'wildcard_domain'); + $this->wildcard_domain = $project_wildcard_domain ?? $global_wildcard_domain ?? null; + + $source = $this->application->source->getMorphClass()::where('id', $this->application->source->id)->first(); + $deployment_id = new Cuid2(10); + + $workdir = $this->get_workdir('application', $this->application->uuid, $deployment_id); + + $command[] = "echo 'Starting deployment of {$this->application->name} ({$this->application->uuid})'"; + $command[] = 'mkdirs -p ' . $workdir; + $command[] = "git clone -b {$this->application->git_branch} {$source->html_url}/{$this->application->git_repository}.git {$workdir}"; + + if (!file_exists($workdir) && $workdir != "/") { + $command[] = "echo 'Removing {$workdir}'"; + $command[] = "rm -rf {$workdir}"; + } + $this->activity = remoteProcess(implode("\n", $command), $this->destination->server->name); + + Deployment::create([ + 'uuid' => $deployment_id, + 'type_id' => $this->application->id, + 'type_type' => Application::class, + 'activity_log_id' => $this->activity->id, + ]); + } + public function polling() + { + $this->activity?->refresh(); + if (data_get($this->activity, 'properties.exitCode') !== null) { + $this->isKeepAliveOn = false; + } + } + public function render() + { + return view('livewire.demo-deploy-application'); + } +} diff --git a/app/Http/Livewire/RunCommand.php b/app/Http/Livewire/RunCommand.php index f29b5a29d..3e4db9e97 100755 --- a/app/Http/Livewire/RunCommand.php +++ b/app/Http/Livewire/RunCommand.php @@ -15,13 +15,15 @@ class RunCommand extends Component public $command = 'ls'; - public $server = 'testing-host'; + public $server; public $servers = []; public function mount() { $this->servers = Server::all()->pluck('name')->toArray(); + $this->server = $this->servers[0]; + } public function render() { diff --git a/app/Models/Application.php b/app/Models/Application.php index 8f8c4391b..78d3d9c96 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -8,6 +8,10 @@ public function environment() { return $this->belongsTo(Environment::class); } + public function settings() + { + return $this->hasOne(ApplicationSetting::class); + } public function destination() { return $this->morphTo(); @@ -16,4 +20,8 @@ public function source() { return $this->morphTo(); } + public function deployments() + { + return $this->morphMany(Deployment::class, 'type'); + } } diff --git a/app/Models/ApplicationSetting.php b/app/Models/ApplicationSetting.php new file mode 100644 index 000000000..d5f9b6f4e --- /dev/null +++ b/app/Models/ApplicationSetting.php @@ -0,0 +1,9 @@ +morphTo(); } + public function deployments() + { + return $this->morphMany(Deployment::class, 'type'); + } } diff --git a/app/Models/Deployment.php b/app/Models/Deployment.php new file mode 100644 index 000000000..c8751e1fe --- /dev/null +++ b/app/Models/Deployment.php @@ -0,0 +1,24 @@ +morphTo(); + } + public function activity() + { + return $this->belongsTo(Activity::class, 'activity_log_id'); + } +} diff --git a/app/Models/Environment.php b/app/Models/Environment.php index 86e784ef2..29f34208b 100644 --- a/app/Models/Environment.php +++ b/app/Models/Environment.php @@ -4,6 +4,10 @@ class Environment extends BaseModel { + public function project() + { + return $this->belongsTo(Project::class); + } public function applications() { return $this->hasMany(Application::class); diff --git a/app/Models/StandaloneDocker.php b/app/Models/StandaloneDocker.php index 5d6f8ef4e..05724fc54 100644 --- a/app/Models/StandaloneDocker.php +++ b/app/Models/StandaloneDocker.php @@ -8,4 +8,8 @@ public function applications() { return $this->morphMany(Application::class, 'destination'); } + public function server() + { + return $this->belongsTo(Server::class); + } } diff --git a/app/Traits/Shared.php b/app/Traits/Shared.php new file mode 100644 index 000000000..c6884a7a0 --- /dev/null +++ b/app/Traits/Shared.php @@ -0,0 +1,14 @@ +team_id); - $temp_file = 'id.rsa_'.'root'.'@'.$found_server->ip; + $temp_file = 'id.rsa_' . 'root' . '@' . $found_server->ip; Storage::disk('local')->put($temp_file, $found_server->privateKey->private_key, 'private'); - $private_key_location = '/var/www/html/storage/app/'.$temp_file; + $private_key_location = '/var/www/html/storage/app/' . $temp_file; return resolve(DispatchRemoteProcess::class, [ 'remoteProcessArgs' => new RemoteProcessArgs( destination: $found_server->ip, private_key_location: $private_key_location, - command: $command, + command: <<port, user: $found_server->user, ), ])(); } - function checkServer(string $destination){ + function checkServer(string $destination) + { // @TODO: Use UUID instead of name $found_server = Server::where('name', $destination)->first(); if (!$found_server) { @@ -41,11 +44,11 @@ function checkServer(string $destination){ }; return $found_server; } - function checkTeam(string $team_id){ + 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.'); } - } } diff --git a/database/migrations/2023_03_20_112813_create_coolify_instance_settings_table.php b/database/migrations/2023_03_20_112813_create_coolify_instance_settings_table.php new file mode 100644 index 000000000..7c81fb26b --- /dev/null +++ b/database/migrations/2023_03_20_112813_create_coolify_instance_settings_table.php @@ -0,0 +1,41 @@ +id(); + $table->string('fqdn')->nullable(); + $table->string('wildcard_domain')->nullable(); + $table->string('redirect_url')->nullable(); + // $table->string('preview_domain_separator')->default('.'); + $table->integer('public_port_min')->default(9000); + $table->integer('public_port_max')->default(9100); + // $table->string('custom_dns_servers')->default('1.1.1.1,8.8.8.8'); + + $table->boolean('do_not_track')->default(false); + + $table->boolean('is_auto_update_enabled')->default(true); + // $table->boolean('is_dns_check_enabled')->default(true); + $table->boolean('is_registration_enabled')->default(true); + $table->boolean('is_https_forced')->default(true); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('coolify_instance_settings'); + } +}; diff --git a/database/migrations/2023_03_27_081716_create_applications_table.php b/database/migrations/2023_03_27_081716_create_applications_table.php index 5e86f09ce..57515e4fc 100644 --- a/database/migrations/2023_03_27_081716_create_applications_table.php +++ b/database/migrations/2023_03_27_081716_create_applications_table.php @@ -16,8 +16,47 @@ public function up(): void $table->string('uuid')->unique(); $table->string('name'); + $table->string('fqdn')->unique()->nullable(); + $table->string('config_hash')->nullable(); + + $table->string('git_repository'); + $table->string('git_branch'); + $table->string('git_commit_sha')->nullable(); + + $table->string('docker_registry_image_name')->nullable(); + $table->string('docker_registry_image_tag')->nullable(); + + $table->string('build_pack'); + $table->string('base_image')->nullable(); + $table->string('build_image')->nullable(); + + $table->string('install_command')->nullable(); + $table->string('build_command')->nullable(); + $table->string('start_command')->nullable(); + + $table->string('ports_exposes'); + $table->string('ports_mappings')->nullable(); + + $table->string('base_directory')->default('/'); + $table->string('publish_directory')->nullable(); + + $table->string('health_check_path')->nullable(); + $table->string('health_check_port')->nullable(); + $table->string('health_check_host')->default('localhost'); + $table->string('health_check_method')->default('GET'); + $table->integer('health_check_return_code')->default(200); + $table->string('health_check_scheme')->default('http'); + $table->string('health_check_response_text')->nullable(); + $table->integer('health_check_interval')->default(5); + $table->integer('health_check_timeout')->default(5); + $table->integer('health_check_retries')->default(10); + $table->integer('health_check_start_period')->default(5); + + $table->string('status')->default('killed'); + $table->morphs('destination'); $table->morphs('source'); + $table->foreignId('environment_id'); $table->timestamps(); diff --git a/database/migrations/2023_03_27_081717_create_application_settings_table.php b/database/migrations/2023_03_27_081717_create_application_settings_table.php new file mode 100644 index 000000000..dbb94446f --- /dev/null +++ b/database/migrations/2023_03_27_081717_create_application_settings_table.php @@ -0,0 +1,37 @@ +id(); + $table->boolean('is_git_submodules_allowed')->default(true); + $table->boolean('is_git_lfs_allowed')->default(true); + $table->boolean('is_auto_deploy')->default(true); + $table->boolean('is_dual_cert')->default(false); + $table->boolean('is_debug')->default(false); + $table->boolean('is_previews')->default(false); + $table->boolean('is_bot')->default(false); + $table->boolean('is_custom_ssl')->default(false); + $table->boolean('is_http2')->default(false); + $table->foreignId('application_id'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('application_settings'); + } +}; diff --git a/database/migrations/2023_03_27_083622_create_deployments_table.php b/database/migrations/2023_03_27_083622_create_deployments_table.php new file mode 100644 index 000000000..8a0becc33 --- /dev/null +++ b/database/migrations/2023_03_27_083622_create_deployments_table.php @@ -0,0 +1,30 @@ +id(); + $table->string('uuid')->unique(); + $table->morphs('type'); + $table->foreignId('activity_log_id'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('deployments'); + } +}; diff --git a/database/seeders/ApplicationSeeder.php b/database/seeders/ApplicationSeeder.php index 6436edeef..91b954834 100644 --- a/database/seeders/ApplicationSeeder.php +++ b/database/seeders/ApplicationSeeder.php @@ -3,6 +3,7 @@ namespace Database\Seeders; use App\Models\Application; +use App\Models\ApplicationSetting; use App\Models\Environment; use App\Models\GithubApp; use App\Models\StandaloneDocker; @@ -24,20 +25,25 @@ public function run(): void Application::create([ 'id' => 1, 'name' => 'My first application', + 'git_repository' => 'coollabsio/coolify-examples', + 'git_branch' => 'nodejs-fastify', + 'build_pack' => 'nixpacks', + 'ports_exposes' => '3000', + 'ports_mappings' => '3000:3000,3010:3001', 'environment_id' => $environment_1->id, 'destination_id' => $standalone_docker_1->id, 'destination_type' => StandaloneDocker::class, 'source_id' => $github_public_source->id, 'source_type' => GithubApp::class, ]); - Application::create([ - 'id' => 2, - 'name' => 'My second application (Swarm)', - 'environment_id' => $environment_1->id, - 'destination_id' => $swarm_docker_1->id, - 'destination_type' => SwarmDocker::class, - 'source_id' => $github_public_source->id, - 'source_type' => GithubApp::class, - ]); + // Application::create([ + // 'id' => 2, + // 'name' => 'My second application (Swarm)', + // 'environment_id' => $environment_1->id, + // 'destination_id' => $swarm_docker_1->id, + // 'destination_type' => SwarmDocker::class, + // 'source_id' => $github_public_source->id, + // 'source_type' => GithubApp::class, + // ]); } } diff --git a/database/seeders/ApplicationSettingsSeeder.php b/database/seeders/ApplicationSettingsSeeder.php new file mode 100644 index 000000000..760306b07 --- /dev/null +++ b/database/seeders/ApplicationSettingsSeeder.php @@ -0,0 +1,26 @@ + 1, + 'application_id' => $application_1->id, + ]); + } +} diff --git a/database/seeders/CoolifyInstanceSettingsSeeder.php b/database/seeders/CoolifyInstanceSettingsSeeder.php new file mode 100644 index 000000000..8f5625d60 --- /dev/null +++ b/database/seeders/CoolifyInstanceSettingsSeeder.php @@ -0,0 +1,28 @@ + 1, + 'wildcard_domain' => 'coolify.io', + 'is_https_forced' => false, + 'is_registration_enabled' => true, + ]); + } +} diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index c2bf8ac21..00797ca1c 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -9,6 +9,7 @@ class DatabaseSeeder extends Seeder public function run(): void { $this->call([ + CoolifyInstanceSettingsSeeder::class, UserSeeder::class, TeamSeeder::class, PrivateKeySeeder::class, @@ -22,6 +23,7 @@ public function run(): void GithubAppSeeder::class, GitlabAppSeeder::class, ApplicationSeeder::class, + ApplicationSettingsSeeder::class, DBSeeder::class, ServiceSeeder::class, ]); diff --git a/database/seeders/ServerSeeder.php b/database/seeders/ServerSeeder.php index 28f4e42fa..f40912b64 100644 --- a/database/seeders/ServerSeeder.php +++ b/database/seeders/ServerSeeder.php @@ -5,9 +5,7 @@ use App\Models\PrivateKey; use App\Models\Server; use App\Models\Team; -use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; -use Illuminate\Support\Facades\DB; class ServerSeeder extends Seeder { @@ -18,9 +16,10 @@ public function run(): void { $root_team = Team::find(1); $private_key_1 = PrivateKey::find(1); + Server::create([ 'id' => 1, - 'name' => "testing-host", + 'name' => "testing-local-docker-container", 'description' => "This is a test docker container", 'ip' => "coolify-testing-host", 'team_id' => $root_team->id, @@ -28,12 +27,20 @@ public function run(): void ]); Server::create([ 'id' => 2, - 'name' => "testing-host2", + 'name' => "testing-local-docker-container-2", 'description' => "This is a test docker container", 'ip' => "coolify-testing-host-2", 'team_id' => $root_team->id, 'private_key_id' => $private_key_1->id, ]); - + Server::create([ + 'id' => 3, + 'name' => "localhost", + 'description' => "This is the local machine", + 'user' => 'ab', + 'ip' => "172.17.0.1", + 'team_id' => $root_team->id, + 'private_key_id' => $private_key_1->id, + ]); } } diff --git a/database/seeders/StandaloneDockerSeeder.php b/database/seeders/StandaloneDockerSeeder.php index 17b764ba7..082e2647c 100644 --- a/database/seeders/StandaloneDockerSeeder.php +++ b/database/seeders/StandaloneDockerSeeder.php @@ -15,10 +15,10 @@ class StandaloneDockerSeeder extends Seeder */ public function run(): void { - $server_1 = Server::find(1); + $server_3 = Server::find(3); StandaloneDocker::create([ 'id' => 1, - 'server_id' => $server_1->id, + 'server_id' => $server_3->id, ]); } } diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index 031c6a8ce..030b2379b 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -13,9 +13,10 @@

Applications

@forelse ($environment->applications as $application)

{{ $application->name }}

-

Application: {{ $application }}

+

Application: {{ $application->settings }}

Destination Class: {{ $application->destination->getMorphClass() }}

Source Class: {{ $application->source->getMorphClass() }}

+ @empty
  • No application found
  • @endforelse diff --git a/resources/views/livewire/demo-deploy-application.blade.php b/resources/views/livewire/demo-deploy-application.blade.php new file mode 100644 index 000000000..18f781620 --- /dev/null +++ b/resources/views/livewire/demo-deploy-application.blade.php @@ -0,0 +1,13 @@ +
    + @isset($activity?->id) +
    + Activity: {{ $activity?->id ?? 'waiting' }} +
    +
    {{ data_get($activity, 'description') }}
    + {{--
    +
    Details:
    +
    {{ json_encode(data_get($activity, 'properties'), JSON_PRETTY_PRINT) }}
    +
    --}} + @endisset + +
    From 593f1acf10fb06b56690a33e9c6e99f79f1a246c Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 28 Mar 2023 20:59:42 +0200 Subject: [PATCH 02/13] wip --- app/Http/Livewire/DemoDeployApplication.php | 32 ++++++++++++------- bootstrap/helpers.php | 1 - database/seeders/ServerSeeder.php | 2 +- docker/builder/Dockerfile | 25 +++++++++++++++ .../demo-deploy-application.blade.php | 4 --- 5 files changed, 47 insertions(+), 17 deletions(-) create mode 100644 docker/builder/Dockerfile diff --git a/app/Http/Livewire/DemoDeployApplication.php b/app/Http/Livewire/DemoDeployApplication.php index 7b7308dd2..f038a72cb 100644 --- a/app/Http/Livewire/DemoDeployApplication.php +++ b/app/Http/Livewire/DemoDeployApplication.php @@ -19,11 +19,19 @@ class DemoDeployApplication extends Component public Application $application; public $destination; + public string $deployment_id; + public string $workdir; + public CoolifyInstanceSettings $coolify_instance_settings; public $wildcard_domain; + protected $command; + private function dockerPreCommand($command) + { + return $this->command[] = "docker exec {$this->deployment_id} sh -c '{$command}'"; + } public function deploy() { $this->isKeepAliveOn = true; @@ -36,22 +44,24 @@ public function deploy() $this->wildcard_domain = $project_wildcard_domain ?? $global_wildcard_domain ?? null; $source = $this->application->source->getMorphClass()::where('id', $this->application->source->id)->first(); - $deployment_id = new Cuid2(10); + $this->deployment_id = new Cuid2(10); - $workdir = $this->get_workdir('application', $this->application->uuid, $deployment_id); + $this->workdir = "/tmp/{$this->deployment_id}"; - $command[] = "echo 'Starting deployment of {$this->application->name} ({$this->application->uuid})'"; - $command[] = 'mkdirs -p ' . $workdir; - $command[] = "git clone -b {$this->application->git_branch} {$source->html_url}/{$this->application->git_repository}.git {$workdir}"; + $this->command[] = "echo 'Starting deployment of {$this->application->name} ({$this->application->uuid})'"; + $this->command[] = "docker run -d --name {$this->deployment_id} --rm -v /var/run/docker.sock:/var/run/docker.sock coolify-builder >/dev/null"; - if (!file_exists($workdir) && $workdir != "/") { - $command[] = "echo 'Removing {$workdir}'"; - $command[] = "rm -rf {$workdir}"; - } - $this->activity = remoteProcess(implode("\n", $command), $this->destination->server->name); + $this->dockerPreCommand('hostname'); + $this->dockerPreCommand("mkdir -p {$this->workdir}"); + $this->dockerPreCommand("ls -ld {$this->workdir}"); + $this->dockerPreCommand("git clone -b {$this->application->git_branch} {$source->html_url}/{$this->application->git_repository}.git {$this->workdir}"); + $this->dockerPreCommand("ls -l {$this->workdir}"); + $this->command[] = "docker stop -t 0 {$this->deployment_id} >/dev/null"; + + $this->activity = remoteProcess(implode("\n", $this->command), $this->destination->server->name); Deployment::create([ - 'uuid' => $deployment_id, + 'uuid' => $this->deployment_id, 'type_id' => $this->application->id, 'type_type' => Application::class, 'activity_log_id' => $this->activity->id, diff --git a/bootstrap/helpers.php b/bootstrap/helpers.php index cf84362d4..67c650b54 100644 --- a/bootstrap/helpers.php +++ b/bootstrap/helpers.php @@ -22,7 +22,6 @@ function remoteProcess( $temp_file = 'id.rsa_' . 'root' . '@' . $found_server->ip; Storage::disk('local')->put($temp_file, $found_server->privateKey->private_key, 'private'); $private_key_location = '/var/www/html/storage/app/' . $temp_file; - return resolve(DispatchRemoteProcess::class, [ 'remoteProcessArgs' => new RemoteProcessArgs( destination: $found_server->ip, diff --git a/database/seeders/ServerSeeder.php b/database/seeders/ServerSeeder.php index f40912b64..1a6514139 100644 --- a/database/seeders/ServerSeeder.php +++ b/database/seeders/ServerSeeder.php @@ -37,7 +37,7 @@ public function run(): void 'id' => 3, 'name' => "localhost", 'description' => "This is the local machine", - 'user' => 'ab', + 'user' => 'andrasbacsai', 'ip' => "172.17.0.1", 'team_id' => $root_team->id, 'private_key_id' => $private_key_1->id, diff --git a/docker/builder/Dockerfile b/docker/builder/Dockerfile new file mode 100644 index 000000000..16aa2a965 --- /dev/null +++ b/docker/builder/Dockerfile @@ -0,0 +1,25 @@ +FROM alpine:3.17 + +ARG TARGETPLATFORM +# https://download.docker.com/linux/static/stable/ +ARG DOCKER_VERSION=20.10.18 +# https://github.com/docker/compose/releases +# Reverted to 2.6.1 because of this https://github.com/docker/compose/issues/9704. 2.9.0 still has a bug. +ARG DOCKER_COMPOSE_VERSION=2.6.1 +# https://github.com/buildpacks/pack/releases +ARG PACK_VERSION=0.27.0 +# https://github.com/railwayapp/nixpacks/releases +ARG NIXPACKS_VERSION=1.6.0 + +USER root +RUN apk add --no-cache bash curl git git-lfs openssh-client tar tini +RUN mkdir -p ~/.docker/cli-plugins +RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/docker-$DOCKER_VERSION -o /usr/bin/docker +RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/docker-compose-linux-$DOCKER_COMPOSE_VERSION -o ~/.docker/cli-plugins/docker-compose +RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/pack-v$PACK_VERSION -o /usr/local/bin/pack +RUN curl -sSL https://nixpacks.com/install.sh | bash +RUN chmod +x ~/.docker/cli-plugins/docker-compose /usr/bin/docker /usr/local/bin/pack + +ENTRYPOINT ["/sbin/tini", "--"] +CMD ["sh", "-c", "while true; do sleep 1000; done"] + diff --git a/resources/views/livewire/demo-deploy-application.blade.php b/resources/views/livewire/demo-deploy-application.blade.php index 18f781620..628c5de78 100644 --- a/resources/views/livewire/demo-deploy-application.blade.php +++ b/resources/views/livewire/demo-deploy-application.blade.php @@ -4,10 +4,6 @@ Activity: {{ $activity?->id ?? 'waiting' }}
    {{ data_get($activity, 'description') }}
    - {{--
    -
    Details:
    -
    {{ json_encode(data_get($activity, 'properties'), JSON_PRETTY_PRINT) }}
    -
    --}} @endisset From 8e0c1027bbea6b4523a9b7ec70acb923e7c48c20 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 28 Mar 2023 22:13:08 +0200 Subject: [PATCH 03/13] Add coolify-builder image, initials of deployments --- app/Http/Controllers/ProjectController.php | 109 ++++++++++++++++++ app/Http/Livewire/DemoDeployApplication.php | 81 ------------- app/Http/Livewire/DeployApplication.php | 72 ++++++++++++ app/Http/Livewire/PollActivity.php | 27 +++++ app/Traits/Shared.php | 14 --- database/seeders/ServiceSeeder.php | 2 +- docker/builder/Dockerfile | 1 + resources/views/home.blade.php | 48 +------- .../livewire/deploy-application.blade.php | 3 + .../views/livewire/poll-activity.blade.php | 8 ++ resources/views/project/application.blade.php | 6 + resources/views/project/database.blade.php | 5 + resources/views/project/deployment.blade.php | 8 ++ .../views/project/environments.blade.php | 11 ++ resources/views/project/resources.blade.php | 29 +++++ resources/views/project/service.blade.php | 5 + routes/web.php | 10 +- 17 files changed, 299 insertions(+), 140 deletions(-) create mode 100644 app/Http/Controllers/ProjectController.php delete mode 100644 app/Http/Livewire/DemoDeployApplication.php create mode 100644 app/Http/Livewire/DeployApplication.php create mode 100644 app/Http/Livewire/PollActivity.php delete mode 100644 app/Traits/Shared.php create mode 100644 resources/views/livewire/deploy-application.blade.php create mode 100644 resources/views/livewire/poll-activity.blade.php create mode 100644 resources/views/project/application.blade.php create mode 100644 resources/views/project/database.blade.php create mode 100644 resources/views/project/deployment.blade.php create mode 100644 resources/views/project/environments.blade.php create mode 100644 resources/views/project/resources.blade.php create mode 100644 resources/views/project/service.blade.php diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php new file mode 100644 index 000000000..b479a6d19 --- /dev/null +++ b/app/Http/Controllers/ProjectController.php @@ -0,0 +1,109 @@ +route('project_uuid'); + $project = session('currentTeam')->projects->where('uuid', $project_uuid)->first(); + if (!$project) { + return redirect()->route('home'); + } + return view('project.environments', ['project' => $project]); + } + public function resources() + { + $project_uuid = request()->route('project_uuid'); + $project = session('currentTeam')->projects->where('uuid', $project_uuid)->first(); + if (!$project) { + return redirect()->route('home'); + } + return view('project.resources', ['project' => $project]); + } + public function application() + { + $project_uuid = request()->route('project_uuid'); + $environment_name = request()->route('environment_name'); + $application_uuid = request()->route('application_uuid'); + $project = session('currentTeam')->projects->where('uuid', $project_uuid)->first(); + if (!$project) { + return redirect()->route('home'); + } + $environment = $project->environments->where('name', $environment_name)->first(); + if (!$environment) { + return redirect()->route('home'); + } + $application = $environment->applications->where('uuid', $application_uuid)->first(); + if (!$application) { + return redirect()->route('home'); + } + return view('project.application', ['project' => $project, 'application' => $application]); + } + public function database() + { + $project_uuid = request()->route('project_uuid'); + $environment_name = request()->route('environment_name'); + $database_uuid = request()->route('database_uuid'); + $project = session('currentTeam')->projects->where('uuid', $project_uuid)->first(); + if (!$project) { + return redirect()->route('home'); + } + $environment = $project->environments->where('name', $environment_name)->first(); + if (!$environment) { + return redirect()->route('home'); + } + $database = $environment->databases->where('uuid', $database_uuid)->first(); + if (!$database) { + return redirect()->route('home'); + } + + return view('project.database', ['project' => $project, 'database' => $database]); + } + public function service() + { + $project_uuid = request()->route('project_uuid'); + $environment_name = request()->route('environment_name'); + $service_uuid = request()->route('service_uuid'); + + $project = session('currentTeam')->projects->where('uuid', $project_uuid)->first(); + if (!$project) { + return redirect()->route('home'); + } + $environment = $project->environments->where('name', $environment_name)->first(); + if (!$environment) { + return redirect()->route('home'); + } + $service = $environment->services->where('uuid', $service_uuid)->first(); + if (!$service) { + return redirect()->route('home'); + } + + return view('project.service', ['project' => $project, 'service' => $service]); + } + public function deployment() + { + $project_uuid = request()->route('project_uuid'); + $environment_name = request()->route('environment_name'); + $application_uuid = request()->route('application_uuid'); + $deployment_uuid = request()->route('deployment_uuid'); + + $project = session('currentTeam')->projects->where('uuid', $project_uuid)->first(); + if (!$project) { + return redirect()->route('home'); + } + $environment = $project->environments->where('name', $environment_name)->first(); + if (!$environment) { + return redirect()->route('home'); + } + $application = $environment->applications->where('uuid', $application_uuid)->first(); + if (!$application) { + return redirect()->route('home'); + } + $deployment = $application->deployments->where('uuid', $deployment_uuid)->first(); + return view('project.deployment', ['project' => $project, 'deployment' => $deployment]); + } +} diff --git a/app/Http/Livewire/DemoDeployApplication.php b/app/Http/Livewire/DemoDeployApplication.php deleted file mode 100644 index f038a72cb..000000000 --- a/app/Http/Livewire/DemoDeployApplication.php +++ /dev/null @@ -1,81 +0,0 @@ -command[] = "docker exec {$this->deployment_id} sh -c '{$command}'"; - } - public function deploy() - { - $this->isKeepAliveOn = true; - - $this->coolify_instance_settings = CoolifyInstanceSettings::find(1); - $this->application = Application::where('uuid', $this->application_uuid)->first(); - $this->destination = $this->application->destination->getMorphClass()::where('id', $this->application->destination->id)->first(); - $project_wildcard_domain = data_get($this->application, 'environment.project.settings.wildcard_domain'); - $global_wildcard_domain = data_get($this->coolify_instance_settings, 'wildcard_domain'); - $this->wildcard_domain = $project_wildcard_domain ?? $global_wildcard_domain ?? null; - - $source = $this->application->source->getMorphClass()::where('id', $this->application->source->id)->first(); - $this->deployment_id = new Cuid2(10); - - $this->workdir = "/tmp/{$this->deployment_id}"; - - $this->command[] = "echo 'Starting deployment of {$this->application->name} ({$this->application->uuid})'"; - $this->command[] = "docker run -d --name {$this->deployment_id} --rm -v /var/run/docker.sock:/var/run/docker.sock coolify-builder >/dev/null"; - - $this->dockerPreCommand('hostname'); - $this->dockerPreCommand("mkdir -p {$this->workdir}"); - $this->dockerPreCommand("ls -ld {$this->workdir}"); - $this->dockerPreCommand("git clone -b {$this->application->git_branch} {$source->html_url}/{$this->application->git_repository}.git {$this->workdir}"); - $this->dockerPreCommand("ls -l {$this->workdir}"); - $this->command[] = "docker stop -t 0 {$this->deployment_id} >/dev/null"; - - $this->activity = remoteProcess(implode("\n", $this->command), $this->destination->server->name); - - Deployment::create([ - 'uuid' => $this->deployment_id, - 'type_id' => $this->application->id, - 'type_type' => Application::class, - 'activity_log_id' => $this->activity->id, - ]); - } - public function polling() - { - $this->activity?->refresh(); - if (data_get($this->activity, 'properties.exitCode') !== null) { - $this->isKeepAliveOn = false; - } - } - public function render() - { - return view('livewire.demo-deploy-application'); - } -} diff --git a/app/Http/Livewire/DeployApplication.php b/app/Http/Livewire/DeployApplication.php new file mode 100644 index 000000000..367b2d3e7 --- /dev/null +++ b/app/Http/Livewire/DeployApplication.php @@ -0,0 +1,72 @@ +command[] = "docker exec {$this->deployment_uuid} sh -c '{$command}'"; + } + private function start_builder_container() + { + // @TODO: Add --pull=always if the container is published to ghcr.io + $this->command[] = "docker run -d --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock coolify-builder >/dev/null"; + } + public function deploy() + { + $coolify_instance_settings = CoolifyInstanceSettings::find(1); + $application = Application::where('uuid', $this->application_uuid)->first(); + $destination = $application->destination->getMorphClass()::where('id', $application->destination->id)->first(); + $source = $application->source->getMorphClass()::where('id', $application->source->id)->first(); + + // Get Wildcard Domain + $project_wildcard_domain = data_get($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(10); + $workdir = "/artifacts/{$this->deployment_uuid}"; + + // Start build process + $this->command[] = "echo 'Starting deployment of {$application->name} ({$application->uuid})'"; + $this->start_builder_container(); + $this->execute_in_builder('hostname'); + $this->execute_in_builder("git clone -b {$application->git_branch} {$source->html_url}/{$application->git_repository}.git {$workdir}"); + $this->execute_in_builder("ls -l {$workdir}"); + $this->command[] = "docker stop -t 0 {$this->deployment_uuid} >/dev/null"; + + $this->activity = remoteProcess(implode("\n", $this->command), $destination->server->name); + + // Create Deployment + Deployment::create([ + 'uuid' => $this->deployment_uuid, + 'type_id' => $application->id, + 'type_type' => Application::class, + 'activity_log_id' => $this->activity->id, + ]); + // Redirect to deployment page + return redirect()->route('project.deployment', [ + "deployment_uuid" => $this->deployment_uuid, + "project_uuid" => $application->environment->project->uuid, + "environment_name" => $application->environment->name, + "application_uuid" => $application->uuid + ]); + } + public function render() + { + return view('livewire.deploy-application'); + } +} diff --git a/app/Http/Livewire/PollActivity.php b/app/Http/Livewire/PollActivity.php new file mode 100644 index 000000000..c754cca00 --- /dev/null +++ b/app/Http/Livewire/PollActivity.php @@ -0,0 +1,27 @@ +activity = Activity::find($this->activity_log_id); + } + public function polling() + { + $this->activity?->refresh(); + if (data_get($this->activity, 'properties.exitCode') !== null) { + $this->isKeepAliveOn = false; + } + } + public function render() + { + return view('livewire.poll-activity'); + } +} diff --git a/app/Traits/Shared.php b/app/Traits/Shared.php deleted file mode 100644 index c6884a7a0..000000000 --- a/app/Traits/Shared.php +++ /dev/null @@ -1,14 +0,0 @@ - 1, - 'name'=> "My first database", + 'name'=> "My first service", 'environment_id' => $environment_1->id, 'destination_id' => $standalone_docker_1->id, 'destination_type' => StandaloneDocker::class, diff --git a/docker/builder/Dockerfile b/docker/builder/Dockerfile index 16aa2a965..32099603b 100644 --- a/docker/builder/Dockerfile +++ b/docker/builder/Dockerfile @@ -12,6 +12,7 @@ ARG PACK_VERSION=0.27.0 ARG NIXPACKS_VERSION=1.6.0 USER root +WORKDIR /artifacts RUN apk add --no-cache bash curl git git-lfs openssh-client tar tini RUN mkdir -p ~/.docker/cli-plugins RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/docker-$DOCKER_VERSION -o /usr/bin/docker diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index 030b2379b..d0477bc9e 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -1,46 +1,8 @@ -

    - Coolify v4 🎉 -

    Projects

    -
      - @forelse ($projects as $project) -

      {{ $project->name }}

      -

      Project Settings:{{ $project->settings }}

      -

      Environments

      - @forelse ($project->environments as $environment) -

      Environment: {{ $environment->name }}

      -

      Applications

      - @forelse ($environment->applications as $application) -

      {{ $application->name }}

      -

      Application: {{ $application->settings }}

      -

      Destination Class: {{ $application->destination->getMorphClass() }}

      -

      Source Class: {{ $application->source->getMorphClass() }}

      - - @empty -
    • No application found
    • - @endforelse -

      Databases

      - @forelse ($environment->databases as $database) -

      {{ $database->name }}

      -

      Database: {{ $database }}

      -

      Destination Class: {{ $database->destination->getMorphClass() }}

      - @empty -
    • No database found
    • - @endforelse -

      Services

      - @forelse ($environment->services as $service) -

      {{ $service->name }}

      -

      Service: {{ $service }}

      -

      Destination Class: {{ $service->destination->getMorphClass() }}

      - @empty -
    • No service found
    • - @endforelse - @empty -

      No environments found

      - @endforelse - @empty -
    • No projects found
    • - @endforelse -
    + @forelse ($projects as $project) + {{ data_get($project, 'name') }} + @empty +

    No projects found.

    + @endforelse
    diff --git a/resources/views/livewire/deploy-application.blade.php b/resources/views/livewire/deploy-application.blade.php new file mode 100644 index 000000000..14d29bd9c --- /dev/null +++ b/resources/views/livewire/deploy-application.blade.php @@ -0,0 +1,3 @@ +
    + +
    diff --git a/resources/views/livewire/poll-activity.blade.php b/resources/views/livewire/poll-activity.blade.php new file mode 100644 index 000000000..64e8a843d --- /dev/null +++ b/resources/views/livewire/poll-activity.blade.php @@ -0,0 +1,8 @@ +
    + @isset($activity?->id) +
    + Activity: {{ $activity?->id ?? 'waiting' }} +
    +
    {{ data_get($activity, 'description') }}
    + @endisset +
    diff --git a/resources/views/project/application.blade.php b/resources/views/project/application.blade.php new file mode 100644 index 000000000..36ab57146 --- /dev/null +++ b/resources/views/project/application.blade.php @@ -0,0 +1,6 @@ + +

    Application

    +

    Name: {{ $project->name }}

    +

    UUID: {{ $project->uuid }}

    + +
    diff --git a/resources/views/project/database.blade.php b/resources/views/project/database.blade.php new file mode 100644 index 000000000..f256fecb9 --- /dev/null +++ b/resources/views/project/database.blade.php @@ -0,0 +1,5 @@ + +

    Database

    + + +
    diff --git a/resources/views/project/deployment.blade.php b/resources/views/project/deployment.blade.php new file mode 100644 index 000000000..5ca6091fa --- /dev/null +++ b/resources/views/project/deployment.blade.php @@ -0,0 +1,8 @@ + +

    Deployment

    +

    Name: {{ $project->name }}

    +

    UUID: {{ $project->uuid }}

    + +

    Deployment UUID: {{ $deployment->uuid }}

    + +
    diff --git a/resources/views/project/environments.blade.php b/resources/views/project/environments.blade.php new file mode 100644 index 000000000..bf9efb655 --- /dev/null +++ b/resources/views/project/environments.blade.php @@ -0,0 +1,11 @@ + +

    Environments

    + + @foreach ($project->environments as $environment) + + @endforeach +
    diff --git a/resources/views/project/resources.blade.php b/resources/views/project/resources.blade.php new file mode 100644 index 000000000..ec5976d25 --- /dev/null +++ b/resources/views/project/resources.blade.php @@ -0,0 +1,29 @@ + +

    Resources

    + + @foreach ($project->environments as $environment) +
    + @foreach ($environment->applications as $application) +

    + + {{ $application->name }} + +

    + @endforeach + @foreach ($environment->databases as $database) +

    + + {{ $database->name }} + +

    + @endforeach + @foreach ($environment->services as $service) +

    + + {{ $service->name }} + +

    + @endforeach +
    + @endforeach +
    diff --git a/resources/views/project/service.blade.php b/resources/views/project/service.blade.php new file mode 100644 index 000000000..04cbcd4df --- /dev/null +++ b/resources/views/project/service.blade.php @@ -0,0 +1,5 @@ + +

    Service

    + + +
    diff --git a/routes/web.php b/routes/web.php index 18cde30a7..875a117c8 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,6 +1,7 @@ group(function () { - Route::get('/', [HomeController::class, 'show']); + Route::get('/', [HomeController::class, 'show'])->name('home'); + Route::get('/project/{project_uuid}', [ProjectController::class, 'environments'])->name('project.environments'); + Route::get('/project/{project_uuid}/{environment_name}', [ProjectController::class, 'resources'])->name('project.resources'); + Route::get('/project/{project_uuid}/{environment_name}/application/{application_uuid}', [ProjectController::class, 'application'])->name('project.application'); + Route::get('/project/{project_uuid}/{environment_name}/application/{application_uuid}/deployment/{deployment_uuid}', [ProjectController::class, 'deployment'])->name('project.deployment'); + Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}', [ProjectController::class, 'database'])->name('project.database'); + Route::get('/project/{project_uuid}/{environment_name}/service/{service_uuid}', [ProjectController::class, 'service'])->name('project.service'); + Route::get('/profile', function () { return view('profile'); }); From 9019d1484ec5826cbe2c6e04352b17e1a40b991b Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 28 Mar 2023 22:26:12 +0200 Subject: [PATCH 04/13] Use ghcr.io builder image --- app/Http/Livewire/DeployApplication.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/Http/Livewire/DeployApplication.php b/app/Http/Livewire/DeployApplication.php index 367b2d3e7..6df06cc9e 100644 --- a/app/Http/Livewire/DeployApplication.php +++ b/app/Http/Livewire/DeployApplication.php @@ -21,8 +21,7 @@ private function execute_in_builder(string $command) } private function start_builder_container() { - // @TODO: Add --pull=always if the container is published to ghcr.io - $this->command[] = "docker run -d --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock coolify-builder >/dev/null"; + $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"; } public function deploy() { From 78c434458335bdbc5300cbace1fea0a1d0000c45 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 29 Mar 2023 12:27:02 +0200 Subject: [PATCH 05/13] feat: add deployments as activity fix: tests refactor: remoteProcess --- .../RemoteProcess/DispatchRemoteProcess.php | 21 ++++++++--- .../RemoteProcess/RunRemoteProcess.php | 6 ++-- app/Data/RemoteProcessArgs.php | 20 ++++++----- app/Enums/ActivityTypes.php | 2 +- app/Http/Controllers/ProjectController.php | 5 +-- app/Http/Livewire/DeployApplication.php | 9 +++-- app/Http/Livewire/PollActivity.php | 6 +--- app/Http/Livewire/RunCommand.php | 20 +++++------ app/Models/Application.php | 7 ++-- bootstrap/helpers.php | 36 +++++++++---------- database/seeders/ServerSeeder.php | 2 +- .../views/livewire/poll-activity.blade.php | 3 -- .../views/livewire/run-command.blade.php | 9 +---- resources/views/project/deployment.blade.php | 5 ++- tests/Feature/DockerCommandsTest.php | 11 +++--- 15 files changed, 81 insertions(+), 81 deletions(-) diff --git a/app/Actions/RemoteProcess/DispatchRemoteProcess.php b/app/Actions/RemoteProcess/DispatchRemoteProcess.php index 349c847c4..0f9d60192 100644 --- a/app/Actions/RemoteProcess/DispatchRemoteProcess.php +++ b/app/Actions/RemoteProcess/DispatchRemoteProcess.php @@ -10,10 +10,23 @@ class DispatchRemoteProcess { protected Activity $activity; - public function __construct(RemoteProcessArgs $remoteProcessArgs){ - $this->activity = activity() - ->withProperties($remoteProcessArgs->toArray()) - ->log(""); + public function __construct(RemoteProcessArgs $remoteProcessArgs) + { + if ($remoteProcessArgs->model) { + $properties = $remoteProcessArgs->toArray(); + unset($properties['model']); + + $this->activity = activity() + ->withProperties($properties) + ->performedOn($remoteProcessArgs->model) + ->event('deployment') + ->log(""); + } else { + $this->activity = activity() + ->withProperties($remoteProcessArgs->toArray()) + ->event('remote_process') + ->log(""); + } } public function __invoke(): Activity diff --git a/app/Actions/RemoteProcess/RunRemoteProcess.php b/app/Actions/RemoteProcess/RunRemoteProcess.php index da2eeb8bc..14b9190e1 100644 --- a/app/Actions/RemoteProcess/RunRemoteProcess.php +++ b/app/Actions/RemoteProcess/RunRemoteProcess.php @@ -31,7 +31,7 @@ class RunRemoteProcess */ public function __construct(Activity $activity) { - if ($activity->getExtraProperty('type') !== ActivityTypes::COOLIFY_PROCESS->value) { + if ($activity->getExtraProperty('type') !== ActivityTypes::REMOTE_PROCESS->value) { throw new \RuntimeException('Incompatible Activity to run a remote command.'); } @@ -64,7 +64,7 @@ public function __invoke(): ProcessResult protected function getCommand(): string { $user = $this->activity->getExtraProperty('user'); - $destination = $this->activity->getExtraProperty('destination'); + $server_ip = $this->activity->getExtraProperty('server_ip'); $private_key_location = $this->activity->getExtraProperty('private_key_location'); $port = $this->activity->getExtraProperty('port'); $command = $this->activity->getExtraProperty('command'); @@ -80,7 +80,7 @@ protected function getCommand(): string . '-o LogLevel=ERROR ' . '-o ControlMaster=auto -o ControlPersist=yes -o ControlPersist=1m -o ControlPath=/var/www/html/storage/app/.ssh/ssh_mux_%h_%p_%r ' . "-p {$port} " - . "{$user}@{$destination} " + . "{$user}@{$server_ip} " . " 'bash -se' << \\$delimiter" . PHP_EOL . $command . PHP_EOL . $delimiter; diff --git a/app/Data/RemoteProcessArgs.php b/app/Data/RemoteProcessArgs.php index 139b94c71..57c46d813 100644 --- a/app/Data/RemoteProcessArgs.php +++ b/app/Data/RemoteProcessArgs.php @@ -4,17 +4,21 @@ use App\Enums\ActivityTypes; use App\Enums\ProcessStatus; +use Illuminate\Database\Eloquent\Model; use Spatie\LaravelData\Data; class RemoteProcessArgs extends Data { public function __construct( - public string $destination, - public string $private_key_location, - public string $command, - public int $port, - public string $user, - public string $type = ActivityTypes::COOLIFY_PROCESS->value, - public string $status = ProcessStatus::HOLDING->value, - ){} + public Model|null $model, + public string $server_ip, + public string $private_key_location, + public string|null $deployment_uuid, + public string $command, + public int $port, + public string $user, + public string $type = ActivityTypes::REMOTE_PROCESS->value, + public string $status = ProcessStatus::HOLDING->value, + ) { + } } diff --git a/app/Enums/ActivityTypes.php b/app/Enums/ActivityTypes.php index 52a35e2f8..ad1c10c7a 100644 --- a/app/Enums/ActivityTypes.php +++ b/app/Enums/ActivityTypes.php @@ -4,5 +4,5 @@ enum ActivityTypes: string { - case COOLIFY_PROCESS = 'coolify_process'; + case REMOTE_PROCESS = 'remote_process'; } diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php index b479a6d19..43b17430c 100644 --- a/app/Http/Controllers/ProjectController.php +++ b/app/Http/Controllers/ProjectController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use Illuminate\Http\Request; +use Illuminate\Support\Facades\DB; class ProjectController extends Controller { @@ -103,7 +104,7 @@ public function deployment() if (!$application) { return redirect()->route('home'); } - $deployment = $application->deployments->where('uuid', $deployment_uuid)->first(); - return view('project.deployment', ['project' => $project, 'deployment' => $deployment]); + $activity = $application->get_deployment($deployment_uuid); + return view('project.deployment', ['project' => $project, 'activity' => $activity]); } } diff --git a/app/Http/Livewire/DeployApplication.php b/app/Http/Livewire/DeployApplication.php index 6df06cc9e..e6889f1e4 100644 --- a/app/Http/Livewire/DeployApplication.php +++ b/app/Http/Livewire/DeployApplication.php @@ -21,7 +21,7 @@ private function execute_in_builder(string $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"; + $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"; } public function deploy() { @@ -36,18 +36,17 @@ public function deploy() $wildcard_domain = $project_wildcard_domain ?? $global_wildcard_domain ?? null; // Create Deployment ID - $this->deployment_uuid = new Cuid2(10); + $this->deployment_uuid = new Cuid2(12); $workdir = "/artifacts/{$this->deployment_uuid}"; // Start build process $this->command[] = "echo 'Starting deployment of {$application->name} ({$application->uuid})'"; $this->start_builder_container(); - $this->execute_in_builder('hostname'); + // $this->execute_in_builder('hostname'); $this->execute_in_builder("git clone -b {$application->git_branch} {$source->html_url}/{$application->git_repository}.git {$workdir}"); $this->execute_in_builder("ls -l {$workdir}"); $this->command[] = "docker stop -t 0 {$this->deployment_uuid} >/dev/null"; - - $this->activity = remoteProcess(implode("\n", $this->command), $destination->server->name); + $this->activity = remoteProcess($this->command, $destination->server, $this->deployment_uuid, $application); // Create Deployment Deployment::create([ diff --git a/app/Http/Livewire/PollActivity.php b/app/Http/Livewire/PollActivity.php index c754cca00..3a7822d8a 100644 --- a/app/Http/Livewire/PollActivity.php +++ b/app/Http/Livewire/PollActivity.php @@ -3,16 +3,12 @@ namespace App\Http\Livewire; use Livewire\Component; -use Spatie\Activitylog\Models\Activity; class PollActivity extends Component { public $activity; - public $activity_log_id; public $isKeepAliveOn = true; - public function mount() { - $this->activity = Activity::find($this->activity_log_id); - } + public function polling() { $this->activity?->refresh(); diff --git a/app/Http/Livewire/RunCommand.php b/app/Http/Livewire/RunCommand.php index 3e4db9e97..5bb84a56f 100755 --- a/app/Http/Livewire/RunCommand.php +++ b/app/Http/Livewire/RunCommand.php @@ -19,11 +19,13 @@ class RunCommand extends Component public $servers = []; + protected $rules = [ + 'server' => 'required', + ]; public function mount() { - $this->servers = Server::all()->pluck('name')->toArray(); - $this->server = $this->servers[0]; - + $this->servers = Server::all(); + $this->server = $this->servers[0]->uuid; } public function render() { @@ -33,25 +35,19 @@ public function render() public function runCommand() { $this->isKeepAliveOn = true; - - $this->activity = remoteProcess($this->command, $this->server); + $this->activity = remoteProcess([$this->command], Server::where('uuid', $this->server)->first()); } public function runSleepingBeauty() { $this->isKeepAliveOn = true; - - $this->activity = remoteProcess('x=1; while [ $x -le 40 ]; do sleep 0.1 && echo "Welcome $x times" $(( x++ )); done', $this->server); + $this->activity = remoteProcess(['x=1; while [ $x -le 40 ]; do sleep 0.1 && echo "Welcome $x times" $(( x++ )); done'], Server::where('uuid', $this->server)->first()); } public function runDummyProjectBuild() { $this->isKeepAliveOn = true; - - $this->activity = remoteProcess(<<server); + $this->activity = remoteProcess([' cd projects/dummy-project', 'docker-compose build --no-cache'], Server::where('uuid', $this->server)->first()); } public function polling() diff --git a/app/Models/Application.php b/app/Models/Application.php index 78d3d9c96..afd52ae90 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -2,6 +2,8 @@ namespace App\Models; +use Spatie\Activitylog\Models\Activity; + class Application extends BaseModel { public function environment() @@ -20,8 +22,9 @@ public function source() { return $this->morphTo(); } - public function deployments() + + public function get_deployment(string $deployment_uuid) { - return $this->morphMany(Deployment::class, 'type'); + return Activity::where('subject_id', $this->id)->where('properties->deployment_uuid', '=', $deployment_uuid)->first(); } } diff --git a/bootstrap/helpers.php b/bootstrap/helpers.php index 67c650b54..0b9498831 100644 --- a/bootstrap/helpers.php +++ b/bootstrap/helpers.php @@ -3,6 +3,7 @@ use App\Actions\RemoteProcess\DispatchRemoteProcess; use App\Data\RemoteProcessArgs; use App\Models\Server; +use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Storage; use Spatie\Activitylog\Contracts\Activity; @@ -13,36 +14,33 @@ * */ function remoteProcess( - string $command, - string $destination + array $command, + Server $server, + string|null $deployment_uuid = null, + Model|null $model = null, ): Activity { - $found_server = checkServer($destination); - checkTeam($found_server->team_id); + $command_string = implode("\n", $command); + // @TODO: Check if the user has access to this server + // checkTeam($server->team_id); - $temp_file = 'id.rsa_' . 'root' . '@' . $found_server->ip; - Storage::disk('local')->put($temp_file, $found_server->privateKey->private_key, 'private'); + $temp_file = 'id.rsa_' . 'root' . '@' . $server->ip; + Storage::disk('local')->put($temp_file, $server->privateKey->private_key, 'private'); $private_key_location = '/var/www/html/storage/app/' . $temp_file; + return resolve(DispatchRemoteProcess::class, [ 'remoteProcessArgs' => new RemoteProcessArgs( - destination: $found_server->ip, + model: $model, + server_ip: $server->ip, + deployment_uuid: $deployment_uuid, private_key_location: $private_key_location, command: <<port, - user: $found_server->user, + port: $server->port, + user: $server->user, ), ])(); } - function checkServer(string $destination) - { - // @TODO: Use UUID instead of name - $found_server = Server::where('name', $destination)->first(); - if (!$found_server) { - throw new \RuntimeException('Server not found.'); - }; - return $found_server; - } function checkTeam(string $team_id) { $found_team = auth()->user()->teams->pluck('id')->contains($team_id); diff --git a/database/seeders/ServerSeeder.php b/database/seeders/ServerSeeder.php index 1a6514139..548856ba7 100644 --- a/database/seeders/ServerSeeder.php +++ b/database/seeders/ServerSeeder.php @@ -37,7 +37,7 @@ public function run(): void 'id' => 3, 'name' => "localhost", 'description' => "This is the local machine", - 'user' => 'andrasbacsai', + 'user' => 'root', 'ip' => "172.17.0.1", 'team_id' => $root_team->id, 'private_key_id' => $private_key_1->id, diff --git a/resources/views/livewire/poll-activity.blade.php b/resources/views/livewire/poll-activity.blade.php index 64e8a843d..fc3eeed34 100644 --- a/resources/views/livewire/poll-activity.blade.php +++ b/resources/views/livewire/poll-activity.blade.php @@ -1,8 +1,5 @@
    @isset($activity?->id) -
    - Activity: {{ $activity?->id ?? 'waiting' }} -
    {{ data_get($activity, 'description') }}
    @endisset
    diff --git a/resources/views/livewire/run-command.blade.php b/resources/views/livewire/run-command.blade.php index 6482fc298..085bc9bb7 100755 --- a/resources/views/livewire/run-command.blade.php +++ b/resources/views/livewire/run-command.blade.php @@ -4,7 +4,7 @@ @@ -21,13 +21,6 @@ @endif @isset($activity?->id) -
    - Activity: {{ $activity?->id ?? 'waiting' }} -
    {{ data_get($activity, 'description') }}
    - {{--
    -
    Details:
    -
    {{ json_encode(data_get($activity, 'properties'), JSON_PRETTY_PRINT) }}
    -
    --}} @endisset diff --git a/resources/views/project/deployment.blade.php b/resources/views/project/deployment.blade.php index 5ca6091fa..4d58b00ac 100644 --- a/resources/views/project/deployment.blade.php +++ b/resources/views/project/deployment.blade.php @@ -2,7 +2,6 @@

    Deployment

    Name: {{ $project->name }}

    UUID: {{ $project->uuid }}

    - -

    Deployment UUID: {{ $deployment->uuid }}

    - + + diff --git a/tests/Feature/DockerCommandsTest.php b/tests/Feature/DockerCommandsTest.php index 8fc551a04..a8418efc1 100644 --- a/tests/Feature/DockerCommandsTest.php +++ b/tests/Feature/DockerCommandsTest.php @@ -1,5 +1,6 @@ format('Ymd_his'); - $host = 'testing-host'; + $host = Server::where('name', 'testing-local-docker-container')->first(); // Assert there's no containers start with coolify_test_* - $activity = remoteProcess($areThereCoolifyTestContainers, $host); + $activity = remoteProcess([$areThereCoolifyTestContainers], $host); $containers = Output::containerList($activity->getExtraProperty('stdout')); expect($containers)->toBeEmpty(); // start a container nginx -d --name = $containerName - $activity = remoteProcess("docker run -d --rm --name {$containerName} nginx", $host); + $activity = remoteProcess(["docker run -d --rm --name {$containerName} nginx"], $host); expect($activity->getExtraProperty('exitCode'))->toBe(0); // docker ps name = $container - $activity = remoteProcess($areThereCoolifyTestContainers, $host); + $activity = remoteProcess([$areThereCoolifyTestContainers], $host); $containers = Output::containerList($activity->getExtraProperty('stdout')); expect($containers->where('Names', $containerName)->count())->toBe(1); // Stop testing containers - $activity = remoteProcess("docker stop $(docker ps --filter='name={$coolifyNamePrefix}*' -q)", $host); + $activity = remoteProcess(["docker stop $(docker ps --filter='name={$coolifyNamePrefix}*' -q)"], $host); expect($activity->getExtraProperty('exitCode'))->toBe(0); }); From 1c259fe12eb5338b885432e3db8d43763d38371c Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 29 Mar 2023 12:52:22 +0200 Subject: [PATCH 06/13] fix: shorter cuids (7) feat: show deployments --- .../RemoteProcess/DispatchRemoteProcess.php | 2 -- .../RemoteProcess/RunRemoteProcess.php | 3 ++- app/Enums/ActivityTypes.php | 1 + app/Http/Controllers/ProjectController.php | 2 +- app/Http/Livewire/DeployApplication.php | 19 ++++--------------- app/Models/Application.php | 5 ++++- app/Models/BaseModel.php | 2 +- app/Models/User.php | 2 +- bootstrap/helpers.php | 2 ++ resources/views/project/application.blade.php | 9 +++++++++ routes/web.php | 3 +++ 11 files changed, 28 insertions(+), 22 deletions(-) diff --git a/app/Actions/RemoteProcess/DispatchRemoteProcess.php b/app/Actions/RemoteProcess/DispatchRemoteProcess.php index 0f9d60192..21e40d3a0 100644 --- a/app/Actions/RemoteProcess/DispatchRemoteProcess.php +++ b/app/Actions/RemoteProcess/DispatchRemoteProcess.php @@ -19,12 +19,10 @@ public function __construct(RemoteProcessArgs $remoteProcessArgs) $this->activity = activity() ->withProperties($properties) ->performedOn($remoteProcessArgs->model) - ->event('deployment') ->log(""); } else { $this->activity = activity() ->withProperties($remoteProcessArgs->toArray()) - ->event('remote_process') ->log(""); } } diff --git a/app/Actions/RemoteProcess/RunRemoteProcess.php b/app/Actions/RemoteProcess/RunRemoteProcess.php index 14b9190e1..3104cc36f 100644 --- a/app/Actions/RemoteProcess/RunRemoteProcess.php +++ b/app/Actions/RemoteProcess/RunRemoteProcess.php @@ -31,7 +31,8 @@ class RunRemoteProcess */ public function __construct(Activity $activity) { - if ($activity->getExtraProperty('type') !== ActivityTypes::REMOTE_PROCESS->value) { + + if ($activity->getExtraProperty('type') !== ActivityTypes::REMOTE_PROCESS->value && $activity->getExtraProperty('type') !== ActivityTypes::DEPLOYMENT->value) { throw new \RuntimeException('Incompatible Activity to run a remote command.'); } diff --git a/app/Enums/ActivityTypes.php b/app/Enums/ActivityTypes.php index ad1c10c7a..5bcab7cd7 100644 --- a/app/Enums/ActivityTypes.php +++ b/app/Enums/ActivityTypes.php @@ -5,4 +5,5 @@ enum ActivityTypes: string { case REMOTE_PROCESS = 'remote_process'; + case DEPLOYMENT = 'deployment'; } diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php index 43b17430c..a31681a4b 100644 --- a/app/Http/Controllers/ProjectController.php +++ b/app/Http/Controllers/ProjectController.php @@ -42,7 +42,7 @@ public function application() if (!$application) { return redirect()->route('home'); } - return view('project.application', ['project' => $project, 'application' => $application]); + return view('project.application', ['project' => $project, 'application' => $application, 'deployments' => $application->deployments()]); } public function database() { diff --git a/app/Http/Livewire/DeployApplication.php b/app/Http/Livewire/DeployApplication.php index e6889f1e4..95cc1b30b 100644 --- a/app/Http/Livewire/DeployApplication.php +++ b/app/Http/Livewire/DeployApplication.php @@ -36,7 +36,7 @@ public function deploy() $wildcard_domain = $project_wildcard_domain ?? $global_wildcard_domain ?? null; // Create Deployment ID - $this->deployment_uuid = new Cuid2(12); + $this->deployment_uuid = new Cuid2(7); $workdir = "/artifacts/{$this->deployment_uuid}"; // Start build process @@ -48,20 +48,9 @@ public function deploy() $this->command[] = "docker stop -t 0 {$this->deployment_uuid} >/dev/null"; $this->activity = remoteProcess($this->command, $destination->server, $this->deployment_uuid, $application); - // Create Deployment - Deployment::create([ - 'uuid' => $this->deployment_uuid, - 'type_id' => $application->id, - 'type_type' => Application::class, - 'activity_log_id' => $this->activity->id, - ]); - // Redirect to deployment page - return redirect()->route('project.deployment', [ - "deployment_uuid" => $this->deployment_uuid, - "project_uuid" => $application->environment->project->uuid, - "environment_name" => $application->environment->name, - "application_uuid" => $application->uuid - ]); + $currentUrl = url()->previous(); + $deploymentUrl = "$currentUrl/deployment/$this->deployment_uuid"; + return redirect($deploymentUrl); } public function render() { diff --git a/app/Models/Application.php b/app/Models/Application.php index afd52ae90..c6bacd941 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -22,7 +22,10 @@ public function source() { return $this->morphTo(); } - + public function deployments() + { + return Activity::where('subject_id', $this->id)->where('properties->deployment_uuid', '!=', null)->orderBy('created_at', 'desc')->get(); + } public function get_deployment(string $deployment_uuid) { return Activity::where('subject_id', $this->id)->where('properties->deployment_uuid', '=', $deployment_uuid)->first(); diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php index 32bf22f89..1d9564ee2 100644 --- a/app/Models/BaseModel.php +++ b/app/Models/BaseModel.php @@ -12,7 +12,7 @@ protected static function boot() parent::boot(); static::creating(function (Model $model) { - $model->uuid = (string) new Cuid2(); + $model->uuid = (string) new Cuid2(7); }); } } diff --git a/app/Models/User.php b/app/Models/User.php index 576415ec1..5ba3a299d 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -48,7 +48,7 @@ protected static function boot() parent::boot(); static::creating(function (Model $model) { - $model->uuid = (string) new Cuid2(); + $model->uuid = (string) new Cuid2(7); }); } public function teams() diff --git a/bootstrap/helpers.php b/bootstrap/helpers.php index 0b9498831..86bc7a6e0 100644 --- a/bootstrap/helpers.php +++ b/bootstrap/helpers.php @@ -2,6 +2,7 @@ use App\Actions\RemoteProcess\DispatchRemoteProcess; use App\Data\RemoteProcessArgs; +use App\Enums\ActivityTypes; use App\Models\Server; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Storage; @@ -29,6 +30,7 @@ function 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, diff --git a/resources/views/project/application.blade.php b/resources/views/project/application.blade.php index 36ab57146..db9ba59ee 100644 --- a/resources/views/project/application.blade.php +++ b/resources/views/project/application.blade.php @@ -3,4 +3,13 @@

    Name: {{ $project->name }}

    UUID: {{ $project->uuid }}

    +
    +

    Deployments

    + @foreach ($deployments as $deployment) +

    + + {{ data_get($deployment->properties, 'deployment_uuid') }} +

    + @endforeach +
    diff --git a/routes/web.php b/routes/web.php index 875a117c8..067b0536f 100644 --- a/routes/web.php +++ b/routes/web.php @@ -20,9 +20,12 @@ Route::middleware(['auth'])->group(function () { Route::get('/', [HomeController::class, 'show'])->name('home'); Route::get('/project/{project_uuid}', [ProjectController::class, 'environments'])->name('project.environments'); + Route::get('/project/{project_uuid}/{environment_name}', [ProjectController::class, 'resources'])->name('project.resources'); + Route::get('/project/{project_uuid}/{environment_name}/application/{application_uuid}', [ProjectController::class, 'application'])->name('project.application'); Route::get('/project/{project_uuid}/{environment_name}/application/{application_uuid}/deployment/{deployment_uuid}', [ProjectController::class, 'deployment'])->name('project.deployment'); + Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}', [ProjectController::class, 'database'])->name('project.database'); Route::get('/project/{project_uuid}/{environment_name}/service/{service_uuid}', [ProjectController::class, 'service'])->name('project.service'); From 7e47fdf52fa97a51ba03b9b72da5c2abe2a5413b Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 29 Mar 2023 13:13:27 +0200 Subject: [PATCH 07/13] fix: enviroments --- app/Http/Controllers/ProjectController.php | 3 ++- resources/views/project/resources.blade.php | 19 ++++++++----------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php index a31681a4b..d6cadf308 100644 --- a/app/Http/Controllers/ProjectController.php +++ b/app/Http/Controllers/ProjectController.php @@ -23,7 +23,8 @@ public function resources() if (!$project) { return redirect()->route('home'); } - return view('project.resources', ['project' => $project]); + $environment = $project->environments->where('name', request()->route('environment_name'))->first(); + return view('project.resources', ['project' => $project, 'environment' => $environment]); } public function application() { diff --git a/resources/views/project/resources.blade.php b/resources/views/project/resources.blade.php index ec5976d25..cdc5946ef 100644 --- a/resources/views/project/resources.blade.php +++ b/resources/views/project/resources.blade.php @@ -1,29 +1,26 @@

    Resources

    - - @foreach ($project->environments as $environment) -
    - @foreach ($environment->applications as $application) +
    + @foreach ($environment->applications as $application)

    {{ $application->name }}

    - @endforeach - @foreach ($environment->databases as $database) + @endforeach + @foreach ($environment->databases as $database)

    {{ $database->name }}

    - @endforeach - @foreach ($environment->services as $service) + @endforeach + @foreach ($environment->services as $service)

    {{ $service->name }}

    - @endforeach -
    - @endforeach + @endforeach +
    From 7c9506c96de3ec0ac251663a3a35a0911ba95202 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 29 Mar 2023 15:47:56 +0200 Subject: [PATCH 08/13] updates --- .../RemoteProcess/DispatchRemoteProcess.php | 4 +- app/Http/Livewire/DeployApplication.php | 67 +++++++++++++++++-- .../livewire/deploy-application.blade.php | 1 + 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/app/Actions/RemoteProcess/DispatchRemoteProcess.php b/app/Actions/RemoteProcess/DispatchRemoteProcess.php index 21e40d3a0..ad3acc291 100644 --- a/app/Actions/RemoteProcess/DispatchRemoteProcess.php +++ b/app/Actions/RemoteProcess/DispatchRemoteProcess.php @@ -3,6 +3,7 @@ namespace App\Actions\RemoteProcess; use App\Data\RemoteProcessArgs; +use App\Jobs\DeployRemoteProcess; use App\Jobs\ExecuteRemoteProcess; use Spatie\Activitylog\Models\Activity; @@ -30,11 +31,8 @@ public function __construct(RemoteProcessArgs $remoteProcessArgs) public function __invoke(): Activity { $job = new ExecuteRemoteProcess($this->activity); - dispatch($job); - $this->activity->refresh(); - return $this->activity; } } diff --git a/app/Http/Livewire/DeployApplication.php b/app/Http/Livewire/DeployApplication.php index 95cc1b30b..66c79ebef 100644 --- a/app/Http/Livewire/DeployApplication.php +++ b/app/Http/Livewire/DeployApplication.php @@ -4,8 +4,8 @@ use App\Models\Application; use App\Models\CoolifyInstanceSettings; -use App\Models\Deployment; use Livewire\Component; +use Symfony\Component\Yaml\Yaml; use Visus\Cuid2\Cuid2; class DeployApplication extends Component @@ -17,12 +17,25 @@ class DeployApplication extends Component private function execute_in_builder(string $command) { - return $this->command[] = "docker exec {$this->deployment_uuid} sh -c '{$command}'"; + 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(mixed $application) + { + return Yaml::dump([ + 'version' => '3.8', + 'services' => [ + $application->uuid => [ + 'image' => "{$application->uuid}:TAG", + 'container_name' => $application->uuid, + 'restart' => 'always', + ] + ] + ]); + } public function deploy() { $coolify_instance_settings = CoolifyInstanceSettings::find(1); @@ -37,14 +50,48 @@ public function deploy() // Create Deployment ID $this->deployment_uuid = new Cuid2(7); + + // Set wildcard domain + if (!$application->settings->is_bot && !$application->fqdn && $wildcard_domain) { + $application->fqdn = $application->uuid . '.' . $wildcard_domain; + $application->save(); + } $workdir = "/artifacts/{$this->deployment_uuid}"; // Start build process + $docker_compose = $this->generate_docker_compose($application); $this->command[] = "echo 'Starting deployment of {$application->name} ({$application->uuid})'"; $this->start_builder_container(); - // $this->execute_in_builder('hostname'); $this->execute_in_builder("git clone -b {$application->git_branch} {$source->html_url}/{$application->git_repository}.git {$workdir}"); - $this->execute_in_builder("ls -l {$workdir}"); + + // Export git commit to a file + $this->execute_in_builder("cd {$workdir} && git rev-parse HEAD > {$workdir}/.git-commit"); + + // Set TAG in docker-compose.yml + $this->execute_in_builder("echo -e '{$docker_compose}' > {$workdir}/docker-compose.yml"); + $this->execute_in_builder("sed -i \"s/TAG/$(cat {$workdir}/.git-commit)/g\" {$workdir}/docker-compose.yml"); + $this->execute_in_builder("cat {$workdir}/docker-compose.yml"); + + if (str_starts_with($application->base_image, 'apache') || str_starts_with($application->base_image, 'nginx')) { + // @TODO: Add static site builds + } else { + $nixpacks_command = "nixpacks build -o {$workdir} --no-error-without-start"; + if ($application->install_command) { + $nixpacks_command .= " --install-cmd '{$application->install_command}'"; + } + if ($application->build_command) { + $nixpacks_command .= " --build-cmd '{$application->build_command}'"; + } + if ($application->start_command) { + $nixpacks_command .= " --start-cmd '{$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->execute_in_builder("docker build -f {$workdir}/Dockerfile --build-arg SOURCE_COMMIT=$(cat {$workdir}/.git-commit) --progress plain -t {$application->uuid}:$(cat {$workdir}/.git-commit) {$workdir}"); + $this->execute_in_builder("docker compose --project-directory {$workdir} up -d"); $this->command[] = "docker stop -t 0 {$this->deployment_uuid} >/dev/null"; $this->activity = remoteProcess($this->command, $destination->server, $this->deployment_uuid, $application); @@ -52,6 +99,18 @@ public function deploy() $deploymentUrl = "$currentUrl/deployment/$this->deployment_uuid"; return redirect($deploymentUrl); } + public function cancel() + { + // dd($this->deployment_uuid); + // $jobs = DB::table('jobs')->get(); + // foreach ($jobs as $job) { + // // Decode the job payload + // $jobPayload = json_decode($job->payload, true); + // if (str_contains($jobPayload['data']['command'], $this->deployment_uuid)) { + // dd($jobPayload['data']['command']); + // } + // } + } public function render() { return view('livewire.deploy-application'); diff --git a/resources/views/livewire/deploy-application.blade.php b/resources/views/livewire/deploy-application.blade.php index 14d29bd9c..27d01c442 100644 --- a/resources/views/livewire/deploy-application.blade.php +++ b/resources/views/livewire/deploy-application.blade.php @@ -1,3 +1,4 @@
    +
    From 0aa91b977df769ad297e4766bb1596e524e78bae Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 29 Mar 2023 18:09:16 +0200 Subject: [PATCH 09/13] base64 decode data --- app/Http/Livewire/DeployApplication.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Livewire/DeployApplication.php b/app/Http/Livewire/DeployApplication.php index 66c79ebef..c340a9aa7 100644 --- a/app/Http/Livewire/DeployApplication.php +++ b/app/Http/Livewire/DeployApplication.php @@ -59,7 +59,7 @@ public function deploy() $workdir = "/artifacts/{$this->deployment_uuid}"; // Start build process - $docker_compose = $this->generate_docker_compose($application); + $docker_compose_base64 = base64_encode($this->generate_docker_compose($application)); $this->command[] = "echo 'Starting deployment of {$application->name} ({$application->uuid})'"; $this->start_builder_container(); $this->execute_in_builder("git clone -b {$application->git_branch} {$source->html_url}/{$application->git_repository}.git {$workdir}"); @@ -68,7 +68,7 @@ public function deploy() $this->execute_in_builder("cd {$workdir} && git rev-parse HEAD > {$workdir}/.git-commit"); // Set TAG in docker-compose.yml - $this->execute_in_builder("echo -e '{$docker_compose}' > {$workdir}/docker-compose.yml"); + $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->execute_in_builder("cat {$workdir}/docker-compose.yml"); From b0f062ff0c06fd75a2b2b4f6316f358f25db7838 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 29 Mar 2023 18:11:54 +0200 Subject: [PATCH 10/13] wip --- app/Http/Livewire/DeployApplication.php | 4 +-- app/Models/Deployment.php | 24 --------------- ..._03_27_083622_create_deployments_table.php | 30 ------------------- 3 files changed, 2 insertions(+), 56 deletions(-) delete mode 100644 app/Models/Deployment.php delete mode 100644 database/migrations/2023_03_27_083622_create_deployments_table.php diff --git a/app/Http/Livewire/DeployApplication.php b/app/Http/Livewire/DeployApplication.php index c340a9aa7..28533b820 100644 --- a/app/Http/Livewire/DeployApplication.php +++ b/app/Http/Livewire/DeployApplication.php @@ -67,10 +67,10 @@ public function deploy() // Export git commit to a file $this->execute_in_builder("cd {$workdir} && git rev-parse HEAD > {$workdir}/.git-commit"); - // Set TAG in docker-compose.yml + // Create docker-compose.yml $this->execute_in_builder("echo '{$docker_compose_base64}' | base64 -d > {$workdir}/docker-compose.yml"); + // Set TAG in docker-compose.yml $this->execute_in_builder("sed -i \"s/TAG/$(cat {$workdir}/.git-commit)/g\" {$workdir}/docker-compose.yml"); - $this->execute_in_builder("cat {$workdir}/docker-compose.yml"); if (str_starts_with($application->base_image, 'apache') || str_starts_with($application->base_image, 'nginx')) { // @TODO: Add static site builds diff --git a/app/Models/Deployment.php b/app/Models/Deployment.php deleted file mode 100644 index c8751e1fe..000000000 --- a/app/Models/Deployment.php +++ /dev/null @@ -1,24 +0,0 @@ -morphTo(); - } - public function activity() - { - return $this->belongsTo(Activity::class, 'activity_log_id'); - } -} diff --git a/database/migrations/2023_03_27_083622_create_deployments_table.php b/database/migrations/2023_03_27_083622_create_deployments_table.php deleted file mode 100644 index 8a0becc33..000000000 --- a/database/migrations/2023_03_27_083622_create_deployments_table.php +++ /dev/null @@ -1,30 +0,0 @@ -id(); - $table->string('uuid')->unique(); - $table->morphs('type'); - $table->foreignId('activity_log_id'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('deployments'); - } -}; From d70f9f9a468164ca36e2d8be8138535c5e141963 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 29 Mar 2023 20:28:42 +0200 Subject: [PATCH 11/13] wip --- app/Http/Livewire/DeployApplication.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Http/Livewire/DeployApplication.php b/app/Http/Livewire/DeployApplication.php index 28533b820..251812c83 100644 --- a/app/Http/Livewire/DeployApplication.php +++ b/app/Http/Livewire/DeployApplication.php @@ -91,7 +91,8 @@ public function deploy() $this->execute_in_builder("rm -f {$workdir}/.nixpacks/Dockerfile"); } $this->execute_in_builder("docker build -f {$workdir}/Dockerfile --build-arg SOURCE_COMMIT=$(cat {$workdir}/.git-commit) --progress plain -t {$application->uuid}:$(cat {$workdir}/.git-commit) {$workdir}"); - $this->execute_in_builder("docker compose --project-directory {$workdir} up -d"); + $this->execute_in_builder("test -z \"$(docker ps --format '{{.State}}' --filter 'name={$application->uuid}')\" && docker rm -f {$application->uuid} >/dev/null 2>&1"); + $this->execute_in_builder("docker compose --project-directory {$workdir} up -d >/dev/null 2>&1"); $this->command[] = "docker stop -t 0 {$this->deployment_uuid} >/dev/null"; $this->activity = remoteProcess($this->command, $destination->server, $this->deployment_uuid, $application); From e11f8837a478a40c49d3212b1eab3e99f0253503 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 29 Mar 2023 20:33:29 +0200 Subject: [PATCH 12/13] fix multiplexing --- app/Actions/RemoteProcess/RunRemoteProcess.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Actions/RemoteProcess/RunRemoteProcess.php b/app/Actions/RemoteProcess/RunRemoteProcess.php index 3104cc36f..790f21efd 100644 --- a/app/Actions/RemoteProcess/RunRemoteProcess.php +++ b/app/Actions/RemoteProcess/RunRemoteProcess.php @@ -79,7 +79,7 @@ protected function getCommand(): string . '-o PasswordAuthentication=no ' . '-o RequestTTY=no ' . '-o LogLevel=ERROR ' - . '-o ControlMaster=auto -o ControlPersist=yes -o ControlPersist=1m -o ControlPath=/var/www/html/storage/app/.ssh/ssh_mux_%h_%p_%r ' + . '-o ControlMaster=auto -o ControlPersist=1m -o ControlPath=/var/www/html/storage/app/.ssh/ssh_mux_%h_%p_%r ' . "-p {$port} " . "{$user}@{$server_ip} " . " 'bash -se' << \\$delimiter" . PHP_EOL From 6205598b450b38909cafdcc7957cd66dbbfa1e8e Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 30 Mar 2023 09:47:04 +0200 Subject: [PATCH 13/13] fixes --- .../RemoteProcess/DispatchRemoteProcess.php | 2 + app/Http/Livewire/DeployApplication.php | 107 ++++++++++++------ app/Models/Application.php | 22 ++++ app/Providers/AppServiceProvider.php | 7 +- ...085020_create_standalone_dockers_table.php | 1 + database/seeders/StandaloneDockerSeeder.php | 1 + database/seeders/UserSeeder.php | 2 - 7 files changed, 104 insertions(+), 38 deletions(-) diff --git a/app/Actions/RemoteProcess/DispatchRemoteProcess.php b/app/Actions/RemoteProcess/DispatchRemoteProcess.php index ad3acc291..95a55bc91 100644 --- a/app/Actions/RemoteProcess/DispatchRemoteProcess.php +++ b/app/Actions/RemoteProcess/DispatchRemoteProcess.php @@ -20,10 +20,12 @@ public function __construct(RemoteProcessArgs $remoteProcessArgs) $this->activity = activity() ->withProperties($properties) ->performedOn($remoteProcessArgs->model) + ->event($remoteProcessArgs->type) ->log(""); } else { $this->activity = activity() ->withProperties($remoteProcessArgs->toArray()) + ->event($remoteProcessArgs->type) ->log(""); } } diff --git a/app/Http/Livewire/DeployApplication.php b/app/Http/Livewire/DeployApplication.php index 251812c83..ff11c0e25 100644 --- a/app/Http/Livewire/DeployApplication.php +++ b/app/Http/Livewire/DeployApplication.php @@ -14,6 +14,8 @@ class DeployApplication extends Component public $activity; protected string $deployment_uuid; protected array $command = []; + protected Application $application; + protected $destination; private function execute_in_builder(string $command) { @@ -23,28 +25,70 @@ 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(mixed $application) + private function generate_docker_compose() { return Yaml::dump([ 'version' => '3.8', 'services' => [ - $application->uuid => [ - 'image' => "{$application->uuid}:TAG", - 'container_name' => $application->uuid, + $this->application->uuid => [ + 'image' => "{$this->application->uuid}:TAG", + 'expose' => $this->application->ports_exposes, + 'container_name' => $this->application->uuid, 'restart' => 'always', + '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, ] ] ]); } + 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); + } public function deploy() { $coolify_instance_settings = CoolifyInstanceSettings::find(1); - $application = Application::where('uuid', $this->application_uuid)->first(); - $destination = $application->destination->getMorphClass()::where('id', $application->destination->id)->first(); - $source = $application->source->getMorphClass()::where('id', $application->source->id)->first(); - + $this->application = Application::where('uuid', $this->application_uuid)->first(); + $this->destination = $this->application->destination->getMorphClass()::where('id', $this->application->destination->id)->first(); + $source = $this->application->source->getMorphClass()::where('id', $this->application->source->id)->first(); // Get Wildcard Domain - $project_wildcard_domain = data_get($application, 'environment.project.settings.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; @@ -52,49 +96,51 @@ public function deploy() $this->deployment_uuid = new Cuid2(7); // Set wildcard domain - if (!$application->settings->is_bot && !$application->fqdn && $wildcard_domain) { - $application->fqdn = $application->uuid . '.' . $wildcard_domain; - $application->save(); + 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 - $docker_compose_base64 = base64_encode($this->generate_docker_compose($application)); - $this->command[] = "echo 'Starting deployment of {$application->name} ({$application->uuid})'"; + $docker_compose_base64 = base64_encode($this->generate_docker_compose($this->application)); + $this->command[] = "echo 'Starting deployment of {$this->application->name} ({$this->application->uuid})'"; $this->start_builder_container(); - $this->execute_in_builder("git clone -b {$application->git_branch} {$source->html_url}/{$application->git_repository}.git {$workdir}"); + $this->execute_in_builder("git clone -b {$this->application->git_branch} {$source->html_url}/{$this->application->git_repository}.git {$workdir}"); // Export git commit to a file $this->execute_in_builder("cd {$workdir} && git rev-parse HEAD > {$workdir}/.git-commit"); + $this->execute_in_builder("rm -fr {$workdir}/.git"); // Create docker-compose.yml $this->execute_in_builder("echo '{$docker_compose_base64}' | base64 -d > {$workdir}/docker-compose.yml"); // Set TAG in docker-compose.yml $this->execute_in_builder("sed -i \"s/TAG/$(cat {$workdir}/.git-commit)/g\" {$workdir}/docker-compose.yml"); - if (str_starts_with($application->base_image, 'apache') || str_starts_with($application->base_image, 'nginx')) { + 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 ($application->install_command) { - $nixpacks_command .= " --install-cmd '{$application->install_command}'"; + if ($this->application->install_command) { + $nixpacks_command .= " --install-cmd '{$this->application->install_command}'"; } - if ($application->build_command) { - $nixpacks_command .= " --build-cmd '{$application->build_command}'"; + if ($this->application->build_command) { + $nixpacks_command .= " --build-cmd '{$this->application->build_command}'"; } - if ($application->start_command) { - $nixpacks_command .= " --start-cmd '{$application->start_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->execute_in_builder("docker build -f {$workdir}/Dockerfile --build-arg SOURCE_COMMIT=$(cat {$workdir}/.git-commit) --progress plain -t {$application->uuid}:$(cat {$workdir}/.git-commit) {$workdir}"); - $this->execute_in_builder("test -z \"$(docker ps --format '{{.State}}' --filter 'name={$application->uuid}')\" && docker rm -f {$application->uuid} >/dev/null 2>&1"); - $this->execute_in_builder("docker compose --project-directory {$workdir} up -d >/dev/null 2>&1"); + + $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->execute_in_builder("test -z \"$(docker ps --format '{{.State}}' --filter 'name={$this->application->uuid}')\" && docker rm -f {$this->application->uuid}"); + $this->execute_in_builder("docker compose --project-directory {$workdir} up -d"); $this->command[] = "docker stop -t 0 {$this->deployment_uuid} >/dev/null"; - $this->activity = remoteProcess($this->command, $destination->server, $this->deployment_uuid, $application); + $this->activity = remoteProcess($this->command, $this->destination->server, $this->deployment_uuid, $this->application); $currentUrl = url()->previous(); $deploymentUrl = "$currentUrl/deployment/$this->deployment_uuid"; @@ -102,15 +148,6 @@ public function deploy() } public function cancel() { - // dd($this->deployment_uuid); - // $jobs = DB::table('jobs')->get(); - // foreach ($jobs as $job) { - // // Decode the job payload - // $jobPayload = json_decode($job->payload, true); - // if (str_contains($jobPayload['data']['command'], $this->deployment_uuid)) { - // dd($jobPayload['data']['command']); - // } - // } } public function render() { diff --git a/app/Models/Application.php b/app/Models/Application.php index c6bacd941..c0fa88067 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -2,6 +2,7 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Casts\Attribute; use Spatie\Activitylog\Models\Activity; class Application extends BaseModel @@ -22,6 +23,27 @@ public function source() { return $this->morphTo(); } + + public function portsMappings(): Attribute + { + return Attribute::make( + get: fn (string|null $portsMappings) => + is_null($portsMappings) + ? [] + : explode(',', $portsMappings) + + ); + } + public function portsExposes(): Attribute + { + return Attribute::make( + get: fn (string|null $portsExposes) => + is_null($portsExposes) + ? [] + : explode(',', $portsExposes) + + ); + } public function deployments() { return Activity::where('subject_id', $this->id)->where('properties->deployment_uuid', '!=', null)->orderBy('created_at', 'desc')->get(); diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 452e6b65b..3487238e9 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,8 @@ namespace App\Providers; +use Illuminate\Queue\Events\JobProcessed; +use Illuminate\Support\Facades\Queue; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider @@ -19,6 +21,9 @@ public function register(): void */ public function boot(): void { - // + // @TODO: Should remove builder container here + // Queue::after(function (JobProcessed $event) { + // dd($event->job->resolveName()); + // }); } } diff --git a/database/migrations/2023_03_27_085020_create_standalone_dockers_table.php b/database/migrations/2023_03_27_085020_create_standalone_dockers_table.php index 184d1e3e2..814d3eee3 100644 --- a/database/migrations/2023_03_27_085020_create_standalone_dockers_table.php +++ b/database/migrations/2023_03_27_085020_create_standalone_dockers_table.php @@ -14,6 +14,7 @@ public function up(): void Schema::create('standalone_dockers', function (Blueprint $table) { $table->id(); $table->string('uuid')->unique(); + $table->string('network'); $table->foreignId('server_id'); $table->timestamps(); diff --git a/database/seeders/StandaloneDockerSeeder.php b/database/seeders/StandaloneDockerSeeder.php index 082e2647c..9dd27f063 100644 --- a/database/seeders/StandaloneDockerSeeder.php +++ b/database/seeders/StandaloneDockerSeeder.php @@ -18,6 +18,7 @@ public function run(): void $server_3 = Server::find(3); StandaloneDocker::create([ 'id' => 1, + 'network' => 'coolify', 'server_id' => $server_3->id, ]); } diff --git a/database/seeders/UserSeeder.php b/database/seeders/UserSeeder.php index a92626d45..63301e35c 100644 --- a/database/seeders/UserSeeder.php +++ b/database/seeders/UserSeeder.php @@ -2,10 +2,8 @@ namespace Database\Seeders; -use App\Models\Team; use App\Models\User; use Illuminate\Database\Seeder; -use Illuminate\Support\Facades\DB; class UserSeeder extends Seeder {