From a137ffe85280ef5279cfd48da280ecf8fd2bc5cb Mon Sep 17 00:00:00 2001 From: Mark Gregory Date: Wed, 16 Sep 2026 12:32:50 +0100 Subject: [PATCH] new deployer command --- Modules/Countries/Routes/web.php | 2 +- Modules/Documentation/Routes/web.php | 2 +- .../Http/Controllers/GitWebhookController.php | 128 ++++++++++++------ 3 files changed, 87 insertions(+), 45 deletions(-) diff --git a/Modules/Countries/Routes/web.php b/Modules/Countries/Routes/web.php index c1dfe37c..a5cb8346 100644 --- a/Modules/Countries/Routes/web.php +++ b/Modules/Countries/Routes/web.php @@ -5,7 +5,7 @@ use Modules\Countries\Http\Controllers\CountriesController; use Modules\Countries\Http\Controllers\CountrySyncController; use Modules\Countries\Http\Controllers\ReferenceDataController; -const COUNTRY_ROUTE = COUNTRY_ROUTE; +const COUNTRY_ROUTE = '/{country}'; Route::prefix('countries')->name('countries.')->middleware(['web', 'auth'])->group(function (): void { Route::get('/', CountriesController::class)->name('index'); diff --git a/Modules/Documentation/Routes/web.php b/Modules/Documentation/Routes/web.php index 5c8ec508..87624a4c 100644 --- a/Modules/Documentation/Routes/web.php +++ b/Modules/Documentation/Routes/web.php @@ -6,7 +6,7 @@ use Illuminate\Support\Facades\Route; use Modules\Documentation\Http\Controllers\DocumentationCategoryController; use Modules\Documentation\Http\Controllers\DocumentationController; -const DOCUMENT_ROUTE = DOCUMENT_ROUTE; +const DOCUMENT_ROUTE = '/documents/{document}'; Route::middleware(['web', 'auth']) ->prefix('documentation') diff --git a/Modules/Helpdesk/Http/Controllers/GitWebhookController.php b/Modules/Helpdesk/Http/Controllers/GitWebhookController.php index 4e7bde91..4da99d28 100644 --- a/Modules/Helpdesk/Http/Controllers/GitWebhookController.php +++ b/Modules/Helpdesk/Http/Controllers/GitWebhookController.php @@ -22,6 +22,12 @@ final class GitWebhookController private const SHA256_PREFIX = 'sha256='; private const SHA1_PREFIX = 'sha1='; + + private const REQUEST_ID_HEADER = 'X-Request-Id'; + + private const GITHUB_DELIVERY_HEADER = 'X-GitHub-Delivery'; + + private const GITHUB_EVENT_HEADER = 'X-GitHub-Event'; /** * Ingest a signed Git provider webhook for a registered Helpdesk repository. */ @@ -235,43 +241,11 @@ final class GitWebhookController return null; } - $payloadValues = array_values(array_filter([ - Arr::get($payload, 'repository.full_name'), - Arr::get($payload, 'repository.name'), - Arr::get($payload, 'repository.html_url'), - Arr::get($payload, 'repository.clone_url'), - Arr::get($payload, 'repository.ssh_url'), - Arr::get($payload, 'repository.url'), - Arr::get($payload, 'repository.owner.username') && Arr::get($payload, 'repository.name') - ? Arr::get($payload, 'repository.owner.username') . '/' . Arr::get($payload, 'repository.name') - : null, - ], static fn (mixed $value): bool => is_scalar($value) && trim((string) $value) !== '')); - - $normalisedPayloadValues = array_values(array_filter(array_map( - fn (mixed $value): ?string => $this->normaliseRepositoryIdentity((string) $value), - $payloadValues, - ))); + $normalisedPayloadValues = $this->normalisedPayloadRepositoryValues($payload); foreach ($repositories as $repository) { - $repositoryValues = array_filter([ - $repository->remote_url, - $repository->name, - Str::of((string) $repository->remote_url)->afterLast(':')->beforeLast('.git')->value(), - ], static fn (mixed $value): bool => is_scalar($value) && trim((string) $value) !== ''); - - $normalisedRepositoryValues = array_values(array_filter(array_map( - fn (mixed $value): ?string => $this->normaliseRepositoryIdentity((string) $value), - $repositoryValues, - ))); - - foreach ($normalisedPayloadValues as $payloadValue) { - foreach ($normalisedRepositoryValues as $repositoryValue) { - if ($payloadValue === $repositoryValue - || str_ends_with($repositoryValue, '/' . $payloadValue) - || str_ends_with($payloadValue, '/' . $repositoryValue)) { - return $repository; - } - } + if ($this->repositoryMatchesPayload($repository, $normalisedPayloadValues)) { + return $repository; } } @@ -287,6 +261,74 @@ final class GitWebhookController return null; } + + /** @param array $payload + * @return list + */ + private function normalisedPayloadRepositoryValues(array $payload): array + { + $name = Arr::get($payload, 'repository.name'); + $owner = Arr::get($payload, 'repository.owner.username'); + + return $this->normaliseRepositoryValues([ + Arr::get($payload, 'repository.full_name'), + $name, + Arr::get($payload, 'repository.html_url'), + Arr::get($payload, 'repository.clone_url'), + Arr::get($payload, 'repository.ssh_url'), + Arr::get($payload, 'repository.url'), + $owner && $name ? $owner . '/' . $name : null, + ]); + } + + /** @param list $payloadValues */ + private function repositoryMatchesPayload(GitRepository $repository, array $payloadValues): bool + { + $repositoryValues = $this->normaliseRepositoryValues([ + $repository->remote_url, + $repository->name, + Str::of((string) $repository->remote_url)->afterLast(':')->beforeLast('.git')->value(), + ]); + + foreach ($payloadValues as $payloadValue) { + foreach ($repositoryValues as $repositoryValue) { + if ($this->repositoryIdentitiesMatch($payloadValue, $repositoryValue)) { + return true; + } + } + } + + return false; + } + + /** @param array $values + * @return list + */ + private function normaliseRepositoryValues(array $values): array + { + $normalised = []; + + foreach ($values as $value) { + if (! is_scalar($value) || trim((string) $value) === '') { + continue; + } + + $identity = $this->normaliseRepositoryIdentity((string) $value); + if ($identity !== null) { + $normalised[] = $identity; + } + } + + return $normalised; + } + + private function repositoryIdentitiesMatch(string $payloadValue, string $repositoryValue): bool + { + return $payloadValue === $repositoryValue + || str_ends_with($repositoryValue, '/' . $payloadValue) + || str_ends_with($payloadValue, '/' . $repositoryValue); + } + /** * Normalise a repository name or URL so Gitea payloads match local settings reliably. */ @@ -341,11 +383,11 @@ final class GitWebhookController private function eventName(Request $request, string $provider): ?string { return match ($provider) { - 'github' => $request->header('X-GitHub-Event'), + 'github' => $request->header(self::GITHUB_EVENT_HEADER), 'gitlab' => $request->header('X-Gitlab-Event'), - 'gitea' => $request->header('X-Gitea-Event') ?: $request->header('X-Gitea-Event-Type') ?: $request->header('X-GitHub-Event'), + 'gitea' => $request->header('X-Gitea-Event') ?: $request->header('X-Gitea-Event-Type') ?: $request->header(self::GITHUB_EVENT_HEADER), 'bitbucket' => $request->header('X-Event-Key'), - default => $request->header('X-Helpdesk-Event') ?: $request->header('X-GitHub-Event') ?: $request->header('X-Gitlab-Event') ?: 'push', + default => $request->header('X-Helpdesk-Event') ?: $request->header(self::GITHUB_EVENT_HEADER) ?: $request->header('X-Gitlab-Event') ?: 'push', }; } @@ -355,11 +397,11 @@ final class GitWebhookController private function deliveryId(Request $request, string $provider): ?string { return match ($provider) { - 'github' => $request->header('X-GitHub-Delivery'), - 'gitlab' => $request->header('X-Gitlab-Event-UUID') ?: $request->header('X-Request-Id'), - 'gitea' => $request->header('X-Gitea-Delivery') ?: $request->header('X-GitHub-Delivery') ?: $request->header('X-Request-Id'), - 'bitbucket' => $request->header('X-Request-UUID') ?: $request->header('X-Request-Id'), - default => $request->header('X-Helpdesk-Delivery') ?: $request->header('X-GitHub-Delivery') ?: $request->header('X-Request-Id'), + 'github' => $request->header(self::GITHUB_DELIVERY_HEADER), + 'gitlab' => $request->header('X-Gitlab-Event-UUID') ?: $request->header(self::REQUEST_ID_HEADER), + 'gitea' => $request->header('X-Gitea-Delivery') ?: $request->header(self::GITHUB_DELIVERY_HEADER) ?: $request->header(self::REQUEST_ID_HEADER), + 'bitbucket' => $request->header('X-Request-UUID') ?: $request->header(self::REQUEST_ID_HEADER), + default => $request->header('X-Helpdesk-Delivery') ?: $request->header(self::GITHUB_DELIVERY_HEADER) ?: $request->header(self::REQUEST_ID_HEADER), }; } }