mirror of
https://github.com/coollabsio/coolify.git
synced 2026-03-11 08:55:47 +00:00
Add runner execution observability and lifecycle hardening for self-hosted GitHub Actions runners, including: - scheduled artifact cleanup job for cached runner tarballs/templates - workflow_job in_progress handling to mark executions as running - safer cleanup failure handling and non-functional server fallback - persisted workflow job HTML URLs with execution "Open" links in UI - configurable runner group name sync (UI + provisioning + GitHub API) - webhook events persistence and auto-fix for missing required events - strict enum/status checks and related model cast/query improvements Also includes migrations and expanded feature/unit coverage for webhook, provisioning, cleanup, and runner group/event sync behaviors.
101 lines
2.9 KiB
PHP
101 lines
2.9 KiB
PHP
<?php
|
|
|
|
use App\Models\GithubApp;
|
|
use App\Models\Team;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Http;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
function setupGithubAppWithKey(array $appAttributes = []): GithubApp
|
|
{
|
|
$team = Team::factory()->create();
|
|
$privateKeyId = DB::table('private_keys')->insertGetId([
|
|
'uuid' => fake()->uuid(),
|
|
'name' => 'test-key',
|
|
'private_key' => 'test-key-value',
|
|
'team_id' => $team->id,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
|
|
return GithubApp::create(array_merge([
|
|
'uuid' => fake()->uuid(),
|
|
'name' => 'test-app',
|
|
'html_url' => 'https://github.com',
|
|
'api_url' => 'https://api.github.com',
|
|
'team_id' => $team->id,
|
|
'app_id' => 12345,
|
|
'private_key_id' => $privateKeyId,
|
|
], $appAttributes));
|
|
}
|
|
|
|
it('stores webhook events from github api response', function () {
|
|
$app = setupGithubAppWithKey();
|
|
|
|
Http::fake([
|
|
'*/app' => Http::response([
|
|
'permissions' => ['contents' => 'read', 'metadata' => 'read'],
|
|
'events' => ['push', 'installation', 'pull_request'],
|
|
]),
|
|
]);
|
|
|
|
$response = Http::get('https://api.github.com/app')->json();
|
|
$app->webhook_events = data_get($response, 'events', []);
|
|
$app->save();
|
|
$app->refresh();
|
|
|
|
expect($app->webhook_events)->toBe(['push', 'installation', 'pull_request']);
|
|
});
|
|
|
|
it('auto-fixes missing events by patching github api', function () {
|
|
$app = setupGithubAppWithKey([
|
|
'webhook_events' => ['push'],
|
|
'pull_requests' => 'write',
|
|
]);
|
|
|
|
Http::fake([
|
|
'api.github.com/app' => Http::response([
|
|
'events' => ['push', 'pull_request'],
|
|
]),
|
|
]);
|
|
|
|
$missing = $app->missingWebhookEvents();
|
|
expect($missing)->toContain('pull_request');
|
|
|
|
$updatedEvents = array_values(array_unique(
|
|
array_merge($app->webhook_events ?? [], $missing)
|
|
));
|
|
|
|
$response = Http::withHeaders([
|
|
'Authorization' => 'Bearer fake-jwt',
|
|
'Accept' => 'application/vnd.github+json',
|
|
])->patch('https://api.github.com/app', [
|
|
'events' => $updatedEvents,
|
|
]);
|
|
|
|
expect($response->successful())->toBeTrue();
|
|
|
|
$app->webhook_events = data_get($response->json(), 'events', $updatedEvents);
|
|
$app->save();
|
|
$app->refresh();
|
|
|
|
expect($app->webhook_events)
|
|
->toContain('push')
|
|
->toContain('pull_request');
|
|
|
|
Http::assertSent(function ($request) {
|
|
return $request->method() === 'PATCH'
|
|
&& str_contains($request->url(), '/app')
|
|
&& in_array('pull_request', $request['events']);
|
|
});
|
|
});
|
|
|
|
it('does not patch when no events are missing', function () {
|
|
$app = setupGithubAppWithKey([
|
|
'webhook_events' => ['push', 'installation'],
|
|
]);
|
|
|
|
expect($app->missingWebhookEvents())->toBe([]);
|
|
});
|