From 499a8666db592a32c690021b72486580bd2de5ab Mon Sep 17 00:00:00 2001
From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com>
Date: Wed, 27 May 2026 08:37:10 +0200
Subject: [PATCH] fix(github): allow custom webhook endpoint input
---
app/Livewire/Source/Github/Change.php | 1 +
.../livewire/source/github/change.blade.php | 15 +++--
tests/Feature/GithubSourceChangeTest.php | 63 +++++++++++++++++--
3 files changed, 68 insertions(+), 11 deletions(-)
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())
-
+
+
+
+
@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');
});
});