feat: deploy key based deployments
This commit is contained in:
parent
fe6ecd465e
commit
946758e219
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Project\New;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Project;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\SwarmDocker;
|
||||
use Livewire\Component;
|
||||
use Spatie\Url\Url;
|
||||
|
||||
class GithubPrivateRepositoryDeployKey extends Component
|
||||
{
|
||||
public $parameters;
|
||||
public $private_keys;
|
||||
public int $private_key_id;
|
||||
public string $repository_url;
|
||||
|
||||
public $servers;
|
||||
public $standalone_docker;
|
||||
public $swarm_docker;
|
||||
public $chosenServer;
|
||||
public $chosenDestination;
|
||||
|
||||
public int $port = 3000;
|
||||
public string $type;
|
||||
|
||||
public bool $is_static = false;
|
||||
public null|string $publish_directory = null;
|
||||
protected $rules = [
|
||||
'repository_url' => 'required|url',
|
||||
'port' => 'required|numeric',
|
||||
'is_static' => 'required|boolean',
|
||||
'publish_directory' => 'nullable|string',
|
||||
];
|
||||
public function mount()
|
||||
{
|
||||
if (config('app.env') === 'local') {
|
||||
$this->repository_url = 'https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify';
|
||||
}
|
||||
$this->parameters = getParameters();
|
||||
$this->private_keys = PrivateKey::where('team_id', session('currentTeam')->id)->get();
|
||||
$this->servers = session('currentTeam')->load(['servers'])->servers;
|
||||
}
|
||||
public function chooseServer($server)
|
||||
{
|
||||
$this->chosenServer = $server;
|
||||
$this->standalone_docker = StandaloneDocker::where('server_id', $server['id'])->get();
|
||||
$this->swarm_docker = SwarmDocker::where('server_id', $server['id'])->get();
|
||||
}
|
||||
public function setDestination($destination_uuid, $destination_type)
|
||||
{
|
||||
$class = "App\Models\\{$destination_type}";
|
||||
$instance = new $class;
|
||||
$this->chosenDestination = $instance::where('uuid', $destination_uuid)->first();
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
if ($this->is_static) {
|
||||
$this->port = 80;
|
||||
$this->publish_directory = '/dist';
|
||||
} else {
|
||||
$this->port = 3000;
|
||||
$this->publish_directory = null;
|
||||
}
|
||||
}
|
||||
public function setPrivateKey($private_key_id)
|
||||
{
|
||||
$this->private_key_id = $private_key_id;
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
$url = Url::fromString($this->repository_url);
|
||||
$git_host = $url->getHost();
|
||||
$git_repository = $url->getSegment(1) . '/' . $url->getSegment(2);
|
||||
$git_branch = $url->getSegment(4) ?? 'main';
|
||||
|
||||
if ($this->type === 'project') {
|
||||
$project = Project::create([
|
||||
'name' => generateRandomName(),
|
||||
'team_id' => session('currentTeam')->id,
|
||||
]);
|
||||
$environment = $project->environments->first();
|
||||
} else {
|
||||
$project = Project::where('uuid', $this->parameters['project_uuid'])->firstOrFail();
|
||||
$environment = $project->environments->where('name', $this->parameters['environment_name'])->firstOrFail();
|
||||
}
|
||||
$application_init = [
|
||||
'name' => generateRandomName(),
|
||||
'git_repository' => $git_repository,
|
||||
'git_branch' => $git_branch,
|
||||
'git_full_url' => "git@$git_host:$git_repository.git",
|
||||
'build_pack' => 'nixpacks',
|
||||
'ports_exposes' => $this->port,
|
||||
'publish_directory' => $this->publish_directory,
|
||||
'environment_id' => $environment->id,
|
||||
'destination_id' => $this->chosenDestination->id,
|
||||
'destination_type' => $this->chosenDestination->getMorphClass(),
|
||||
'private_key_id' => $this->private_key_id,
|
||||
];
|
||||
$application = Application::create($application_init);
|
||||
$application->settings->is_static = $this->is_static;
|
||||
$application->settings->save();
|
||||
|
||||
return redirect()->route('project.application.configuration', [
|
||||
'project_uuid' => $project->uuid,
|
||||
'environment_name' => $environment->name,
|
||||
'application_uuid' => $application->uuid,
|
||||
]);
|
||||
}
|
||||
}
|
@ -14,8 +14,8 @@
|
||||
|
||||
class PublicGitRepository extends Component
|
||||
{
|
||||
public string $public_repository_url;
|
||||
public int $port;
|
||||
public string $repository_url;
|
||||
public int $port = 3000;
|
||||
public string $type;
|
||||
public $parameters;
|
||||
|
||||
@ -31,7 +31,7 @@ class PublicGitRepository extends Component
|
||||
public null|string $publish_directory = null;
|
||||
|
||||
protected $rules = [
|
||||
'public_repository_url' => 'required|url',
|
||||
'repository_url' => 'required|url',
|
||||
'port' => 'required|numeric',
|
||||
'is_static' => 'required|boolean',
|
||||
'publish_directory' => 'nullable|string',
|
||||
@ -39,7 +39,7 @@ class PublicGitRepository extends Component
|
||||
public function mount()
|
||||
{
|
||||
if (config('app.env') === 'local') {
|
||||
$this->public_repository_url = 'https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify';
|
||||
$this->repository_url = 'https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify';
|
||||
$this->port = 3000;
|
||||
}
|
||||
$this->parameters = getParameters();
|
||||
@ -73,7 +73,7 @@ public function instantSave()
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
$url = Url::fromString($this->public_repository_url);
|
||||
$url = Url::fromString($this->repository_url);
|
||||
$git_host = $url->getHost();
|
||||
$git_repository = $url->getSegment(1) . '/' . $url->getSegment(2);
|
||||
$git_branch = $url->getSegment(4) ?? 'main';
|
||||
|
@ -107,7 +107,10 @@ public function handle(): void
|
||||
{
|
||||
try {
|
||||
$coolify_instance_settings = InstanceSettings::find(0);
|
||||
$this->source = $this->application->source->getMorphClass()::where('id', $this->application->source->id)->first();
|
||||
$deployment_type = $this->application->deploymentType();
|
||||
if ($this->application->deploymentType() === 'source') {
|
||||
$this->source = $this->application->source->getMorphClass()::where('id', $this->application->source->id)->first();
|
||||
}
|
||||
|
||||
// Get Wildcard Domain
|
||||
$project_wildcard_domain = data_get($this->application, 'environment.project.settings.wildcard_domain');
|
||||
@ -443,29 +446,19 @@ private function setGitImportSettings($git_clone_command)
|
||||
}
|
||||
private function gitImport()
|
||||
{
|
||||
$source_html_url = data_get($this->application, 'source.html_url');
|
||||
$url = parse_url(filter_var($source_html_url, FILTER_SANITIZE_URL));
|
||||
$source_html_url_host = $url['host'];
|
||||
$source_html_url_scheme = $url['scheme'];
|
||||
|
||||
$git_clone_command = "git clone -q -b {$this->application->git_branch}";
|
||||
if ($this->source->getMorphClass() == 'App\Models\GithubApp') {
|
||||
if ($this->source->is_public) {
|
||||
$git_clone_command = "{$git_clone_command} {$this->source->html_url}/{$this->application->git_repository} {$this->workdir}";
|
||||
$git_clone_command = $this->setGitImportSettings($git_clone_command);
|
||||
return [
|
||||
$this->execute_in_builder($git_clone_command)
|
||||
];
|
||||
} else {
|
||||
if (!$this->source->app_id) {
|
||||
$private_key = base64_encode($this->source->privateKey->private_key);
|
||||
|
||||
$git_clone_command = "GIT_SSH_COMMAND=\"ssh -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" {$git_clone_command} git@$source_html_url_host:{$this->application->git_repository}.git {$this->workdir}";
|
||||
if ($this->application->deploymentType() === 'source') {
|
||||
$source_html_url = data_get($this->application, 'source.html_url');
|
||||
$url = parse_url(filter_var($source_html_url, FILTER_SANITIZE_URL));
|
||||
$source_html_url_host = $url['host'];
|
||||
$source_html_url_scheme = $url['scheme'];
|
||||
|
||||
if ($this->source->getMorphClass() == 'App\Models\GithubApp') {
|
||||
if ($this->source->is_public) {
|
||||
$git_clone_command = "{$git_clone_command} {$this->source->html_url}/{$this->application->git_repository} {$this->workdir}";
|
||||
$git_clone_command = $this->setGitImportSettings($git_clone_command);
|
||||
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_clone_command)
|
||||
];
|
||||
} else {
|
||||
@ -476,6 +469,17 @@ private function gitImport()
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->application->deploymentType() === 'deploy_key') {
|
||||
$private_key = base64_encode($this->application->private_key->private_key);
|
||||
$git_clone_command = "GIT_SSH_COMMAND=\"ssh -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" {$git_clone_command} {$this->application->git_full_url} {$this->workdir}";
|
||||
$git_clone_command = $this->setGitImportSettings($git_clone_command);
|
||||
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_clone_command)
|
||||
];
|
||||
}
|
||||
}
|
||||
private function nixpacks_build_cmd()
|
||||
{
|
||||
|
@ -28,6 +28,7 @@ protected static function booted()
|
||||
'description',
|
||||
'git_repository',
|
||||
'git_branch',
|
||||
'git_full_url',
|
||||
'build_pack',
|
||||
'environment_id',
|
||||
'destination_id',
|
||||
@ -37,6 +38,7 @@ protected static function booted()
|
||||
'ports_mappings',
|
||||
'ports_exposes',
|
||||
'publish_directory',
|
||||
'private_key_id'
|
||||
];
|
||||
public function publishDirectory(): Attribute
|
||||
{
|
||||
@ -113,6 +115,10 @@ public function nixpacks_environment_variables(): HasMany
|
||||
{
|
||||
return $this->hasMany(EnvironmentVariable::class)->where('key', 'like', 'NIXPACKS_%');
|
||||
}
|
||||
public function private_key()
|
||||
{
|
||||
return $this->belongsTo(PrivateKey::class);
|
||||
}
|
||||
public function environment()
|
||||
{
|
||||
return $this->belongsTo(Environment::class);
|
||||
@ -149,4 +155,14 @@ public function isDeployable(): bool
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public function deploymentType()
|
||||
{
|
||||
if (data_get($this, 'source')) {
|
||||
return 'source';
|
||||
}
|
||||
if (data_get($this, 'private_key_id')) {
|
||||
return 'deploy_key';
|
||||
}
|
||||
throw new \Exception('No deployment type found');
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ public function up(): void
|
||||
$table->string('git_repository');
|
||||
$table->string('git_branch');
|
||||
$table->string('git_commit_sha')->default('HEAD');
|
||||
$table->string('git_full_url')->nullable();
|
||||
|
||||
$table->string('docker_registry_image_name')->nullable();
|
||||
$table->string('docker_registry_image_tag')->nullable();
|
||||
@ -55,8 +56,9 @@ public function up(): void
|
||||
$table->string('status')->default('exited');
|
||||
|
||||
$table->nullableMorphs('destination');
|
||||
$table->morphs('source');
|
||||
$table->nullableMorphs('source');
|
||||
|
||||
$table->foreignId('private_key_id')->nullable();
|
||||
$table->foreignId('environment_id');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
@ -0,0 +1,84 @@
|
||||
<div>
|
||||
@if ($servers->count() > 0)
|
||||
<h1>Choose a server</h1>
|
||||
@endif
|
||||
@forelse ($servers as $server)
|
||||
@if ($chosenServer && $chosenServer['id'] === $server->id)
|
||||
<x-inputs.button class="bg-blue-500" wire:click="chooseServer({{ $server }})">{{ $server->name }}
|
||||
</x-inputs.button>
|
||||
@else
|
||||
<x-inputs.button wire:click="chooseServer({{ $server }})">{{ $server->name }}</x-inputs.button>
|
||||
@endif
|
||||
@empty
|
||||
No servers found.
|
||||
<p>Did you forget to add a destination on the server?</p>
|
||||
@endforelse
|
||||
|
||||
@isset($chosenServer)
|
||||
@if ($standalone_docker->count() > 0 || $swarm_docker->count() > 0)
|
||||
<h1>Choose a destination</h1>
|
||||
<div>
|
||||
@foreach ($standalone_docker as $standalone)
|
||||
@if ($chosenDestination?->uuid == $standalone->uuid)
|
||||
<x-inputs.button class="bg-blue-500"
|
||||
wire:click="setDestination('{{ $standalone->uuid }}','StandaloneDocker')">
|
||||
{{ $standalone->network }}</x-inputs.button>
|
||||
@else
|
||||
<x-inputs.button wire:click="setDestination('{{ $standalone->uuid }}','StandaloneDocker')">
|
||||
{{ $standalone->network }}</x-inputs.button>
|
||||
@endif
|
||||
@endforeach
|
||||
@foreach ($swarm_docker as $standalone)
|
||||
@if ($chosenDestination?->uuid == $standalone->uuid)
|
||||
<x-inputs.button class="bg-blue-500"
|
||||
wire:click="setDestination('{{ $standalone->uuid }}','SwarmDocker')">
|
||||
{{ $standalone->network }}</x-inputs.button>
|
||||
@else
|
||||
<x-inputs.button wire:click="setDestination('{{ $standalone->uuid }}','SwarmDocker')">
|
||||
{{ $standalone->uuid }}</x-inputs.button>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
<div>
|
||||
<a href="{{ route('destination.new', ['server_id' => $chosenServer['id']]) }}">Add
|
||||
a new
|
||||
destination</a>
|
||||
</div>
|
||||
@else
|
||||
<h1>No destinations found on this server.</h1>
|
||||
<a href="{{ route('destination.new', ['server_id' => $chosenServer['id']]) }}">Add
|
||||
a
|
||||
destination</a>
|
||||
@endif
|
||||
|
||||
@endisset
|
||||
@isset($chosenDestination)
|
||||
<h1>Choose a repository</h1>
|
||||
<form wire:submit.prevent='submit'>
|
||||
<div class="flex items-end gap-2 pb-2">
|
||||
<x-inputs.input class="w-96" id="repository_url" label="Repository URL" />
|
||||
@if ($is_static)
|
||||
<x-inputs.input id="publish_directory" label="Publish Directory" />
|
||||
@else
|
||||
<x-inputs.input type="number" id="port" label="Port" :readonly="$is_static" />
|
||||
@endif
|
||||
<x-inputs.input instantSave type="checkbox" id="is_static" label="Static Site?" />
|
||||
</div>
|
||||
<x-inputs.button type="submit">
|
||||
Submit
|
||||
</x-inputs.button>
|
||||
</form>
|
||||
<div>
|
||||
<h1>Select a private key</h1>
|
||||
@foreach ($private_keys as $key)
|
||||
@if ($private_key_id == $key->id)
|
||||
<x-inputs.button class="bg-blue-500" wire:click.defer="setPrivateKey('{{ $key->id }}')">
|
||||
{{ $key->name }}</x-inputs.button>
|
||||
@else
|
||||
<x-inputs.button wire:click.defer="setPrivateKey('{{ $key->id }}')">{{ $key->name }}
|
||||
</x-inputs.button>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
@endisset
|
||||
</div>
|
@ -56,7 +56,7 @@
|
||||
@isset($chosenDestination)
|
||||
<h1>Choose a repository</h1>
|
||||
<form class="flex flex-col gap-2 w-96" wire:submit.prevent='submit'>
|
||||
<x-inputs.input class="w-96" id="public_repository_url" label="Repository URL" />
|
||||
<x-inputs.input class="w-96" id="repository_url" label="Repository URL" />
|
||||
<x-inputs.input instantSave type="checkbox" id="is_static" label="Static Site?" />
|
||||
@if ($is_static)
|
||||
<x-inputs.input id="publish_directory" label="Publish Directory" />
|
||||
|
@ -7,7 +7,10 @@
|
||||
<div x-data="{ activeTab: 'choose' }">
|
||||
<div class="flex flex-col w-64 gap-2 mb-10">
|
||||
<x-inputs.button @click.prevent="activeTab = 'public-repo'">Public Repository</x-inputs.button>
|
||||
<x-inputs.button @click.prevent="activeTab = 'github-private-repo'">Private Repository (GitHub App)
|
||||
<x-inputs.button @click.prevent="activeTab = 'github-private-repo'">Private Repository (with GitHub App)
|
||||
</x-inputs.button>
|
||||
<x-inputs.button @click.prevent="activeTab = 'github-private-repo-deploy-key'">Private Repository (with
|
||||
Deploy Key)
|
||||
</x-inputs.button>
|
||||
@if ($type === 'project')
|
||||
<livewire:project.new.empty-project />
|
||||
@ -20,5 +23,8 @@
|
||||
<div x-cloak x-show="activeTab === 'github-private-repo'">
|
||||
<livewire:project.new.github-private-repository :type="$type" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'github-private-repo-deploy-key'">
|
||||
<livewire:project.new.github-private-repository-deploy-key :type="$type" />
|
||||
</div>
|
||||
</div>
|
||||
</x-layout>
|
||||
|
Loading…
Reference in New Issue
Block a user