From 619d39533188e01ff2bac56b88076a22a05ae228 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Fri, 11 Aug 2023 22:41:47 +0200 Subject: [PATCH] feat: dockerfile build pack --- app/Http/Livewire/Dev/ScheduledBackups.php | 16 --- .../Livewire/Project/Application/General.php | 6 + .../Livewire/Project/New/SimpleDockerfile.php | 68 ++++++++++ .../Shared/EnvironmentVariable/Show.php | 4 + app/Jobs/ApplicationDeploymentJob.php | 125 +++++++++++------- app/Models/Application.php | 14 ++ app/Models/GithubApp.php | 2 +- bootstrap/helpers/docker.php | 10 ++ ...8_add_dockerfile_to_applications_table.php | 25 ++++ database/seeders/ApplicationSeeder.php | 20 ++- database/seeders/GithubAppSeeder.php | 1 + resources/views/dashboard.blade.php | 3 +- .../livewire/dev/scheduled-backups.blade.php | 13 -- .../application/deployment-logs.blade.php | 2 +- .../project/application/general.blade.php | 82 +++++++----- ...ub-private-repository-deploy-key.blade.php | 2 +- .../livewire/project/new/select.blade.php | 29 ++-- .../project/new/simple-dockerfile.blade.php | 15 +++ .../environment-variable/show.blade.php | 4 +- .../application/configuration.blade.php | 24 ++-- resources/views/project/new.blade.php | 2 + 21 files changed, 330 insertions(+), 137 deletions(-) delete mode 100644 app/Http/Livewire/Dev/ScheduledBackups.php create mode 100644 app/Http/Livewire/Project/New/SimpleDockerfile.php create mode 100644 database/migrations/2023_08_11_190528_add_dockerfile_to_applications_table.php delete mode 100644 resources/views/livewire/dev/scheduled-backups.blade.php create mode 100644 resources/views/livewire/project/new/simple-dockerfile.blade.php diff --git a/app/Http/Livewire/Dev/ScheduledBackups.php b/app/Http/Livewire/Dev/ScheduledBackups.php deleted file mode 100644 index a7599d44a..000000000 --- a/app/Http/Livewire/Dev/ScheduledBackups.php +++ /dev/null @@ -1,16 +0,0 @@ -scheduledDatabaseBackup = ScheduledDatabaseBackup::all(); - } -} diff --git a/app/Http/Livewire/Project/Application/General.php b/app/Http/Livewire/Project/Application/General.php index 53416ae00..8b2ed8900 100644 --- a/app/Http/Livewire/Project/Application/General.php +++ b/app/Http/Livewire/Project/Application/General.php @@ -47,6 +47,7 @@ class General extends Component 'application.publish_directory' => 'nullable', 'application.ports_exposes' => 'required', 'application.ports_mappings' => 'nullable', + 'application.dockerfile' => 'nullable', ]; protected $validationAttributes = [ 'application.name' => 'name', @@ -64,6 +65,7 @@ class General extends Component 'application.publish_directory' => 'Publish directory', 'application.ports_exposes' => 'Ports exposes', 'application.ports_mappings' => 'Ports mappings', + 'application.dockerfile' => 'Dockerfile', ]; public function instantSave() @@ -140,6 +142,10 @@ public function submit() $domains = Str::of($this->application->fqdn)->trim()->explode(',')->map(function ($domain) { return Str::of($domain)->trim()->lower(); }); + $port = get_port_from_dockerfile($this->application->dockerfile); + if ($port) { + $this->application->ports_exposes = $port; + } if ($this->application->base_directory && $this->application->base_directory !== '/') { $this->application->base_directory = rtrim($this->application->base_directory, '/'); } diff --git a/app/Http/Livewire/Project/New/SimpleDockerfile.php b/app/Http/Livewire/Project/New/SimpleDockerfile.php new file mode 100644 index 000000000..bbc82fcd5 --- /dev/null +++ b/app/Http/Livewire/Project/New/SimpleDockerfile.php @@ -0,0 +1,68 @@ +parameters = get_route_parameters(); + $this->query = request()->query(); + if (is_dev()) { + $this->dockerfile = 'FROM nginx +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] +'; + } + } + public function submit() + { + $this->validate([ + 'dockerfile' => 'required' + ]); + $destination_uuid = $this->query['destination']; + $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); + if (!$destination) { + $destination = SwarmDocker::where('uuid', $destination_uuid)->first(); + } + if (!$destination) { + throw new \Exception('Destination not found. What?!'); + } + $destination_class = $destination->getMorphClass(); + + $project = Project::where('uuid', $this->parameters['project_uuid'])->first(); + $environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first(); + + $port = get_port_from_dockerfile($this->dockerfile); + $application = Application::create([ + 'name' => 'dockerfile-' . new Cuid2(7), + 'repository_project_id' => 0, + 'git_repository' => "coollabsio/coolify", + 'git_branch' => 'main', + 'build_pack' => 'dockerfile', + 'dockerfile' => $this->dockerfile, + 'ports_exposes' => $port, + 'environment_id' => $environment->id, + 'destination_id' => $destination->id, + 'destination_type' => $destination_class, + 'source_id' => 0, + 'source_type' => GithubApp::class + ]); + redirect()->route('project.application.configuration', [ + 'application_uuid' => $application->uuid, + 'environment_name' => $environment->name, + 'project_uuid' => $project->uuid, + ]); + } +} diff --git a/app/Http/Livewire/Project/Shared/EnvironmentVariable/Show.php b/app/Http/Livewire/Project/Shared/EnvironmentVariable/Show.php index f75a91b63..cfc94af22 100644 --- a/app/Http/Livewire/Project/Shared/EnvironmentVariable/Show.php +++ b/app/Http/Livewire/Project/Shared/EnvironmentVariable/Show.php @@ -28,6 +28,10 @@ public function mount() $this->parameters = get_route_parameters(); } + public function instantSave() + { + $this->submit(); + } public function submit() { $this->validate(); diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index a37b609c7..fc0a3a1b7 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -117,10 +117,14 @@ public function handle(): void 'status' => ApplicationDeploymentStatus::IN_PROGRESS->value, ]); try { - if ($this->pull_request_id !== 0) { - $this->deploy_pull_request(); + if ($this->application->dockerfile) { + $this->deploy_simple_dockerfile(); } else { - $this->deploy(); + if ($this->pull_request_id !== 0) { + $this->deploy_pull_request(); + } else { + $this->deploy(); + } } if ($this->application->fqdn) dispatch(new ProxyStartJob($this->server)); $this->next(ApplicationDeploymentStatus::FINISHED->value); @@ -150,7 +154,74 @@ public function handle(): void ); } } + private function deploy_simple_dockerfile() + { + $dockerfile_base64 = base64_encode($this->application->dockerfile); + $this->execute_remote_command( + [ + "echo 'Starting deployment of {$this->application->name}.'" + ], + ); + $this->prepare_builder_image(); + $this->execute_remote_command( + [ + $this->execute_in_builder("echo '$dockerfile_base64' | base64 -d > $this->workdir/Dockerfile") + ], + ); + $this->build_image_name = "{$this->application->git_repository}:build"; + $this->production_image_name = "{$this->application->uuid}:latest"; + ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green(); + $this->generate_compose_file(); + $this->generate_build_env_variables(); + $this->add_build_env_variables_to_dockerfile(); + $this->build_image(); + $this->stop_running_container(); + $this->start_by_compose_file(); + } + private function deploy() + { + $this->execute_remote_command( + [ + "echo 'Starting deployment of {$this->application->git_repository}:{$this->application->git_branch}.'" + ], + ); + $this->prepare_builder_image(); + $this->clone_repository(); + $tag = Str::of("{$this->commit}-{$this->application->id}-{$this->pull_request_id}"); + if (strlen($tag) > 128) { + $tag = $tag->substr(0, 128); + } + + $this->build_image_name = "{$this->application->git_repository}:{$tag}-build"; + $this->production_image_name = "{$this->application->uuid}:{$tag}"; + ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green(); + + if (!$this->force_rebuild) { + $this->execute_remote_command([ + "docker images -q {$this->production_image_name} 2>/dev/null", "hidden" => true, "save" => "local_image_found" + ]); + if (Str::of($this->saved_outputs->get('local_image_found'))->isNotEmpty()) { + $this->execute_remote_command([ + "echo 'Docker Image found locally with the same Git Commit SHA {$this->application->uuid}:{$this->commit}. Build step skipped...'" + ]); + $this->generate_compose_file(); + $this->stop_running_container(); + $this->start_by_compose_file(); + return; + } + } + $this->cleanup_git(); + if ($this->application->build_pack === 'nixpacks') { + $this->generate_nixpacks_confs(); + } + $this->generate_compose_file(); + $this->generate_build_env_variables(); + $this->add_build_env_variables_to_dockerfile(); + $this->build_image(); + $this->stop_running_container(); + $this->start_by_compose_file(); + } private function deploy_pull_request() { $this->build_image_name = "{$this->application->uuid}:pr-{$this->pull_request_id}-build"; @@ -162,7 +233,9 @@ private function deploy_pull_request() $this->prepare_builder_image(); $this->clone_repository(); $this->cleanup_git(); - $this->generate_buildpack(); + if ($this->application->build_pack === 'nixpacks') { + $this->generate_nixpacks_confs(); + } $this->generate_compose_file(); // Needs separate preview variables // $this->generate_build_env_variables(); @@ -277,7 +350,7 @@ private function cleanup_git() ); } - private function generate_buildpack() + private function generate_nixpacks_confs() { $this->execute_remote_command( [ @@ -589,49 +662,7 @@ private function start_by_compose_file() ); } - private function deploy() - { - $this->execute_remote_command( - [ - "echo 'Starting deployment of {$this->application->git_repository}:{$this->application->git_branch}.'" - ], - ); - $this->prepare_builder_image(); - $this->clone_repository(); - - $tag = Str::of("{$this->commit}-{$this->application->id}-{$this->pull_request_id}"); - if (strlen($tag) > 128) { - $tag = $tag->substr(0, 128); - } - - $this->build_image_name = "{$this->application->git_repository}:{$tag}-build"; - $this->production_image_name = "{$this->application->uuid}:{$tag}"; - ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green(); - - if (!$this->force_rebuild) { - $this->execute_remote_command([ - "docker images -q {$this->production_image_name} 2>/dev/null", "hidden" => true, "save" => "local_image_found" - ]); - if (Str::of($this->saved_outputs->get('local_image_found'))->isNotEmpty()) { - $this->execute_remote_command([ - "echo 'Docker Image found locally with the same Git Commit SHA {$this->application->uuid}:{$this->commit}. Build step skipped...'" - ]); - $this->generate_compose_file(); - $this->stop_running_container(); - $this->start_by_compose_file(); - return; - } - } - $this->cleanup_git(); - $this->generate_buildpack(); - $this->generate_compose_file(); - $this->generate_build_env_variables(); - $this->add_build_env_variables_to_dockerfile(); - $this->build_image(); - $this->stop_running_container(); - $this->start_by_compose_file(); - } private function generate_build_env_variables() { diff --git a/app/Models/Application.php b/app/Models/Application.php index 9602d45ec..6b730edee 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -215,4 +215,18 @@ public function deploymentType() } throw new \Exception('No deployment type found'); } + public function could_set_build_commands(): bool + { + if ($this->build_pack === 'nixpacks') { + return true; + } + return false; + } + public function git_based(): bool + { + if ($this->dockerfile || $this->build_pack === 'dockerfile') { + return false; + } + return true; + } } diff --git a/app/Models/GithubApp.php b/app/Models/GithubApp.php index c9badfed1..74462e8d7 100644 --- a/app/Models/GithubApp.php +++ b/app/Models/GithubApp.php @@ -6,7 +6,7 @@ class GithubApp extends BaseModel { - protected $fillable = ['name', 'uuid', 'organization', 'api_url', 'html_url', 'custom_user', 'custom_port', 'team_id', 'client_secret', 'webhook_secret']; + protected $guarded = []; protected $appends = ['type']; protected $casts = [ 'is_public' => 'boolean', diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php index 82f6e4521..2787f153e 100644 --- a/bootstrap/helpers/docker.php +++ b/bootstrap/helpers/docker.php @@ -64,3 +64,13 @@ function generate_container_name(string $uuid, int $pull_request_id = 0) return $uuid; } } +function get_port_from_dockerfile($dockerfile): int +{ + $port = preg_grep('/EXPOSE\s+(\d+)/', explode("\n", $dockerfile)); + if (count($port) > 0 && preg_match('/EXPOSE\s+(\d+)/', $port[1], $matches)) { + $port = $matches[1]; + } else { + $port = 80; + } + return $port; +} diff --git a/database/migrations/2023_08_11_190528_add_dockerfile_to_applications_table.php b/database/migrations/2023_08_11_190528_add_dockerfile_to_applications_table.php new file mode 100644 index 000000000..f2ee10640 --- /dev/null +++ b/database/migrations/2023_08_11_190528_add_dockerfile_to_applications_table.php @@ -0,0 +1,25 @@ +longText('dockerfile')->nullable(); + }); + } + + public function down(): void + { + Schema::table('applications', function (Blueprint $table) { + $table->dropColumn('dockerfile'); + }); + } +}; diff --git a/database/seeders/ApplicationSeeder.php b/database/seeders/ApplicationSeeder.php index f5b000662..a43e0f19a 100644 --- a/database/seeders/ApplicationSeeder.php +++ b/database/seeders/ApplicationSeeder.php @@ -23,11 +23,27 @@ public function run(): void 'git_branch' => 'nodejs-fastify', 'build_pack' => 'nixpacks', 'ports_exposes' => '3000', - 'ports_mappings' => '3000:3000', + 'ports_mappings' => '3005:3000', 'environment_id' => 1, 'destination_id' => 0, 'destination_type' => StandaloneDocker::class, - 'source_id' => 1, + 'source_id' => 0, + 'source_type' => GithubApp::class + ]); + Application::create([ + 'name' => 'coollabsio/coolify-examples:dockerfile', + 'description' => 'Dockerfile Example', + 'fqdn' => 'http://foos.com', + 'repository_project_id' => 603035348, + 'git_repository' => 'coollabsio/coolify-examples', + 'git_branch' => 'dockerfile', + 'build_pack' => 'dockerfile', + 'ports_exposes' => '3000', + 'ports_mappings' => '3080:80', + 'environment_id' => 1, + 'destination_id' => 0, + 'destination_type' => StandaloneDocker::class, + 'source_id' => 0, 'source_type' => GithubApp::class ]); } diff --git a/database/seeders/GithubAppSeeder.php b/database/seeders/GithubAppSeeder.php index 1b5143988..4aa5ec753 100644 --- a/database/seeders/GithubAppSeeder.php +++ b/database/seeders/GithubAppSeeder.php @@ -13,6 +13,7 @@ class GithubAppSeeder extends Seeder public function run(): void { GithubApp::create([ + 'id' => 0, 'name' => 'Public GitHub', 'api_url' => 'https://api.github.com', 'html_url' => 'https://github.com', diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index 056a4cd3f..ee55d08a8 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -23,7 +23,6 @@ @if (is_dev()) - - + {{-- --}} @endif diff --git a/resources/views/livewire/dev/scheduled-backups.blade.php b/resources/views/livewire/dev/scheduled-backups.blade.php deleted file mode 100644 index e1646b1da..000000000 --- a/resources/views/livewire/dev/scheduled-backups.blade.php +++ /dev/null @@ -1,13 +0,0 @@ -
-

Scheduled Databse Backups

- @foreach ($scheduledDatabaseBackup as $backup) -
- {{ $backup->id }} - {{ $backup->database->id }} - {{ $backup->frequency }} - {{ $backup->database->type() }} - {{ $backup->created_at }} - {{ $backup->updated_at }} -
- @endforeach -
diff --git a/resources/views/livewire/project/application/deployment-logs.blade.php b/resources/views/livewire/project/application/deployment-logs.blade.php index 888c9d18b..1a0c5116f 100644 --- a/resources/views/livewire/project/application/deployment-logs.blade.php +++ b/resources/views/livewire/project/application/deployment-logs.blade.php @@ -18,7 +18,7 @@ class="scrollbar flex flex-col-reverse w-full overflow-y-auto border border-dott @if (decode_remote_command_output($application_deployment_queue)->count() > 0) @foreach (decode_remote_command_output($application_deployment_queue) as $line)
$line['type'] == 'stdout', 'text-error' => $line['type'] == 'stderr', 'text-warning' => $line['hidden'], diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php index 614446478..f8d5aff37 100644 --- a/resources/views/livewire/project/application/general.blade.php +++ b/resources/views/livewire/project/application/general.blade.php @@ -12,10 +12,10 @@
- - @if ($wildcard_domain) -
+
+ + @if ($wildcard_domain) @if ($global_wildcard_domain) Set Global Wildcard @@ -24,11 +24,11 @@ Set Server Wildcard @endif -
- @endif + @endif +
- + @if ($application->settings->is_static) @@ -37,22 +37,30 @@ @endif -

Build

-
- - - -
-
- - @if ($application->settings->is_static) - - @else - - @endif -
+ @if ($application->could_set_build_commands()) +

Build

+
+ + + +
+ +
+ + @if ($application->settings->is_static) + + @else + + @endif +
+ @endif + @if ($application->dockerfile) + + @endif +

Network

@if ($application->settings->is_static) @@ -67,20 +75,26 @@

Advanced

- + @if ($application->could_set_build_commands()) + + @endif - - - - + @if ($application->git_based()) + + + + + + @endif + {{-- --}} diff --git a/resources/views/livewire/project/new/github-private-repository-deploy-key.blade.php b/resources/views/livewire/project/new/github-private-repository-deploy-key.blade.php index d0501eb06..7ea57360c 100644 --- a/resources/views/livewire/project/new/github-private-repository-deploy-key.blade.php +++ b/resources/views/livewire/project/new/github-private-repository-deploy-key.blade.php @@ -1,6 +1,6 @@

Create a new Application

-
Deploy any public or private Git repositories through a Deploy Key.
+
Deploy any public or private Git repositories through a Deploy Key.
@if ($current_step === 'private_keys')
    diff --git a/resources/views/livewire/project/new/select.blade.php b/resources/views/livewire/project/new/select.blade.php index ea16779c5..fa07a0ec1 100644 --- a/resources/views/livewire/project/new/select.blade.php +++ b/resources/views/livewire/project/new/select.blade.php @@ -1,16 +1,16 @@

    New Resource

    Deploy resources, like Applications, Databases, Services...
    -
    +
    @if ($current_step === 'type')
    • Select Source Type
    • Select a Server
    • Select a Destination
    -

    Applications

    -
    -
    Applications +
    +
    @@ -21,7 +21,7 @@
    -
    @@ -32,7 +32,7 @@
    -
    @@ -44,8 +44,21 @@
    -

    Databases

    -
    +
    +
    +
    +
    + Based on a Dockerfile +
    +
    + You can deploy a simple Dockerfile, without Git. +
    +
    +
    +
    +

    Databases

    +
    diff --git a/resources/views/livewire/project/new/simple-dockerfile.blade.php b/resources/views/livewire/project/new/simple-dockerfile.blade.php new file mode 100644 index 000000000..0ebac6577 --- /dev/null +++ b/resources/views/livewire/project/new/simple-dockerfile.blade.php @@ -0,0 +1,15 @@ +
    +

    Create a new Application

    +
    You can deploy a simple Dockerfile, without Git.
    +
    +
    +

    Dockerfile

    + Save +
    + +
    +
    diff --git a/resources/views/livewire/project/shared/environment-variable/show.blade.php b/resources/views/livewire/project/shared/environment-variable/show.blade.php index a36d8be82..6c74ceb37 100644 --- a/resources/views/livewire/project/shared/environment-variable/show.blade.php +++ b/resources/views/livewire/project/shared/environment-variable/show.blade.php @@ -8,9 +8,7 @@ class="font-bold text-warning">({{ $env->key }})?

    - @if (data_get($parameters, 'application_uuid')) - - @endif +
    Update diff --git a/resources/views/project/application/configuration.blade.php b/resources/views/project/application/configuration.blade.php index 27e7baea0..3d43a349c 100644 --- a/resources/views/project/application/configuration.blade.php +++ b/resources/views/project/application/configuration.blade.php @@ -9,8 +9,10 @@ @click.prevent="activeTab = 'environment-variables'; window.location.hash = 'environment-variables'" href="#">Environment Variables - Source + @if ($application->git_based()) + Source + @endif Destination @@ -18,10 +20,12 @@ Storages - Previews - Deployments - + @if ($application->git_based()) + Previews + Deployments + + @endif Rollback @@ -40,9 +44,11 @@
    -
    - -
    + @if ($application->git_based()) +
    + +
    + @endif
    diff --git a/resources/views/project/new.blade.php b/resources/views/project/new.blade.php index b3b13215e..fcf6e999b 100644 --- a/resources/views/project/new.blade.php +++ b/resources/views/project/new.blade.php @@ -5,6 +5,8 @@ @elseif ($type === 'private-deploy-key') + @elseif ($type === 'dockerfile') + @else @endif