diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 24f4eff9e..b4f63661c 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -4,6 +4,7 @@ namespace App\Exceptions; use App\Models\InstanceSettings; use App\Models\User; +use Illuminate\Auth\AuthenticationException; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Sentry\Laravel\Integration; use Sentry\State\Scope; @@ -40,6 +41,13 @@ class Handler extends ExceptionHandler ]; 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. */ @@ -47,6 +55,7 @@ class Handler extends ExceptionHandler { $this->reportable(function (Throwable $e) { if (isDev()) { + ray($e); return; } $this->settings = InstanceSettings::get(); diff --git a/app/Http/Livewire/Project/Shared/Webhooks.php b/app/Http/Livewire/Project/Shared/Webhooks.php index a943347b1..d425b51df 100644 --- a/app/Http/Livewire/Project/Shared/Webhooks.php +++ b/app/Http/Livewire/Project/Shared/Webhooks.php @@ -8,9 +8,27 @@ class Webhooks extends Component { public $resource; 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() { $this->deploywebhook = generateDeployWebhook($this->resource); + $this->githubManualWebhook = generateGitManualWebhook($this->resource, 'github'); + $this->gitlabManualWebhook = generateGitManualWebhook($this->resource, 'gitlab'); } public function render() { diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index fbf6ef005..f9f6ec90c 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -53,6 +53,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted private StandaloneDocker|SwarmDocker $destination; private Server $server; private ?ApplicationPreview $preview = null; + private ?string $git_type = null; private string $container_name; private ?string $currently_running_container_name = null; @@ -99,6 +100,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted $this->force_rebuild = $this->application_deployment_queue->force_rebuild; $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'); if ($source) { $this->source = $source->getMorphClass()::where('id', $this->application->source->id)->first(); @@ -218,12 +221,16 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted } finally { if (isset($this->docker_compose_base64)) { $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( [ "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", @@ -349,7 +356,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted $this->prepare_builder_image(); $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(); @@ -652,7 +659,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted } if ($this->pull_request_id !== 0) { $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(' && '); } @@ -664,14 +671,28 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted throw new Exception('Private key not found. Please add a private key to the application and try again.'); } $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 = $this->set_git_import_settings($git_clone_command); + $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_base); $commands = collect([ 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, "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(' && '); } if ($this->application->deploymentType() === 'other') { @@ -992,7 +1013,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); }"); } else { $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} @@ -1104,7 +1125,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); private function add_build_env_variables_to_dockerfile() { $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")); @@ -1113,7 +1134,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); } $dockerfile_base64 = base64_encode($dockerfile->implode("\n")); $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 ]); } diff --git a/app/Models/Application.php b/app/Models/Application.php index 0e72f94ce..27f34df34 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -85,6 +85,18 @@ class Application extends BaseModel ); } + 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 { return Attribute::make( diff --git a/app/Notifications/Channels/EmailChannel.php b/app/Notifications/Channels/EmailChannel.php index 82fc9a65c..99afcf5a7 100644 --- a/app/Notifications/Channels/EmailChannel.php +++ b/app/Notifications/Channels/EmailChannel.php @@ -29,6 +29,10 @@ class EmailChannel ->html((string)$mailMessage->render()) ); } catch (Exception $e) { + $error = $e->getMessage(); + if ($error === 'No email settings found.') { + throw $e; + } ray($e->getMessage()); $message = "EmailChannel error: {$e->getMessage()}. Failed to send email to:"; if (isset($recepients)) { diff --git a/bootstrap/helpers/applications.php b/bootstrap/helpers/applications.php index d78d19992..fce225dc5 100644 --- a/bootstrap/helpers/applications.php +++ b/bootstrap/helpers/applications.php @@ -4,7 +4,7 @@ use App\Jobs\ApplicationDeploymentJob; use App\Models\Application; 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([ 'application_id' => $application_id, @@ -14,6 +14,7 @@ function queue_application_deployment(int $application_id, string $deployment_uu 'is_webhook' => $is_webhook, 'restart_only' => $restart_only, 'commit' => $commit, + 'git_type' => $git_type ]); $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'); diff --git a/bootstrap/helpers/github.php b/bootstrap/helpers/github.php index 16ac3671a..60ff6d9c9 100644 --- a/bootstrap/helpers/github.php +++ b/bootstrap/helpers/github.php @@ -50,8 +50,11 @@ function generate_github_jwt_token(GithubApp $source) 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->is_public) { $response = Http::github($source->api_url)->$method($endpoint); diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index d6766a8d0..a11e857ce 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -510,6 +510,17 @@ function generateDeployWebhook($resource) $url = $api . $endpoint . "?uuid=$uuid&force=false"; 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) { return preg_replace('/\e[[][A-Za-z0-9];?[0-9]*m?/', '', $text); diff --git a/config/sentry.php b/config/sentry.php index 980c71a6c..01c00e557 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -7,7 +7,7 @@ return [ // The release version of your application // 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 'environment' => config('app.env'), diff --git a/config/version.php b/config/version.php index 4a865bbd4..57d619e4d 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ 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'); + }); + } +}; diff --git a/database/migrations/2023_11_14_121416_add_git_type.php b/database/migrations/2023_11_14_121416_add_git_type.php new file mode 100644 index 000000000..1e2a307fe --- /dev/null +++ b/database/migrations/2023_11_14_121416_add_git_type.php @@ -0,0 +1,34 @@ +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'); + }); + } +}; diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php index 173aa52e8..2753b4bf7 100644 --- a/resources/views/livewire/project/application/general.blade.php +++ b/resources/views/livewire/project/application/general.blade.php @@ -69,10 +69,12 @@
- @if ($application->build_pack === 'dockerfile') + @if ($application->build_pack === 'dockerfile' && !$application->dockerfile) + @endif + @if ($application->build_pack === 'dockerfile') @endif diff --git a/resources/views/livewire/project/shared/webhooks.blade.php b/resources/views/livewire/project/shared/webhooks.blade.php index ba0461bdb..c77ae3315 100644 --- a/resources/views/livewire/project/shared/webhooks.blade.php +++ b/resources/views/livewire/project/shared/webhooks.blade.php @@ -1,10 +1,41 @@ -
+

Webhooks

- + +
+
+

Manual Git Webhooks

+ @if ($githubManualWebhook && $gitlabManualWebhook) +
+
+ + + +
+ + Webhook Configuration on GitHub + + + +
+ + +
+ Save +
+ @else + You are using an official Git App. You do not need manual webhooks. + @endif
diff --git a/routes/webhooks.php b/routes/webhooks.php index bb2c55a3d..a2dbf5a1a 100644 --- a/routes/webhooks.php +++ b/routes/webhooks.php @@ -63,6 +63,244 @@ Route::get('/source/github/install', function () { 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 () { try { $id = null; @@ -150,6 +388,7 @@ Route::post('/source/github/events', function () { $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, @@ -160,7 +399,8 @@ Route::post('/source/github/events', function () { pull_request_id: $pull_request_id, deployment_uuid: $deployment_uuid, 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); return response('Preview Deployment queued.'); @@ -169,7 +409,7 @@ Route::post('/source/github/events', function () { 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(); if ($found) { $found->delete(); diff --git a/versions.json b/versions.json index c960ce7d7..a40a9c2f1 100644 --- a/versions.json +++ b/versions.json @@ -4,7 +4,7 @@ "version": "3.12.36" }, "v4": { - "version": "4.0.0-beta.131" + "version": "4.0.0-beta.132" } } }