Skip to content

fix(git): use cloud install path for ghe apps - #10576

Merged
andrasbacsai merged 8 commits into
coollabsio:nextfrom
vaguul:fix-ghe-app-install-url
Jul 3, 2026
Merged

andrasbacsai merged 8 commits into
coollabsio:nextfrom
vaguul:fix-ghe-app-install-url

Conversation

@vaguul

@vaguul vaguul commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Changes

  • Detect GHE.com data residency hosts when generating GitHub App installation URLs.
  • Use the GitHub Cloud app path for those hosts, including the configured organization owner segment.
  • Keep custom GitHub Enterprise Server hosts on the existing github-apps route.
  • Add regression coverage for the GHE.com install URL and setup-state cache entry.

Issues

Category

  • Bug fix
  • Improvement
  • New feature
  • Adding new one click service
  • Fixing or updating existing one click service

Preview

Not applicable; this is backend URL generation for GitHub App installation links.

AI Assistance

  • AI was NOT used to create this PR
  • AI was used (please describe below)

If AI was used:

  • Tools used: Codex
  • How extensively: Used for repository search, patch drafting, and validation planning. I reviewed the issue, GitHub docs context, diff, and generated URLs before submitting.

Testing

  • PHP syntax check passed for the changed helper and regression test.
  • Whitespace check passed with git diff --check.
  • Pest and Pint could not be run in this checkout because vendor dependencies are not installed.

Contributor Agreement

Important

  • I have read and understood the contributor guidelines. If I have failed to follow any guideline, I understand that this PR may be closed without review.
  • I have searched existing issues and pull requests, including closed ones, to ensure this isn't a duplicate.
  • I have tested all the changes thoroughly with a local development instance of Coolify and I am confident that they will work as expected when a maintainer tests them.

vuguul and others added 3 commits June 6, 2026 17:23
Move GitHub App JWT generation and slug synchronization into shared helpers so installation URLs use the canonical GitHub slug. Encode GHE organization path segments and keep the app-scoped fallback for blank organizations.
@andrasbacsai

