From d8972e97c976c0111c3399bf41e38da9c19ca562 Mon Sep 17 00:00:00 2001
From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com>
Date: Wed, 13 May 2026 09:31:28 +0200
Subject: [PATCH 01/12] fix(previews): clean up closed PR previews after update
failures
Catch and report failures while updating closed pull request status so preview deployment cleanup still runs for closed GitHub pull request webhooks.
Add coverage for cleanup continuing when GitHub comment cleanup fails.
---
app/Jobs/ProcessGithubPullRequestWebhook.php | 24 +++++--
.../ProcessGithubPullRequestWebhookTest.php | 72 +++++++++++++++++++
2 files changed, 89 insertions(+), 7 deletions(-)
create mode 100644 tests/Feature/ProcessGithubPullRequestWebhookTest.php
diff --git a/app/Jobs/ProcessGithubPullRequestWebhook.php b/app/Jobs/ProcessGithubPullRequestWebhook.php
index 54e386676..5a390e8ed 100644
--- a/app/Jobs/ProcessGithubPullRequestWebhook.php
+++ b/app/Jobs/ProcessGithubPullRequestWebhook.php
@@ -14,6 +14,7 @@
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
+use Throwable;
use Visus\Cuid2\Cuid2;
class ProcessGithubPullRequestWebhook implements ShouldBeEncrypted, ShouldQueue
@@ -70,16 +71,25 @@ private function handleClosedAction(Application $application): void
->first();
if ($found) {
- ApplicationPullRequestUpdateJob::dispatchSync(
- application: $application,
- preview: $found,
- status: ProcessStatus::CLOSED
- );
-
- CleanupPreviewDeployment::run($application, $this->pullRequestId, $found);
+ try {
+ $this->dispatchPullRequestClosedUpdate($application, $found);
+ } catch (Throwable $e) {
+ report($e);
+ } finally {
+ CleanupPreviewDeployment::run($application, $this->pullRequestId, $found);
+ }
}
}
+ protected function dispatchPullRequestClosedUpdate(Application $application, ApplicationPreview $preview): void
+ {
+ ApplicationPullRequestUpdateJob::dispatchSync(
+ application: $application,
+ preview: $preview,
+ status: ProcessStatus::CLOSED
+ );
+ }
+
private function handleOpenAction(Application $application, ?GithubApp $githubApp): void
{
if (! $application->isPRDeployable()) {
diff --git a/tests/Feature/ProcessGithubPullRequestWebhookTest.php b/tests/Feature/ProcessGithubPullRequestWebhookTest.php
new file mode 100644
index 000000000..6e0e9241e
--- /dev/null
+++ b/tests/Feature/ProcessGithubPullRequestWebhookTest.php
@@ -0,0 +1,72 @@
+ InstanceSettings::firstOrCreate(['id' => 0]));
+
+ $this->team = Team::factory()->create();
+ $this->server = Server::factory()->create(['team_id' => $this->team->id]);
+ $this->destination = StandaloneDocker::where('server_id', $this->server->id)->first();
+ $this->project = Project::factory()->create(['team_id' => $this->team->id]);
+ $this->environment = Environment::factory()->create(['project_id' => $this->project->id]);
+
+ $this->application = Application::factory()->create([
+ 'environment_id' => $this->environment->id,
+ 'destination_id' => $this->destination->id,
+ 'destination_type' => $this->destination->getMorphClass(),
+ ]);
+});
+
+it('cleans up a closed pull request preview when pull request comment cleanup fails', function () {
+ $preview = ApplicationPreview::create([
+ 'application_id' => $this->application->id,
+ 'pull_request_id' => 42,
+ 'pull_request_html_url' => 'https://github.com/example/repo/pull/42',
+ ]);
+
+ CleanupPreviewDeployment::shouldRun()
+ ->once()
+ ->withArgs(fn (Application $application, int $pullRequestId, ApplicationPreview $applicationPreview): bool => $application->is($this->application)
+ && $pullRequestId === 42
+ && $applicationPreview->is($preview))
+ ->andReturn([
+ 'cancelled_deployments' => 0,
+ 'killed_containers' => 0,
+ 'status' => 'success',
+ ]);
+
+ $job = new class(
+ applicationId: $this->application->id,
+ githubAppId: null,
+ action: 'closed',
+ pullRequestId: 42,
+ pullRequestHtmlUrl: 'https://github.com/example/repo/pull/42',
+ pullRequestTitle: null,
+ beforeSha: null,
+ afterSha: null,
+ commitSha: 'HEAD',
+ authorAssociation: 'OWNER',
+ fullName: 'example/repo',
+ ) extends ProcessGithubPullRequestWebhook
+ {
+ protected function dispatchPullRequestClosedUpdate(Application $application, ApplicationPreview $preview): void
+ {
+ throw new RuntimeException('GitHub comment cleanup failed.');
+ }
+ };
+
+ $job->handle();
+});
From 7053f560c5d01ec64d2b7da45c15b83cd66be70b Mon Sep 17 00:00:00 2001
From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com>
Date: Thu, 4 Jun 2026 15:41:38 +0200
Subject: [PATCH 02/12] chore(navbar): replace theme selector with cycle button
---
resources/views/components/navbar.blade.php | 36 ++++-----------------
tests/Unit/NavbarThemeSwitcherTest.php | 13 ++++++++
2 files changed, 20 insertions(+), 29 deletions(-)
create mode 100644 tests/Unit/NavbarThemeSwitcherTest.php
diff --git a/resources/views/components/navbar.blade.php b/resources/views/components/navbar.blade.php
index ecd798cc2..c2ca28a2d 100644
--- a/resources/views/components/navbar.blade.php
+++ b/resources/views/components/navbar.blade.php
@@ -376,7 +376,11 @@ class="{{ request()->is('settings*') ? 'menu-item-active menu-item' : 'menu-item
-
+
+