diff --git a/app/Livewire/Source/Github/Change.php b/app/Livewire/Source/Github/Change.php index ed8daa861..4559f59f5 100644 --- a/app/Livewire/Source/Github/Change.php +++ b/app/Livewire/Source/Github/Change.php @@ -97,6 +97,7 @@ protected function rules(): array 'metadata' => 'nullable|string', 'pullRequests' => 'nullable|string', 'privateKeyId' => 'nullable|int', + 'webhook_endpoint' => ['required', 'string', 'url'], ]; } diff --git a/resources/views/livewire/source/github/change.blade.php b/resources/views/livewire/source/github/change.blade.php index 0769a5732..cd8a98833 100644 --- a/resources/views/livewire/source/github/change.blade.php +++ b/resources/views/livewire/source/github/change.blade.php @@ -259,8 +259,13 @@ class="px-2 py-1 text-xs font-bold uppercase tracking-wide bg-coollabs/10 dark:b
@if (!isCloud() || isDev()) - + + + @if ($fqdn) @endif @@ -273,7 +278,7 @@ class="px-2 py-1 text-xs font-bold uppercase tracking-wide bg-coollabs/10 dark:b @if (config('app.url')) @endif - + @else
You need to register a GitHub App before using this source.
@endif @@ -337,10 +342,10 @@ function createGithubApp(webhook_endpoint, preview_deployment_permissions, admin uuid } = @js($github_app->only(['organization', 'html_url', 'uuid'])); if (!webhook_endpoint) { - alert('Please select a webhook endpoint.'); + alert('Please enter a webhook endpoint.'); return; } - let baseUrl = webhook_endpoint; + let baseUrl = webhook_endpoint.trim().replace(/\/+$/, ''); const name = @js($name); const manifestState = @js($manifestState); const isDev = @js(config('app.env')) === diff --git a/tests/Feature/GithubSourceChangeTest.php b/tests/Feature/GithubSourceChangeTest.php index 8437f09af..57b020ef6 100644 --- a/tests/Feature/GithubSourceChangeTest.php +++ b/tests/Feature/GithubSourceChangeTest.php @@ -20,8 +20,27 @@ // Set current team $this->actingAs($this->user); session(['currentTeam' => $this->team]); + + InstanceSettings::forceCreate([ + 'id' => 0, + 'fqdn' => null, + 'public_ipv4' => null, + 'public_ipv6' => null, + ]); }); +function validPrivateKey(): string +{ + $key = openssl_pkey_new([ + 'private_key_bits' => 2048, + 'private_key_type' => OPENSSL_KEYTYPE_RSA, + ]); + + openssl_pkey_export($key, $privateKey); + + return $privateKey; +} + describe('GitHub Source Change Component', function () { test('all github app form controls declare explicit authorization', function () { $view = file_get_contents(resource_path('views/livewire/source/github/change.blade.php')); @@ -68,8 +87,7 @@ test('defaults webhook endpoint to app url when it is the first available endpoint', function () { config(['app.url' => 'http://localhost:8000']); - InstanceSettings::forceCreate([ - 'id' => 0, + InstanceSettings::findOrFail(0)->update([ 'fqdn' => null, 'public_ipv4' => null, 'public_ipv6' => null, @@ -91,10 +109,39 @@ ->assertSet('webhook_endpoint', 'http://localhost:8000'); }); + test('webhook endpoint can be typed manually when creating github app', function () { + config(['app.url' => 'http://localhost:8000']); + + InstanceSettings::findOrFail(0)->update([ + 'fqdn' => 'http://staging.example.com', + 'public_ipv4' => '84.1.202.183', + 'public_ipv6' => null, + ]); + + $githubApp = GithubApp::create([ + 'name' => 'Test GitHub App', + 'api_url' => 'https://api.github.com', + 'html_url' => 'https://github.com', + 'custom_user' => 'git', + 'custom_port' => 22, + 'team_id' => $this->team->id, + 'is_system_wide' => false, + ]); + + Livewire::withQueryParams(['github_app_uuid' => $githubApp->uuid]) + ->test(Change::class) + ->assertSuccessful() + ->set('webhook_endpoint', 'https://staging.example.com') + ->assertSet('webhook_endpoint', 'https://staging.example.com') + ->assertSee('Type or select the public URL GitHub should use for webhooks', false) + ->assertSee('https://staging.example.com') + ->assertSee('webhook-endpoint-suggestions'); + }); + test('can mount with fully configured github app', function () { $privateKey = PrivateKey::create([ 'name' => 'Test Key', - 'private_key' => 'test-private-key-content', + 'private_key' => validPrivateKey(), 'team_id' => $this->team->id, ]); @@ -128,7 +175,7 @@ test('can update github app from null to valid values', function () { $privateKey = PrivateKey::create([ 'name' => 'Test Key', - 'private_key' => 'test-private-key-content', + 'private_key' => validPrivateKey(), 'team_id' => $this->team->id, ]); @@ -201,8 +248,8 @@ // Verify the database was updated $githubApp->refresh(); - expect($githubApp->app_id)->toBe('1234567890'); - expect($githubApp->installation_id)->toBe('1234567890'); + expect($githubApp->app_id)->toBe(1234567890); + expect($githubApp->installation_id)->toBe(1234567890); }); test('checkPermissions validates required fields', function () { @@ -223,6 +270,8 @@ ->assertSuccessful() ->call('checkPermissions') ->assertDispatched('error', function ($event, $message) { + $message = is_array($message) ? implode(' ', $message) : $message; + return str_contains($message, 'App ID') && str_contains($message, 'Private Key'); }); }); @@ -246,6 +295,8 @@ ->assertSuccessful() ->call('checkPermissions') ->assertDispatched('error', function ($event, $message) { + $message = is_array($message) ? implode(' ', $message) : $message; + return str_contains($message, 'Private Key not found'); }); });