From a047971bc1568ff833047d3ca5b6fc4e53209aa7 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Wed, 3 Jun 2026 10:07:57 +0200 Subject: [PATCH] fix(github): use provided app for installation URLs Generate GitHub App installation links and setup cache state from the current app instance, and keep the Livewire app name in sync after permission checks. --- app/Livewire/Source/Github/Change.php | 1 + bootstrap/helpers/github.php | 11 +++++------ tests/Feature/GithubSourceChangeTest.php | 25 ++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/app/Livewire/Source/Github/Change.php b/app/Livewire/Source/Github/Change.php index 74ed812af..648bfe6ee 100644 --- a/app/Livewire/Source/Github/Change.php +++ b/app/Livewire/Source/Github/Change.php @@ -211,6 +211,7 @@ class Change extends Component GithubAppPermissionJob::dispatchSync($this->github_app); $this->github_app->refresh()->makeVisible('client_secret')->makeVisible('webhook_secret'); $this->syncData(false); + $this->name = str($this->github_app->name)->kebab(); $this->dispatch('success', 'Github App permissions updated.'); } catch (\Throwable $e) { diff --git a/bootstrap/helpers/github.php b/bootstrap/helpers/github.php index bb819e4aa..0ec76f6fa 100644 --- a/bootstrap/helpers/github.php +++ b/bootstrap/helpers/github.php @@ -119,18 +119,17 @@ function githubApi(GithubApp|GitlabApp|null $source, string $endpoint, string $m function getInstallationPath(GithubApp $source): string { - $github = GithubApp::where('uuid', $source->uuid)->first(); - $name = str(Str::kebab($github->name)); - $installation_path = $github->html_url === 'https://github.com' ? 'apps' : 'github-apps'; + $name = str(Str::kebab($source->name)); + $installation_path = $source->html_url === 'https://github.com' ? 'apps' : 'github-apps'; $state = Str::random(64); Cache::put('github-app-setup-state:'.hash('sha256', $state), [ 'action' => 'install', - 'github_app_id' => $github->id, - 'team_id' => $github->team_id, + 'github_app_id' => $source->id, + 'team_id' => $source->team_id, ], now()->addMinutes(60)); - return "$github->html_url/$installation_path/$name/installations/new?".http_build_query(['state' => $state]); + return "$source->html_url/$installation_path/$name/installations/new?".http_build_query(['state' => $state]); } function getPermissionsPath(GithubApp $source) diff --git a/tests/Feature/GithubSourceChangeTest.php b/tests/Feature/GithubSourceChangeTest.php index e6f758682..07bc2a2c3 100644 --- a/tests/Feature/GithubSourceChangeTest.php +++ b/tests/Feature/GithubSourceChangeTest.php @@ -124,6 +124,29 @@ describe('GitHub Source Change Component', function () { ]); }); + test('installation path is generated from the provided github app instance', function () { + $githubApp = new GithubApp; + $githubApp->forceFill([ + 'id' => 123, + 'name' => 'Provided GitHub App', + 'html_url' => 'https://github.example.com', + 'team_id' => 456, + ]); + + $installationUrl = getInstallationPath($githubApp); + parse_str(parse_url($installationUrl, PHP_URL_QUERY), $query); + $installState = $query['state'] ?? null; + + expect($installationUrl)->toStartWith('https://github.example.com/github-apps/provided-git-hub-app/installations/new?') + ->and($installState)->not->toBeEmpty() + ->and(Cache::get('github-app-setup-state:'.hash('sha256', $installState))) + ->toMatchArray([ + 'action' => 'install', + 'github_app_id' => 123, + 'team_id' => 456, + ]); + }); + test('defaults webhook endpoint to app url when it is the first available endpoint', function () { config(['app.url' => 'http://localhost:8000']); @@ -389,11 +412,13 @@ describe('GitHub Source Change Component', function () { Livewire::withQueryParams(['github_app_uuid' => $githubApp->uuid]) ->test(Change::class) ->assertSuccessful() + ->assertSet('name', 'test-git-hub-app') ->assertSet('contents', null) ->assertSet('metadata', null) ->assertSet('pullRequests', null) ->call('checkPermissions') ->assertDispatched('success') + ->assertSet('name', 'test-git-hub-app') ->assertSet('contents', 'read') ->assertSet('metadata', 'read') ->assertSet('pullRequests', 'write');