Merge pull request #1450 from coollabsio/next

v4.0.0-beta.132
This commit is contained in:
Andras Bacsai 2023-11-14 14:22:17 +01:00 committed by GitHub
commit efd2899ae3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 435 additions and 19 deletions

View File

@ -4,6 +4,7 @@
use App\Models\InstanceSettings; use App\Models\InstanceSettings;
use App\Models\User; use App\Models\User;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Sentry\Laravel\Integration; use Sentry\Laravel\Integration;
use Sentry\State\Scope; use Sentry\State\Scope;
@ -40,6 +41,13 @@ class Handler extends ExceptionHandler
]; ];
private InstanceSettings $settings; private InstanceSettings $settings;
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->is('api/*') || $request->expectsJson() || $this->shouldReturnJson($request, $exception)) {
return response()->json(['message' => $exception->getMessage()], 401);
}
return redirect()->guest($exception->redirectTo() ?? route('login'));
}
/** /**
* Register the exception handling callbacks for the application. * Register the exception handling callbacks for the application.
*/ */
@ -47,6 +55,7 @@ public function register(): void
{ {
$this->reportable(function (Throwable $e) { $this->reportable(function (Throwable $e) {
if (isDev()) { if (isDev()) {
ray($e);
return; return;
} }
$this->settings = InstanceSettings::get(); $this->settings = InstanceSettings::get();

View File

@ -8,9 +8,27 @@ class Webhooks extends Component
{ {
public $resource; public $resource;
public ?string $deploywebhook = null; public ?string $deploywebhook = null;
public ?string $githubManualWebhook = null;
public ?string $gitlabManualWebhook = null;
protected $rules = [
'resource.manual_webhook_secret_github' => 'nullable|string',
'resource.manual_webhook_secret_gitlab' => 'nullable|string',
];
public function saveSecret()
{
try {
$this->validate();
$this->resource->save();
$this->emit('success','Secret Saved.');
} catch (\Exception $e) {
return handleError($e, $this);
}
}
public function mount() public function mount()
{ {
$this->deploywebhook = generateDeployWebhook($this->resource); $this->deploywebhook = generateDeployWebhook($this->resource);
$this->githubManualWebhook = generateGitManualWebhook($this->resource, 'github');
$this->gitlabManualWebhook = generateGitManualWebhook($this->resource, 'gitlab');
} }
public function render() public function render()
{ {

View File

@ -53,6 +53,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
private StandaloneDocker|SwarmDocker $destination; private StandaloneDocker|SwarmDocker $destination;
private Server $server; private Server $server;
private ?ApplicationPreview $preview = null; private ?ApplicationPreview $preview = null;
private ?string $git_type = null;
private string $container_name; private string $container_name;
private ?string $currently_running_container_name = null; private ?string $currently_running_container_name = null;
@ -99,6 +100,8 @@ public function __construct(int $application_deployment_queue_id)
$this->force_rebuild = $this->application_deployment_queue->force_rebuild; $this->force_rebuild = $this->application_deployment_queue->force_rebuild;
$this->restart_only = $this->application_deployment_queue->restart_only; $this->restart_only = $this->application_deployment_queue->restart_only;
$this->git_type = data_get($this->application_deployment_queue, 'git_type');
$source = data_get($this->application, 'source'); $source = data_get($this->application, 'source');
if ($source) { if ($source) {
$this->source = $source->getMorphClass()::where('id', $this->application->source->id)->first(); $this->source = $source->getMorphClass()::where('id', $this->application->source->id)->first();
@ -218,12 +221,16 @@ public function handle(): void
} finally { } finally {
if (isset($this->docker_compose_base64)) { if (isset($this->docker_compose_base64)) {
$readme = generate_readme_file($this->application->name, $this->application_deployment_queue->updated_at); $readme = generate_readme_file($this->application->name, $this->application_deployment_queue->updated_at);
$composeFileName = "$this->configuration_dir/docker-compose.yml";
if ($this->pull_request_id !== 0) {
$composeFileName = "$this->configuration_dir/docker-compose-pr-{$this->pull_request_id}.yml";
}
$this->execute_remote_command( $this->execute_remote_command(
[ [
"mkdir -p $this->configuration_dir" "mkdir -p $this->configuration_dir"
], ],
[ [
"echo '{$this->docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml", "echo '{$this->docker_compose_base64}' | base64 -d > $composeFileName",
], ],
[ [
"echo '{$readme}' > $this->configuration_dir/README.md", "echo '{$readme}' > $this->configuration_dir/README.md",
@ -349,7 +356,7 @@ private function deploy_simple_dockerfile()
$this->prepare_builder_image(); $this->prepare_builder_image();
$this->execute_remote_command( $this->execute_remote_command(
[ [
executeInDocker($this->deployment_uuid, "echo '$dockerfile_base64' | base64 -d > $this->workdir/Dockerfile") executeInDocker($this->deployment_uuid, "echo '$dockerfile_base64' | base64 -d > $this->workdir$this->dockerfile_location")
], ],
); );
$this->generate_image_names(); $this->generate_image_names();
@ -652,7 +659,7 @@ private function generate_git_import_commands()
} }
if ($this->pull_request_id !== 0) { if ($this->pull_request_id !== 0) {
$this->branch = "pull/{$this->pull_request_id}/head:$pr_branch_name"; $this->branch = "pull/{$this->pull_request_id}/head:$pr_branch_name";
$commands->push(executeInDocker($this->deployment_uuid, "cd {$this->basedir} && git fetch origin pull/{$this->pull_request_id}/head:$pr_branch_name && git checkout $pr_branch_name")); $commands->push(executeInDocker($this->deployment_uuid, "cd {$this->basedir} && git fetch origin $this->branch && git checkout $pr_branch_name"));
} }
return $commands->implode(' && '); return $commands->implode(' && ');
} }
@ -664,14 +671,28 @@ private function generate_git_import_commands()
throw new Exception('Private key not found. Please add a private key to the application and try again.'); throw new Exception('Private key not found. Please add a private key to the application and try again.');
} }
$private_key = base64_encode($private_key); $private_key = base64_encode($private_key);
$git_clone_command = "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" {$git_clone_command} {$this->customRepository} {$this->basedir}"; $git_clone_command_base = "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" {$git_clone_command} {$this->customRepository} {$this->basedir}";
$git_clone_command = $this->set_git_import_settings($git_clone_command); $git_clone_command = $this->set_git_import_settings($git_clone_command_base);
$commands = collect([ $commands = collect([
executeInDocker($this->deployment_uuid, "mkdir -p /root/.ssh"), executeInDocker($this->deployment_uuid, "mkdir -p /root/.ssh"),
executeInDocker($this->deployment_uuid, "echo '{$private_key}' | base64 -d > /root/.ssh/id_rsa"), executeInDocker($this->deployment_uuid, "echo '{$private_key}' | base64 -d > /root/.ssh/id_rsa"),
executeInDocker($this->deployment_uuid, "chmod 600 /root/.ssh/id_rsa"), executeInDocker($this->deployment_uuid, "chmod 600 /root/.ssh/id_rsa"),
executeInDocker($this->deployment_uuid, $git_clone_command)
]); ]);
if ($this->pull_request_id !== 0) {
ray($this->git_type);
if ($this->git_type === 'gitlab') {
$this->branch = "merge-requests/{$this->pull_request_id}/head:$pr_branch_name";
$commands->push(executeInDocker($this->deployment_uuid, "echo 'Checking out $this->branch'"));
$git_clone_command = "{$git_clone_command} && cd {$this->basedir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $this->branch && git checkout $pr_branch_name";
}
if ($this->git_type === 'github') {
$this->branch = "pull/{$this->pull_request_id}/head:$pr_branch_name";
$commands->push(executeInDocker($this->deployment_uuid, "echo 'Checking out $this->branch'"));
$git_clone_command = "{$git_clone_command} && cd {$this->basedir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $this->branch && git checkout $pr_branch_name";
}
}
$commands->push(executeInDocker($this->deployment_uuid, $git_clone_command));
return $commands->implode(' && '); return $commands->implode(' && ');
} }
if ($this->application->deploymentType() === 'other') { if ($this->application->deploymentType() === 'other') {
@ -992,7 +1013,7 @@ private function build_image()
}"); }");
} else { } else {
$this->execute_remote_command([ $this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build $this->buildTarget $this->addHosts --network host -f {$this->workdir}/{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true executeInDocker($this->deployment_uuid, "docker build $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
]); ]);
$dockerfile = base64_encode("FROM {$this->application->static_image} $dockerfile = base64_encode("FROM {$this->application->static_image}
@ -1104,7 +1125,7 @@ private function generate_build_env_variables()
private function add_build_env_variables_to_dockerfile() private function add_build_env_variables_to_dockerfile()
{ {
$this->execute_remote_command([ $this->execute_remote_command([
executeInDocker($this->deployment_uuid, "cat {$this->workdir}/{$this->dockerfile_location}"), "hidden" => true, "save" => 'dockerfile' executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), "hidden" => true, "save" => 'dockerfile'
]); ]);
$dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile'))->trim()->explode("\n")); $dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile'))->trim()->explode("\n"));
@ -1113,7 +1134,7 @@ private function add_build_env_variables_to_dockerfile()
} }
$dockerfile_base64 = base64_encode($dockerfile->implode("\n")); $dockerfile_base64 = base64_encode($dockerfile->implode("\n"));
$this->execute_remote_command([ $this->execute_remote_command([
executeInDocker($this->deployment_uuid, "echo '{$dockerfile_base64}' | base64 -d > {$this->workdir}/{$this->dockerfile_location}"), executeInDocker($this->deployment_uuid, "echo '{$dockerfile_base64}' | base64 -d > {$this->workdir}{$this->dockerfile_location}"),
"hidden" => true "hidden" => true
]); ]);
} }

View File

@ -85,6 +85,18 @@ public function gitBranchLocation(): Attribute
); );
} }
public function gitWebhook(): Attribute
{
return Attribute::make(
get: function () {
if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) {
return "{$this->source->html_url}/{$this->git_repository}/settings/hooks";
}
return $this->git_repository;
}
);
}
public function gitCommits(): Attribute public function gitCommits(): Attribute
{ {
return Attribute::make( return Attribute::make(

View File

@ -29,6 +29,10 @@ public function send(SendsEmail $notifiable, Notification $notification): void
->html((string)$mailMessage->render()) ->html((string)$mailMessage->render())
); );
} catch (Exception $e) { } catch (Exception $e) {
$error = $e->getMessage();
if ($error === 'No email settings found.') {
throw $e;
}
ray($e->getMessage()); ray($e->getMessage());
$message = "EmailChannel error: {$e->getMessage()}. Failed to send email to:"; $message = "EmailChannel error: {$e->getMessage()}. Failed to send email to:";
if (isset($recepients)) { if (isset($recepients)) {

View File

@ -4,7 +4,7 @@
use App\Models\Application; use App\Models\Application;
use App\Models\ApplicationDeploymentQueue; use App\Models\ApplicationDeploymentQueue;
function queue_application_deployment(int $application_id, string $deployment_uuid, int | null $pull_request_id = 0, string $commit = 'HEAD', bool $force_rebuild = false, bool $is_webhook = false, bool $restart_only = false) function queue_application_deployment(int $application_id, string $deployment_uuid, int | null $pull_request_id = 0, string $commit = 'HEAD', bool $force_rebuild = false, bool $is_webhook = false, bool $restart_only = false, ?string $git_type = null)
{ {
$deployment = ApplicationDeploymentQueue::create([ $deployment = ApplicationDeploymentQueue::create([
'application_id' => $application_id, 'application_id' => $application_id,
@ -14,6 +14,7 @@ function queue_application_deployment(int $application_id, string $deployment_uu
'is_webhook' => $is_webhook, 'is_webhook' => $is_webhook,
'restart_only' => $restart_only, 'restart_only' => $restart_only,
'commit' => $commit, 'commit' => $commit,
'git_type' => $git_type
]); ]);
$queued_deployments = ApplicationDeploymentQueue::where('application_id', $application_id)->where('status', 'queued')->get()->sortByDesc('created_at'); $queued_deployments = ApplicationDeploymentQueue::where('application_id', $application_id)->where('status', 'queued')->get()->sortByDesc('created_at');
$running_deployments = ApplicationDeploymentQueue::where('application_id', $application_id)->where('status', 'in_progress')->get()->sortByDesc('created_at'); $running_deployments = ApplicationDeploymentQueue::where('application_id', $application_id)->where('status', 'in_progress')->get()->sortByDesc('created_at');

View File

@ -50,8 +50,11 @@ function generate_github_jwt_token(GithubApp $source)
return $issuedToken; return $issuedToken;
} }
function githubApi(GithubApp|GitlabApp $source, string $endpoint, string $method = 'get', array|null $data = null, bool $throwError = true) function githubApi(GithubApp|GitlabApp|null $source, string $endpoint, string $method = 'get', array|null $data = null, bool $throwError = true)
{ {
if (is_null($source)) {
throw new \Exception('Not implemented yet.');
}
if ($source->getMorphClass() == 'App\Models\GithubApp') { if ($source->getMorphClass() == 'App\Models\GithubApp') {
if ($source->is_public) { if ($source->is_public) {
$response = Http::github($source->api_url)->$method($endpoint); $response = Http::github($source->api_url)->$method($endpoint);

View File

@ -510,6 +510,17 @@ function generateDeployWebhook($resource)
$url = $api . $endpoint . "?uuid=$uuid&force=false"; $url = $api . $endpoint . "?uuid=$uuid&force=false";
return $url; return $url;
} }
function generateGitManualWebhook($resource, $type) {
if ($resource->source_id !== 0) {
return null;
}
if ($resource->getMorphClass() === 'App\Models\Application') {
$baseUrl = base_url();
$api = Url::fromString($baseUrl) . "/webhooks/source/$type/events/manual";
return $api;
}
return null;
}
function removeAnsiColors($text) function removeAnsiColors($text)
{ {
return preg_replace('/\e[[][A-Za-z0-9];?[0-9]*m?/', '', $text); return preg_replace('/\e[[][A-Za-z0-9];?[0-9]*m?/', '', $text);

View File

@ -7,7 +7,7 @@
// The release version of your application // The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
'release' => '4.0.0-beta.131', 'release' => '4.0.0-beta.132',
// When left empty or `null` the Laravel environment will be used // When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'), 'environment' => config('app.env'),

View File

@ -1,3 +1,3 @@
<?php <?php
return '4.0.0-beta.131'; return '4.0.0-beta.132';

View File

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('applications', function (Blueprint $table) {
$table->string('manual_webhook_secret_github')->nullable();
$table->string('manual_webhook_secret_gitlab')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('applications', function (Blueprint $table) {
$table->dropColumn('manual_webhook_secret_github');
$table->dropColumn('manual_webhook_secret_gitlab');
});
}
};

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('application_previews', function (Blueprint $table) {
$table->string('git_type')->nullable();
});
Schema::table('application_deployment_queues', function (Blueprint $table) {
$table->string('git_type')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('application_previews', function (Blueprint $table) {
$table->dropColumn('git_type');
});
Schema::table('application_deployment_queues', function (Blueprint $table) {
$table->dropColumn('git_type');
});
}
};

View File

@ -69,10 +69,12 @@
<div class="flex flex-col gap-2 xl:flex-row"> <div class="flex flex-col gap-2 xl:flex-row">
<x-forms.input placeholder="/" id="application.base_directory" label="Base Directory" <x-forms.input placeholder="/" id="application.base_directory" label="Base Directory"
helper="Directory to use as root. Useful for monorepos." /> helper="Directory to use as root. Useful for monorepos." />
@if ($application->build_pack === 'dockerfile') @if ($application->build_pack === 'dockerfile' && !$application->dockerfile)
<x-forms.input placeholder="/Dockerfile" id="application.dockerfile_location" <x-forms.input placeholder="/Dockerfile" id="application.dockerfile_location"
label="Dockerfile Location" label="Dockerfile Location"
helper="It is calculated together with the Base Directory: {{ Str::start($application->base_directory . $application->dockerfile_location, '/') }}" /> helper="It is calculated together with the Base Directory: {{ Str::start($application->base_directory . $application->dockerfile_location, '/') }}" />
@endif
@if ($application->build_pack === 'dockerfile')
<x-forms.input id="application.dockerfile_target_build" label="Docker Build Stage Target" <x-forms.input id="application.dockerfile_target_build" label="Docker Build Stage Target"
helper="Useful if you have multi-staged dockerfile." /> helper="Useful if you have multi-staged dockerfile." />
@endif @endif

View File

@ -1,10 +1,41 @@
<div> <div class="flex flex-col gap-2">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<h2>Webhooks</h2> <h2>Webhooks</h2>
<x-helper <x-helper
helper="For more details goto our <a class='text-white underline' href='https://coolify.io/docs/api-endpoints' target='_blank'>docs</a>." /> helper="For more details goto our <a class='text-white underline' href='https://coolify.io/docs/api-endpoints' target='_blank'>docs</a>." />
</div> </div>
<div> <div>
<x-forms.input readonly label="Deploy Webhook (auth required)" id="deploywebhook"></x-forms.input> <x-forms.input readonly
helper="See details in our <a target='_blank' class='text-white underline' href='https://coolify.io/docs/api-authentication'>documentation</a>."
label="Deploy Webhook (auth required)" id="deploywebhook"></x-forms.input>
</div>
<div>
<h3>Manual Git Webhooks</h3>
@if ($githubManualWebhook && $gitlabManualWebhook)
<form wire:submit.prevent='saveSecret' class="flex flex-col gap-2">
<div class="flex items-end gap-2">
<x-forms.input helper="Content Type in GitHub configuration could be json or form-urlencoded."
readonly label="GitHub" id="githubManualWebhook"></x-forms.input>
<x-forms.input type="password"
helper="Need to set a secret to be able to use this webhook. It should match with the secret in GitHub."
label="GitHub Webhook Secret" id="resource.manual_webhook_secret_github"></x-forms.input>
</div>
<a target="_blank" class="flex hover:no-underline" href="{{ $resource?->gitWebhook }}">
<x-forms.button>Webhook Configuration on GitHub
<x-external-link />
</x-forms.button>
</a>
<div class="flex gap-2">
<x-forms.input readonly label="GitLab" id="gitlabManualWebhook"></x-forms.input>
<x-forms.input type="password"
helper="Need to set a secret to be able to use this webhook. It should match with the secret in GitLab."
label="GitLab Webhook Secret" id="resource.manual_webhook_secret_gitlab"></x-forms.input>
</div>
<x-forms.button type="submit">Save</x-forms.button>
</form>
@else
You are using an official Git App. You do not need manual webhooks.
@endif
</div> </div>
</div> </div>

View File

@ -63,6 +63,244 @@
return handleError($e); return handleError($e);
} }
}); });
Route::post('/source/gitlab/events/manual', function () {
try {
$payload = request()->collect();
$headers = request()->headers->all();
ray($payload, $headers);
$x_gitlab_token = data_get($headers, 'x-gitlab-token.0');
$x_gitlab_event = data_get($payload, 'object_kind');
if ($x_gitlab_event === 'push') {
$branch = data_get($payload, 'ref');
$full_name = data_get($payload, 'project.path_with_namespace');
if (Str::isMatch('/refs\/heads\/*/', $branch)) {
$branch = Str::after($branch, 'refs/heads/');
}
if (!$branch) {
return response('Nothing to do. No branch found in the request.');
}
ray('Manual Webhook GitLab Push Event with branch: ' . $branch);
}
if ($x_gitlab_event === 'merge_request') {
$action = data_get($payload, 'object_attributes.action');
ray($action);
$branch = data_get($payload, 'object_attributes.source_branch');
$base_branch = data_get($payload, 'object_attributes.target_branch');
$full_name = data_get($payload, 'project.path_with_namespace');
$pull_request_id = data_get($payload, 'object_attributes.iid');
$pull_request_html_url = data_get($payload, 'object_attributes.url');
if (!$branch) {
return response('Nothing to do. No branch found in the request.');
}
ray('Webhook GitHub Pull Request Event with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id);
}
$applications = Application::where('git_repository', 'like', "%$full_name%");
if ($x_gitlab_event === 'push') {
$applications = $applications->where('git_branch', $branch)->get();
if ($applications->isEmpty()) {
return response("Nothing to do. No applications found with deploy key set, branch is '$branch' and Git Repository name has $full_name.");
}
}
if ($x_gitlab_event === 'merge_request') {
$applications = $applications->where('git_branch', $base_branch)->get();
if ($applications->isEmpty()) {
return response("Nothing to do. No applications found with branch '$base_branch'.");
}
}
foreach ($applications as $application) {
$webhook_secret = data_get($application, 'manual_webhook_secret_gitlab');
if ($webhook_secret !== $x_gitlab_token) {
ray('Invalid signature');
continue;
}
$isFunctional = $application->destination->server->isFunctional();
if (!$isFunctional) {
ray('Server is not functional: ' . $application->destination->server->name);
continue;
}
if ($x_gitlab_event === 'push') {
if ($application->isDeployable()) {
ray('Deploying ' . $application->name . ' with branch ' . $branch);
$deployment_uuid = new Cuid2(7);
queue_application_deployment(
application_id: $application->id,
deployment_uuid: $deployment_uuid,
force_rebuild: false,
is_webhook: true
);
} else {
ray('Deployments disabled for ' . $application->name);
}
}
if ($x_gitlab_event === 'merge_request') {
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened' || $action === 'reopen' || $action === 'update') {
if ($application->isPRDeployable()) {
$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' => 'gitlab',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
]);
}
queue_application_deployment(
application_id: $application->id,
pull_request_id: $pull_request_id,
deployment_uuid: $deployment_uuid,
force_rebuild: false,
is_webhook: true,
git_type: 'gitlab'
);
ray('Deploying preview for ' . $application->name . ' with branch ' . $branch . ' and base branch ' . $base_branch . ' and pull request id ' . $pull_request_id);
return response('Preview Deployment queued.');
} else {
ray('Preview deployments disabled for ' . $application->name);
return response('Nothing to do. Preview Deployments disabled.');
}
}
if ($action === 'closed') {
$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);
// ray('Stopping container: ' . $container_name);
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
return response('Preview Deployment closed.');
}
return response('Nothing to do. No Preview Deployment found');
}
}
}
} catch (Exception $e) {
ray($e->getMessage());
return handleError($e);
}
});
Route::post('/source/github/events/manual', function () {
try {
$x_github_event = Str::lower(request()->header('X-GitHub-Event'));
$x_hub_signature_256 = Str::after(request()->header('X-Hub-Signature-256'), 'sha256=');
$content_type = request()->header('Content-Type');
$payload = request()->collect();
if ($x_github_event === 'ping') {
// Just pong
return response('pong');
}
if ($content_type !== 'application/json') {
$payload = json_decode(data_get($payload, 'payload'), true);
}
if ($x_github_event === 'push') {
$branch = data_get($payload, 'ref');
$full_name = data_get($payload, 'repository.full_name');
if (Str::isMatch('/refs\/heads\/*/', $branch)) {
$branch = Str::after($branch, 'refs/heads/');
}
ray('Manual Webhook GitHub Push Event with branch: ' . $branch);
}
if ($x_github_event === 'pull_request') {
$action = data_get($payload, 'action');
$full_name = data_get($payload, 'repository.full_name');
$pull_request_id = data_get($payload, 'number');
$pull_request_html_url = data_get($payload, 'pull_request.html_url');
$branch = data_get($payload, 'pull_request.head.ref');
$base_branch = data_get($payload, 'pull_request.base.ref');
ray('Webhook GitHub Pull Request Event with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id);
}
if (!$branch) {
return response('Nothing to do. No branch found in the request.');
}
$applications = Application::where('git_repository', 'like', "%$full_name%");
if ($x_github_event === 'push') {
$applications = $applications->where('git_branch', $branch)->get();
if ($applications->isEmpty()) {
return response("Nothing to do. No applications found with deploy key set, branch is '$branch' and Git Repository name has $full_name.");
}
}
if ($x_github_event === 'pull_request') {
$applications = $applications->where('git_branch', $base_branch)->get();
if ($applications->isEmpty()) {
return response("Nothing to do. No applications found with branch '$base_branch'.");
}
}
ray($applications);
foreach ($applications as $application) {
ray($application);
$webhook_secret = data_get($application, 'manual_webhook_secret_github');
ray($webhook_secret);
$hmac = hash_hmac('sha256', request()->getContent(), $webhook_secret);
ray($hmac, $x_hub_signature_256);
if (!hash_equals($x_hub_signature_256, $hmac)) {
ray('Invalid signature');
continue;
}
$isFunctional = $application->destination->server->isFunctional();
if (!$isFunctional) {
ray('Server is not functional: ' . $application->destination->server->name);
continue;
}
if ($x_github_event === 'push') {
if ($application->isDeployable()) {
ray('Deploying ' . $application->name . ' with branch ' . $branch);
$deployment_uuid = new Cuid2(7);
queue_application_deployment(
application_id: $application->id,
deployment_uuid: $deployment_uuid,
force_rebuild: false,
is_webhook: true
);
} else {
ray('Deployments disabled for ' . $application->name);
}
}
if ($x_github_event === 'pull_request') {
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
if ($application->isPRDeployable()) {
$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' => 'github',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
]);
}
queue_application_deployment(
application_id: $application->id,
pull_request_id: $pull_request_id,
deployment_uuid: $deployment_uuid,
force_rebuild: false,
is_webhook: true,
git_type: 'github'
);
ray('Deploying preview for ' . $application->name . ' with branch ' . $branch . ' and base branch ' . $base_branch . ' and pull request id ' . $pull_request_id);
return response('Preview Deployment queued.');
} else {
ray('Preview deployments disabled for ' . $application->name);
return response('Nothing to do. Preview Deployments disabled.');
}
}
if ($action === 'closed') {
$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);
// ray('Stopping container: ' . $container_name);
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
return response('Preview Deployment closed.');
}
return response('Nothing to do. No Preview Deployment found');
}
}
}
} catch (Exception $e) {
ray($e->getMessage());
return handleError($e);
}
});
Route::post('/source/github/events', function () { Route::post('/source/github/events', function () {
try { try {
$id = null; $id = null;
@ -150,6 +388,7 @@
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (!$found) { if (!$found) {
ApplicationPreview::create([ ApplicationPreview::create([
'git_type' => 'github',
'application_id' => $application->id, 'application_id' => $application->id,
'pull_request_id' => $pull_request_id, 'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url, 'pull_request_html_url' => $pull_request_html_url,
@ -160,7 +399,8 @@
pull_request_id: $pull_request_id, pull_request_id: $pull_request_id,
deployment_uuid: $deployment_uuid, deployment_uuid: $deployment_uuid,
force_rebuild: false, force_rebuild: false,
is_webhook: true is_webhook: true,
git_type: 'github'
); );
ray('Deploying preview for ' . $application->name . ' with branch ' . $branch . ' and base branch ' . $base_branch . ' and pull request id ' . $pull_request_id); ray('Deploying preview for ' . $application->name . ' with branch ' . $branch . ' and base branch ' . $base_branch . ' and pull request id ' . $pull_request_id);
return response('Preview Deployment queued.'); return response('Preview Deployment queued.');
@ -169,7 +409,7 @@
return response('Nothing to do. Preview Deployments disabled.'); return response('Nothing to do. Preview Deployments disabled.');
} }
} }
if ($action === 'closed') { if ($action === 'closed' || $action === 'close') {
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if ($found) { if ($found) {
$found->delete(); $found->delete();

View File

@ -4,7 +4,7 @@
"version": "3.12.36" "version": "3.12.36"
}, },
"v4": { "v4": {
"version": "4.0.0-beta.131" "version": "4.0.0-beta.132"
} }
} }
} }