diff --git a/app/Jobs/DeployApplicationJob.php b/app/Jobs/DeployApplicationJob.php index 2e1ee1aac..3e2e86735 100644 --- a/app/Jobs/DeployApplicationJob.php +++ b/app/Jobs/DeployApplicationJob.php @@ -14,6 +14,7 @@ use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Http; use Lcobucci\JWT\Encoding\ChainedFormatter; use Lcobucci\JWT\Encoding\JoseEncoder; @@ -48,7 +49,7 @@ class DeployApplicationJob implements ShouldQueue $server = $this->destination->server; - $private_key_location = savePrivateKey($server); + $private_key_location = savePrivateKeyForServer($server); $remoteProcessArgs = new RemoteProcessArgs( server_ip: $server->ip, @@ -97,11 +98,17 @@ class DeployApplicationJob implements ShouldQueue // Import git repository $this->executeNow([ - "echo -n 'Importing {$this->application->git_repository}:{$this->application->git_branch} to {$this->workdir}... '", - $this->gitImport(), - "echo 'Done.'" + "echo -n 'Importing {$this->application->git_repository}:{$this->application->git_branch} to {$this->workdir}... '" + ]); + + $this->executeNow([ + ...$this->gitImport(), ], 'importing_git_repository'); + $this->executeNow([ + "echo 'Done.'" + ]); + // Get git commit $this->executeNow([$this->execute_in_builder("cd {$this->workdir} && git rev-parse HEAD")], 'commit_sha', hideFromOutput: true); $this->git_commit = $this->activity->properties->get('commit_sha'); @@ -134,12 +141,10 @@ class DeployApplicationJob implements ShouldQueue ]); $this->executeNow([ "echo -n 'Starting new container... '", - $this->execute_in_builder("docker compose --project-directory {$this->workdir} up -d >/dev/null 2>&1"), + $this->execute_in_builder("docker compose --project-directory {$this->workdir} up -d >/dev/null"), "echo 'Done. 🎉'", - ], setStatus: true); - $this->executeNow([ "docker stop -t 0 {$this->deployment_uuid} >/dev/null" - ]); + ], setStatus: true); } private function execute_in_builder(string $command) @@ -149,7 +154,6 @@ class DeployApplicationJob implements ShouldQueue private function generate_docker_compose() { - $docker_compose = [ 'version' => '3.8', 'services' => [ @@ -157,6 +161,9 @@ class DeployApplicationJob implements ShouldQueue 'image' => "{$this->application->uuid}:$this->git_commit", 'container_name' => $this->application->uuid, 'restart' => 'always', + 'environment' => [ + 'PORT' => $this->application->ports_exposes[0] + ], 'labels' => $this->set_labels_for_applications(), 'expose' => $this->application->ports_exposes, 'networks' => [ @@ -254,9 +261,13 @@ class DeployApplicationJob implements ShouldQueue return $labels; } - private function executeNow(array $command, string $propertyName = null, bool $hideFromOutput = false, $setStatus = false) + private function executeNow(array|Collection $command, string $propertyName = null, bool $hideFromOutput = false, $setStatus = false) { - $commandText = collect($command)->implode("\n"); + if ($command instanceof Collection) { + $commandText = $command->implode("\n"); + } else { + $commandText = collect($command)->implode("\n"); + } $this->activity->properties = $this->activity->properties->merge([ 'command' => $commandText, @@ -285,12 +296,27 @@ class DeployApplicationJob implements ShouldQueue $url = parse_url(filter_var($source_html_url, FILTER_SANITIZE_URL)); $source_html_url_host = $url['host']; $source_html_url_scheme = $url['scheme']; + if ($this->application->source->getMorphClass() == 'App\Models\GithubApp') { if ($this->source->is_public) { - return $this->execute_in_builder("git clone -q -b {$this->application->git_branch} {$this->source->html_url}/{$this->application->git_repository}.git {$this->workdir}"); + return [ + $this->execute_in_builder("git clone -q -b {$this->application->git_branch} {$this->source->html_url}/{$this->application->git_repository}.git {$this->workdir}") + ]; } else { - $github_access_token = $this->generate_jwt_token_for_github(); - return $this->execute_in_builder("git clone -q -b {$this->application->git_branch} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$this->application->git_repository}.git {$this->workdir}"); + if (!$this->application->source->app_id) { + $private_key = base64_encode($this->application->source->privateKey->private_key); + return [ + $this->execute_in_builder("mkdir -p /root/.ssh"), + $this->execute_in_builder("echo '{$private_key}' | base64 -d > /root/.ssh/id_rsa"), + $this->execute_in_builder("chmod 600 /root/.ssh/id_rsa"), + $this->execute_in_builder("GIT_SSH_COMMAND=\"ssh -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git clone -q -b {$this->application->git_branch} git@$source_html_url_host:{$this->application->git_repository}.git {$this->workdir}") + ]; + } else { + $github_access_token = $this->generate_jwt_token_for_github(); + return [ + $this->execute_in_builder("git clone -q -b {$this->application->git_branch} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$this->application->git_repository}.git {$this->workdir}") + ]; + } } } } diff --git a/bootstrap/helpers.php b/bootstrap/helpers.php index ef1183cf7..6eaccda06 100644 --- a/bootstrap/helpers.php +++ b/bootstrap/helpers.php @@ -29,7 +29,7 @@ if (!function_exists('remoteProcess')) { // @TODO: Check if the user has access to this server // checkTeam($server->team_id); - $private_key_location = savePrivateKey($server); + $private_key_location = savePrivateKeyForServer($server); return resolve(DispatchRemoteProcess::class, [ 'remoteProcessArgs' => new RemoteProcessArgs( @@ -56,8 +56,8 @@ if (!function_exists('remoteProcess')) { // } // } -if (!function_exists('savePrivateKey')) { - function savePrivateKey(Server $server) +if (!function_exists('savePrivateKeyForServer')) { + function savePrivateKeyForServer(Server $server) { $temp_file = 'id.rsa_' . 'root' . '@' . $server->ip; Storage::disk('local')->put($temp_file, $server->privateKey->private_key, 'private'); @@ -118,9 +118,10 @@ if (!function_exists('formatDockerLabelsToJson')) { } } if (!function_exists('runRemoteCommandSync')) { - function runRemoteCommandSync($server, array $command) { + function runRemoteCommandSync($server, array $command) + { $command_string = implode("\n", $command); - $private_key_location = savePrivateKey($server); + $private_key_location = savePrivateKeyForServer($server); $ssh_command = generateSshCommand($private_key_location, $server->ip, $server->user, $server->port, $command_string); $process = Process::run($ssh_command); $output = trim($process->output()); diff --git a/database/migrations/2023_03_28_083723_create_github_apps_table.php b/database/migrations/2023_03_28_083723_create_github_apps_table.php index 049c8f366..5273f9306 100644 --- a/database/migrations/2023_03_28_083723_create_github_apps_table.php +++ b/database/migrations/2023_03_28_083723_create_github_apps_table.php @@ -21,8 +21,6 @@ return new class extends Migration $table->string('html_url'); $table->integer('custom_port')->default(22); $table->string('custom_user')->default('git'); - $table->boolean('is_system_wide')->default(false); - $table->boolean('is_public')->default(false); $table->integer('app_id')->nullable(); $table->integer('installation_id')->nullable(); @@ -30,6 +28,9 @@ return new class extends Migration $table->longText('client_secret')->nullable(); $table->longText('webhook_secret')->nullable(); + $table->boolean('is_system_wide')->default(false); + $table->boolean('is_public')->default(false); + $table->foreignId('private_key_id')->nullable(); $table->foreignId('team_id'); $table->timestamps(); diff --git a/database/seeders/ApplicationSeeder.php b/database/seeders/ApplicationSeeder.php index c3c6b16ae..6b5b4a518 100644 --- a/database/seeders/ApplicationSeeder.php +++ b/database/seeders/ApplicationSeeder.php @@ -22,12 +22,12 @@ class ApplicationSeeder extends Seeder $standalone_docker_1 = StandaloneDocker::find(1); $swarm_docker_1 = SwarmDocker::find(1); - $github_public_source = GithubApp::find(1); - $github_private_source = GithubApp::find(2); + $github_public_source = GithubApp::where('name', 'Public GitHub')->first(); + $github_private_source = GithubApp::where('name', 'coolify-laravel-development-private-github')->first(); + $github_private_source_with_deploy_key = GithubApp::where('name', 'Private GitHub (deployment key)')->first(); $pv_storage = LocalPersistentVolume::find(1); Application::create([ - 'id' => 1, 'name' => 'Public application (from GitHub)', 'git_repository' => 'coollabsio/coolify-examples', 'git_branch' => 'nodejs-fastify', @@ -41,7 +41,6 @@ class ApplicationSeeder extends Seeder 'source_type' => GithubApp::class, ]); Application::create([ - 'id' => 2, 'name' => 'Private application (through GitHub App)', 'git_repository' => 'coollabsio/nodejs-example', 'git_branch' => 'main', @@ -54,5 +53,18 @@ class ApplicationSeeder extends Seeder 'source_id' => $github_private_source->id, 'source_type' => GithubApp::class, ]); + Application::create([ + 'name' => 'Public application (from GitHub through Deploy Key)', + 'git_repository' => 'coollabsio/php', + 'git_branch' => 'main', + 'build_pack' => 'nixpacks', + 'ports_exposes' => '80,3000', + 'ports_mappings' => '3002:80', + 'environment_id' => $environment_1->id, + 'destination_id' => $standalone_docker_1->id, + 'destination_type' => StandaloneDocker::class, + 'source_id' => $github_private_source_with_deploy_key->id, + 'source_type' => GithubApp::class, + ]); } } diff --git a/database/seeders/GithubAppSeeder.php b/database/seeders/GithubAppSeeder.php index d2b587f7e..52a4aa043 100644 --- a/database/seeders/GithubAppSeeder.php +++ b/database/seeders/GithubAppSeeder.php @@ -16,9 +16,9 @@ class GithubAppSeeder extends Seeder public function run(): void { $root_team = Team::find(1); + $private_key_1 = PrivateKey::find(1); $private_key_2 = PrivateKey::find(2); GithubApp::create([ - 'id' => 1, 'name' => 'Public GitHub', 'api_url' => 'https://api.github.com', 'html_url' => 'https://github.com', @@ -26,7 +26,6 @@ class GithubAppSeeder extends Seeder 'team_id' => $root_team->id, ]); GithubApp::create([ - 'id' => 2, 'name' => 'coolify-laravel-development-private-github', 'api_url' => 'https://api.github.com', 'html_url' => 'https://github.com', @@ -39,5 +38,13 @@ class GithubAppSeeder extends Seeder 'private_key_id' => $private_key_2->id, 'team_id' => $root_team->id, ]); + GithubApp::create([ + 'name' => 'Private GitHub (deployment key)', + 'api_url' => 'https://api.github.com', + 'html_url' => 'https://github.com', + 'is_public' => false, + 'private_key_id' => $private_key_1->id, + 'team_id' => $root_team->id, + ]); } }