fix: bitbucket manual deployments

This commit is contained in:
Andras Bacsai 2024-01-29 10:43:18 +01:00
parent 07d8461f96
commit 987409bae4
5 changed files with 166 additions and 47 deletions

View File

@ -158,7 +158,9 @@ public function __construct(int $application_deployment_queue_id)
$this->preview->fqdn = $preview_fqdn; $this->preview->fqdn = $preview_fqdn;
$this->preview->save(); $this->preview->save();
} }
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::IN_PROGRESS); if ($this->application->is_github_based()) {
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::IN_PROGRESS);
}
} }
} }
@ -230,6 +232,8 @@ public function handle(): void
$this->next(ApplicationDeploymentStatus::FINISHED->value); $this->next(ApplicationDeploymentStatus::FINISHED->value);
$this->application->isConfigurationChanged(false); $this->application->isConfigurationChanged(false);
return; return;
} else if ($this->pull_request_id !== 0) {
$this->deploy_pull_request();
} else if ($this->application->dockerfile) { } else if ($this->application->dockerfile) {
$this->deploy_simple_dockerfile(); $this->deploy_simple_dockerfile();
} else if ($this->application->build_pack === 'dockercompose') { } else if ($this->application->build_pack === 'dockercompose') {
@ -241,11 +245,7 @@ public function handle(): void
} else if ($this->application->build_pack === 'static') { } else if ($this->application->build_pack === 'static') {
$this->deploy_static_buildpack(); $this->deploy_static_buildpack();
} else { } else {
if ($this->pull_request_id !== 0) { $this->deploy_nixpacks_buildpack();
$this->deploy_pull_request();
} else {
$this->deploy_nixpacks_buildpack();
}
} }
if ($this->server->isProxyShouldRun()) { if ($this->server->isProxyShouldRun()) {
dispatch(new ContainerStatusJob($this->server)); dispatch(new ContainerStatusJob($this->server));
@ -256,13 +256,18 @@ public function handle(): void
} }
$this->next(ApplicationDeploymentStatus::FINISHED->value); $this->next(ApplicationDeploymentStatus::FINISHED->value);
if ($this->pull_request_id !== 0) { if ($this->pull_request_id !== 0) {
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::FINISHED); if ($this->application->is_github_based()) {
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::FINISHED);
}
} }
$this->application->isConfigurationChanged(true); $this->application->isConfigurationChanged(true);
} catch (Exception $e) { } catch (Exception $e) {
if ($this->pull_request_id !== 0) { if ($this->pull_request_id !== 0) {
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::ERROR); if ($this->application->is_github_based()) {
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::ERROR);
}
} }
ray($e);
$this->fail($e); $this->fail($e);
throw $e; throw $e;
} finally { } finally {
@ -729,10 +734,8 @@ private function deploy_pull_request()
$this->generate_nixpacks_confs(); $this->generate_nixpacks_confs();
} }
$this->generate_compose_file(); $this->generate_compose_file();
// Needs separate preview variables
$this->generate_build_env_variables(); $this->generate_build_env_variables();
if ($this->application->build_pack !== 'nixpacks') { if ($this->application->build_pack === 'dockerfile') {
$this->add_build_env_variables_to_dockerfile(); $this->add_build_env_variables_to_dockerfile();
} }
$this->build_image(); $this->build_image();
@ -868,7 +871,12 @@ private function clone_repository()
private function generate_git_import_commands() private function generate_git_import_commands()
{ {
['commands' => $commands, 'branch' => $this->branch, 'fullRepoUrl' => $this->fullRepoUrl] = $this->application->generateGitImportCommands($this->deployment_uuid, $this->pull_request_id, $this->git_type); ['commands' => $commands, 'branch' => $this->branch, 'fullRepoUrl' => $this->fullRepoUrl] = $this->application->generateGitImportCommands(
deployment_uuid: $this->deployment_uuid,
pull_request_id: $this->pull_request_id,
git_type: $this->git_type,
commit: $this->commit
);
return $commands; return $commands;
} }

View File