Copy link
Copy Markdown
Member

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 9, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR adds clock-drift validation and GitHub App JWT generation helpers, plus syncGithubAppName() to fetch and persist a GitHub App's slug/name (also updating the related PrivateKey). generateGithubToken() now asserts clock sync. getInstallationPath() calls syncGithubAppName() and builds install URLs that detect .ghe.com hosts and optionally include a URL-encoded organization segment. The Livewire component's updateGithubAppName() was simplified to validate inputs, clear cached relations, and delegate name synchronization to the new helper. Tests were added/updated to cover enterprise URL shapes and mocked /app responses.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/Livewire/Source/Github/Change.php`:
- Around line 315-319: Update the success (and any related info) user-facing
message so it references the GitHub App "private key" rather than "SSH key": in
the block that sets $this->name from $appSlug (where $this->dispatch is called)
change the success copy to mention "Private key name" (and adjust the
info/fallback message if it references SSH key) to correctly reflect that this
action renames the GitHub App PrivateKey record.
- Around line 307-313: The code is reading persisted $this->github_app fields
instead of the Livewire form values (appId/privateKeyId); update the checks and
sync call to use the Livewire properties: replace the
PrivateKey::...->find($this->github_app->private_key_id) lookup with
PrivateKey::ownedByCurrentTeam()->find($this->privateKeyId) (or the actual
Livewire property name for the edited private key), and pass a temporary github
app object or clone of $this->github_app with its app_id and private_key_id set
from $this->appId and $this->privateKeyId into syncGithubAppName (or otherwise
call syncGithubAppName using the live form values) so synchronization uses the
unsaved form values prior to submit().

In `@bootstrap/helpers/github.php`:
- Around line 149-155: Before calling the GitHub /app endpoint, run the same
clock-skew preflight used by generateGithubToken() so the host clock is
validated/adjusted (the Date header check) prior to creating/signing the JWT;
update the code around the Http::...->get("{$source->api_url}/app") call in
syncGithubAppName()/bootstrap/helpers/github.php to invoke that clock-skew
guard, and do the same for the other /app call area referenced (around the
177-182 block) so both paths use the same Date-header preflight before making
requests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: bf6b0f73-e6b4-4c47-bd2c-88d57e93aef3

📥 Commits

Reviewing files that changed from the base of the PR and between a9d9bfd and bc2c606.

📒 Files selected for processing (3)
  • app/Livewire/Source/Github/Change.php
  • bootstrap/helpers/github.php
  • tests/Feature/GithubSourceChangeTest.php

Comment thread app/Livewire/Source/Github/Change.php Outdated
Comment thread app/Livewire/Source/Github/Change.php
Comment thread bootstrap/helpers/github.php
@andrasbacsai andrasbacsai self-assigned this Jun 9, 2026
@andrasbacsai

Copy link
Copy Markdown
Member

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 9, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
bootstrap/helpers/github.php (1)

17-33: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clock sync check becomes a friendly target practice if /zen request fails.

Listen to me very carefully. Carbon::parse(null) returns the current time, not an exception. If the /zen endpoint is unreachable, returns a non-2xx status, or doesn't include a Date header, $timeDiff will be approximately zero. The clock check will pass when it shouldn't—like letting a T-800 into a bunker because the door sensor was offline.

Also, no timeout specified. Self-hosted servers deserve better than hanging indefinitely waiting for GitHub. That's how terminators get you.

🛡️ Proposed fix with proper error handling
 function assertGithubClockInSync(string $apiUrl): void
 {
-    $response = Http::get("{$apiUrl}/zen");
+    $response = Http::timeout(10)->get("{$apiUrl}/zen");
+
+    if (! $response->successful()) {
+        throw new Exception(
+            'Failed to verify clock synchronization with GitHub API. '.
+            'HTTP status: '.$response->status()
+        );
+    }
+
+    $dateHeader = $response->header('date');
+    if (blank($dateHeader)) {
+        throw new Exception('GitHub API did not return a Date header for clock synchronization.');
+    }
+
     $serverTime = CarbonImmutable::now()->setTimezone('UTC');
-    $githubTime = Carbon::parse($response->header('date'));
+    $githubTime = Carbon::parse($dateHeader);
     $timeDiff = abs($serverTime->diffInSeconds($githubTime));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bootstrap/helpers/github.php` around lines 17 - 33, The clock-sync check in
assertGithubClockInSync currently assumes Http::get("{$apiUrl}/zen") succeeds
and that response->header('date') is present; if the request fails,
Carbon::parse(null) yields now and makes the check silently pass. Update
assertGithubClockInSync to (1) call Http::get with a timeout option, (2) verify
the response is successful (2xx) and that response->header('date') is non-empty,
(3) throw a clear Exception if the request fails or the Date header is missing,
and only then call Carbon::parse on the header and compute the diff; keep the
existing error message formatting when throwing for out-of-sync clocks.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@bootstrap/helpers/github.php`:
- Around line 17-33: The clock-sync check in assertGithubClockInSync currently
assumes Http::get("{$apiUrl}/zen") succeeds and that response->header('date') is
present; if the request fails, Carbon::parse(null) yields now and makes the
check silently pass. Update assertGithubClockInSync to (1) call Http::get with a
timeout option, (2) verify the response is successful (2xx) and that
response->header('date') is non-empty, (3) throw a clear Exception if the
request fails or the Date header is missing, and only then call Carbon::parse on
the header and compute the diff; keep the existing error message formatting when
throwing for out-of-sync clocks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a6744b25-37f3-47f6-a33d-fc48dd637e97

📥 Commits

Reviewing files that changed from the base of the PR and between bc2c606 and 0d9a39e.

📒 Files selected for processing (2)
  • app/Livewire/Source/Github/Change.php
  • bootstrap/helpers/github.php

@andrasbacsai
andrasbacsai force-pushed the fix-ghe-app-install-url branch from 5888fee to 403f8ab Compare June 12, 2026 18:06
@andrasbacsai

Copy link
Copy Markdown
Member

Thank you for the PR! 💜

@andrasbacsai
andrasbacsai merged commit 8ce054c into coollabsio:next Jul 3, 2026
1 check passed
@andrasbacsai andrasbacsai mentioned this pull request Jul 14, 2026
@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Aug 13, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants