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.
This commit is contained in:
Andras Bacsai 2026-05-13 09:31:28 +02:00
parent f098895abf
commit d8972e97c9
2 changed files with 89 additions and 7 deletions

View file

@ -14,6 +14,7 @@
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Throwable;
use Visus\Cuid2\Cuid2; use Visus\Cuid2\Cuid2;
class ProcessGithubPullRequestWebhook implements ShouldBeEncrypted, ShouldQueue class ProcessGithubPullRequestWebhook implements ShouldBeEncrypted, ShouldQueue
@ -70,16 +71,25 @@ private function handleClosedAction(Application $application): void
->first(); ->first();
if ($found) { if ($found) {
ApplicationPullRequestUpdateJob::dispatchSync( try {
application: $application, $this->dispatchPullRequestClosedUpdate($application, $found);
preview: $found, } catch (Throwable $e) {
status: ProcessStatus::CLOSED report($e);
); } finally {
CleanupPreviewDeployment::run($application, $this->pullRequestId, $found);
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 private function handleOpenAction(Application $application, ?GithubApp $githubApp): void
{ {
if (! $application->isPRDeployable()) { if (! $application->isPRDeployable()) {

View file

@ -0,0 +1,72 @@
<?php
use App\Actions\Application\CleanupPreviewDeployment;
use App\Jobs\ProcessGithubPullRequestWebhook;
use App\Models\Application;
use App\Models\ApplicationPreview;
use App\Models\Environment;
use App\Models\InstanceSettings;
use App\Models\Project;
use App\Models\Server;
use App\Models\StandaloneDocker;
use App\Models\Team;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
beforeEach(function () {
InstanceSettings::unguarded(fn () => 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();
});