@ -111,6 +111,13 @@ public function generateImageNames(string $commit, int $pullRequestId)
} }
// End of build packs / deployment types // End of build packs / deployment types
public function is_github_based(): bool
{
if (data_get($this, 'source')) {
return true;
}
return false;
}
public function link() public function link()
{ {
if (data_get($this, 'environment.project.uuid')) { if (data_get($this, 'environment.project.uuid')) {
@ -807,7 +814,7 @@ function setGitImportSettings(string $deployment_uuid, string $git_clone_command
} }
return $git_clone_command; return $git_clone_command;
} }
function generateGitImportCommands(string $deployment_uuid, int $pull_request_id = 0, ?string $git_type = null, bool $exec_in_docker = true, bool $only_checkout = false, ?string $custom_base_dir = null) function generateGitImportCommands(string $deployment_uuid, int $pull_request_id = 0, ?string $git_type = null, bool $exec_in_docker = true, bool $only_checkout = false, ?string $custom_base_dir = null, ?string $commit = null)
{ {
$branch = $this->git_branch; $branch = $this->git_branch;
['repository' => $customRepository, 'port' => $customPort] = $this->customRepository(); ['repository' => $customRepository, 'port' => $customPort] = $this->customRepository();
@ -820,7 +827,6 @@ function generateGitImportCommands(string $deployment_uuid, int $pull_request_id
if ($pull_request_id !== 0) { if ($pull_request_id !== 0) {
$pr_branch_name = "pr-{$pull_request_id}-coolify"; $pr_branch_name = "pr-{$pull_request_id}-coolify";
} }
if ($this->deploymentType() === 'source') { if ($this->deploymentType() === 'source') {
$source_html_url = data_get($this, 'source.html_url'); $source_html_url = data_get($this, 'source.html_url');
$url = parse_url(filter_var($source_html_url, FILTER_SANITIZE_URL)); $url = parse_url(filter_var($source_html_url, FILTER_SANITIZE_URL));
@ -926,6 +932,34 @@ function generateGitImportCommands(string $deployment_uuid, int $pull_request_id
$fullRepoUrl = $customRepository; $fullRepoUrl = $customRepository;
$git_clone_command = "{$git_clone_command} {$customRepository} {$baseDir}"; $git_clone_command = "{$git_clone_command} {$customRepository} {$baseDir}";
$git_clone_command = $this->setGitImportSettings($deployment_uuid, $git_clone_command); $git_clone_command = $this->setGitImportSettings($deployment_uuid, $git_clone_command);
if ($pull_request_id !== 0) {
if ($git_type === 'gitlab') {
$branch = "merge-requests/{$pull_request_id}/head:$pr_branch_name";
if ($exec_in_docker) {
$commands->push(executeInDocker($deployment_uuid, "echo 'Checking out $branch'"));
} else {
$commands->push("echo 'Checking out $branch'");
}
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && git checkout $pr_branch_name";
} else if ($git_type === 'github') {
$branch = "pull/{$pull_request_id}/head:$pr_branch_name";
if ($exec_in_docker) {
$commands->push(executeInDocker($deployment_uuid, "echo 'Checking out $branch'"));
} else {
$commands->push("echo 'Checking out $branch'");
}
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && git checkout $pr_branch_name";
} else if ($git_type === 'bitbucket') {
if ($exec_in_docker) {
$commands->push(executeInDocker($deployment_uuid, "echo 'Checking out $branch'"));
} else {
$commands->push("echo 'Checking out $branch'");
}
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git checkout $commit";
}
}
if ($exec_in_docker) { if ($exec_in_docker) {
$commands->push(executeInDocker($deployment_uuid, $git_clone_command)); $commands->push(executeInDocker($deployment_uuid, $git_clone_command));
} else { } else {

View File

@ -39,7 +39,7 @@
<a :class="activeTab === 'webhooks' && 'text-white'" <a :class="activeTab === 'webhooks' && 'text-white'"
@click.prevent="activeTab = 'webhooks'; window.location.hash = 'webhooks'" href="#">Webhooks @click.prevent="activeTab = 'webhooks'; window.location.hash = 'webhooks'" href="#">Webhooks
</a> </a>
@if ($application->git_based() && $application->build_pack !== 'static') @if ($application->git_based())
<a :class="activeTab === 'previews' && 'text-white'" <a :class="activeTab === 'previews' && 'text-white'"
@click.prevent="activeTab = 'previews'; window.location.hash = 'previews'" href="#">Preview @click.prevent="activeTab = 'previews'; window.location.hash = 'previews'" href="#">Preview
Deployments Deployments

View File

@ -1,11 +1,13 @@
<div> <div>
<livewire:project.application.preview.form :application="$application" /> <livewire:project.application.preview.form :application="$application" />
<div> <div>
<div class="flex items-center gap-2"> @if ($application->is_github_based())
<h3>Pull Requests on Git</h3> <div class="flex items-center gap-2">
<x-forms.button wire:click="load_prs">Load Pull Requests <h3>Pull Requests on Git</h3>
</x-forms.button> <x-forms.button wire:click="load_prs">Load Pull Requests
</div> </x-forms.button>
</div>
@endif
@isset($rate_limit_remaining) @isset($rate_limit_remaining)
<div class="pt-1 ">Requests remaining till rate limited by Git: {{ $rate_limit_remaining }}</div> <div class="pt-1 ">Requests remaining till rate limited by Git: {{ $rate_limit_remaining }}</div>
@endisset @endisset

View File

@ -239,43 +239,58 @@
$return_payloads = collect([]); $return_payloads = collect([]);
$payload = request()->collect(); $payload = request()->collect();
$headers = request()->headers->all(); $headers = request()->headers->all();
$x_bitbucket_token = data_get($headers, 'x-hub-signature', [""])[0]; $x_bitbucket_token = data_get($headers, 'x-hub-signature.0', "");
$x_bitbucket_event = data_get($headers, 'x-event-key', [""])[0]; $x_bitbucket_event = data_get($headers, 'x-event-key.0', "");
$handled_events = collect(['repo:push', 'pullrequest:created', 'pullrequest:rejected', 'pullrequest:fulfilled']);
if (!$handled_events->contains($x_bitbucket_event)) {
return response([
'status' => 'failed',
'message' => 'Nothing to do. Event not handled.',
]);
}
if ($x_bitbucket_event === 'repo:push') { if ($x_bitbucket_event === 'repo:push') {
$branch = data_get($payload, 'push.changes.0.new.name'); $branch = data_get($payload, 'push.changes.0.new.name');
$name = data_get($payload, 'repository.name'); $full_name = data_get($payload, 'repository.full_name');
if (!$branch) { if (!$branch) {
$return_payloads->push([ return response([
'status' => 'failed', 'status' => 'failed',
'message' => 'Nothing to do. No branch found in the request.', 'message' => 'Nothing to do. No branch found in the request.',
]); ]);
return response($return_payloads);
} }
ray('Manual Webhook bitbucket Push Event with branch: ' . $branch); ray('Manual webhook bitbucket push event with branch: ' . $branch);
} }
$applications = Application::where('git_repository', 'like', "%$name%"); if ($x_bitbucket_event === 'pullrequest:created' || $x_bitbucket_event === 'pullrequest:rejected' || $x_bitbucket_event === 'pullrequest:fulfilled') {
if ($x_bitbucket_event === 'repo:push') { $branch = data_get($payload, 'pullrequest.destination.branch.name');
$applications = $applications->where('git_branch', $branch)->get(); $base_branch = data_get($payload, 'pullrequest.source.branch.name');
if ($applications->isEmpty()) { $full_name = data_get($payload, 'repository.full_name');
$return_payloads->push([ $pull_request_id = data_get($payload, 'pullrequest.id');
'status' => 'failed', $pull_request_html_url = data_get($payload, 'pullrequest.links.html.href');
'message' => "Nothing to do. No applications found with deploy key set, branch is '$branch' and Git Repository name has $name.", $commit = data_get($payload, 'pullrequest.source.commit.hash');
]); }
return response($return_payloads); $applications = Application::where('git_repository', 'like', "%$full_name%");
} $applications = $applications->where('git_branch', $branch)->get();
if ($applications->isEmpty()) {
return response([
'status' => 'failed',
'message' => "Nothing to do. No applications found with deploy key set, branch is '$branch' and Git Repository name has $full_name.",
]);
} }
foreach ($applications as $application) { foreach ($applications as $application) {
if (!$application->isPRDeployable()) {
$return_payloads->push([
'application' => $application->name,
'status' => 'failed',
'message' => 'Preview deployments disabled.',
]);
continue;
}
$webhook_secret = data_get($application, 'manual_webhook_secret_bitbucket'); $webhook_secret = data_get($application, 'manual_webhook_secret_bitbucket');
$payload = request()->getContent(); $payload = request()->getContent();
fwrite(STDOUT, $payload);
list($algo, $hash) = explode('=', $x_bitbucket_token, 2); list($algo, $hash) = explode('=', $x_bitbucket_token, 2);
$payloadHash = hash_hmac($algo, $payload, $webhook_secret); $payloadHash = hash_hmac($algo, $payload, $webhook_secret);
if (!hash_equals($hash, $payloadHash) && !isDev()) {
if (!hash_equals($hash, $payloadHash)) {
$return_payloads->push([ $return_payloads->push([
'application' => $application->name, 'application' => $application->name,
'status' => 'failed', 'status' => 'failed',
@ -289,34 +304,94 @@
$return_payloads->push([ $return_payloads->push([
'application' => $application->name, 'application' => $application->name,
'status' => 'failed', 'status' => 'failed',
'message' => 'Server is not functional', 'message' => 'Server is not functional.',
]); ]);
ray('Server is not functional: ' . $application->destination->server->name); ray('Server is not functional: ' . $application->destination->server->name);
continue; continue;
} }
if ($x_bitbucket_event === 'repo:push') { if ($x_bitbucket_event === 'repo:push') {
if ($application->isDeployable()) { if ($application->isPRDeployable()) {
ray('Deploying ' . $application->name . ' with branch ' . $branch); ray('Deploying ' . $application->name . ' with branch ' . $branch);
$deployment_uuid = new Cuid2(7); $deployment_uuid = new Cuid2(7);
queue_application_deployment( queue_application_deployment(
application_id: $application->id, application: $application,
deployment_uuid: $deployment_uuid, deployment_uuid: $deployment_uuid,
force_rebuild: false, force_rebuild: false,
is_webhook: true is_webhook: true
); );
$return_payloads->push([
'application' => $application->name,
'status' => 'success',
'message' => 'Preview deployment queued.',
]);
} else { } else {
$return_payloads->push([ $return_payloads->push([
'application' => $application->name, 'application' => $application->name,
'status' => 'failed', 'status' => 'failed',
'message' => 'Deployments disabled', 'message' => 'Preview deployments disabled.',
]);
}
}
if ($x_bitbucket_event === 'pullrequest:created') {
if ($application->isPRDeployable()) {
ray('Deploying preview for ' . $application->name . ' with branch ' . $branch . ' and base branch ' . $base_branch . ' and pull request id ' . $pull_request_id);
$deployment_uuid = new Cuid2(7);
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (!$found) {
ApplicationPreview::create([
'git_type' => 'bitbucket',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
]);
}
queue_application_deployment(
application: $application,
pull_request_id: $pull_request_id,
deployment_uuid: $deployment_uuid,
force_rebuild: false,
commit: $commit,
is_webhook: true,
git_type: 'bitbucket'
);
$return_payloads->push([
'application' => $application->name,
'status' => 'success',
'message' => 'Preview deployment queued.',
]);
} else {
$return_payloads->push([
'application' => $application->name,
'status' => 'failed',
'message' => 'Preview deployments disabled.',
]);
}
}
if ($x_bitbucket_event === 'pullrequest:rejected' || $x_bitbucket_event === 'pullrequest:fulfilled') {
ray('Pull request rejected');
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if ($found) {
$found->delete();
$container_name = generateApplicationContainerName($application, $pull_request_id);
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
$return_payloads->push([
'application' => $application->name,
'status' => 'success',
'message' => 'Preview deployment closed.',
]);
} else {
$return_payloads->push([
'application' => $application->name,
'status' => 'failed',
'message' => 'No preview deployment found.',
]); ]);
ray('Deployments disabled for ' . $application->name);
} }
} }
} }
ray($return_payloads);
return response($return_payloads); return response($return_payloads);
} catch (Exception $e) { } catch (Exception $e) {
ray($e->getMessage()); ray($e);
return handleError($e); return handleError($e);
} }
}); });