From 968508583d31c93bb3783d8cb17276e92280f64d Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Wed, 1 Apr 2026 09:11:56 +0200 Subject: [PATCH] fix(project): handle slash branches in public repo URLs Parse `/tree/...` URLs by first capturing the full branch candidate, then iteratively resolving valid branch names for GitHub API lookups and deriving the remaining path as base directory. Also adjust env var editor/input view classes (`font-sans`, `w-full`) and add/extend feature tests for both branch parsing and multiline toggle rendering. --- .../Project/New/PublicGitRepository.php | 38 +++++++--- .../shared/environment-variable/all.blade.php | 8 +-- .../environment-variable/show.blade.php | 2 +- ...ronmentVariableMultilineToggleViewTest.php | 9 +++ .../PublicGitRepositoryBranchParsingTest.php | 71 +++++++++++++++++++ 5 files changed, 113 insertions(+), 15 deletions(-) create mode 100644 tests/Feature/PublicGitRepositoryBranchParsingTest.php diff --git a/app/Livewire/Project/New/PublicGitRepository.php b/app/Livewire/Project/New/PublicGitRepository.php index 62ac7ec0d..dbfa15a55 100644 --- a/app/Livewire/Project/New/PublicGitRepository.php +++ b/app/Livewire/Project/New/PublicGitRepository.php @@ -208,13 +208,8 @@ private function getGitSource() if ($this->repository_url_parsed->getSegment(3) === 'tree') { $path = str($this->repository_url_parsed->getPath())->trim('/'); - $this->git_branch = str($path)->after('tree/')->before('/')->value(); - $this->base_directory = str($path)->after($this->git_branch)->after('/')->value(); - if (filled($this->base_directory)) { - $this->base_directory = '/'.$this->base_directory; - } else { - $this->base_directory = '/'; - } + $this->git_branch = str($path)->after('tree/')->value(); + $this->base_directory = '/'; } else { $this->git_branch = 'main'; } @@ -235,9 +230,32 @@ private function getBranch() return; } if ($this->git_source->getMorphClass() === GithubApp::class) { - ['rate_limit_remaining' => $this->rate_limit_remaining, 'rate_limit_reset' => $this->rate_limit_reset] = githubApi(source: $this->git_source, endpoint: "/repos/{$this->git_repository}/branches/{$this->git_branch}"); - $this->rate_limit_reset = Carbon::parse((int) $this->rate_limit_reset)->format('Y-M-d H:i:s'); - $this->branchFound = true; + $originalBranch = $this->git_branch; + $branchToTry = $originalBranch; + + while (true) { + try { + $encodedBranch = urlencode($branchToTry); + ['rate_limit_remaining' => $this->rate_limit_remaining, 'rate_limit_reset' => $this->rate_limit_reset] = githubApi(source: $this->git_source, endpoint: "/repos/{$this->git_repository}/branches/{$encodedBranch}"); + $this->rate_limit_reset = Carbon::parse((int) $this->rate_limit_reset)->format('Y-M-d H:i:s'); + $this->git_branch = $branchToTry; + + $remaining = str($originalBranch)->after($branchToTry)->trim('/')->value(); + $this->base_directory = filled($remaining) ? '/'.$remaining : '/'; + + $this->branchFound = true; + + return; + } catch (\Throwable $e) { + if (str_contains($branchToTry, '/')) { + $branchToTry = str($branchToTry)->beforeLast('/')->value(); + + continue; + } + + throw $e; + } + } } } diff --git a/resources/views/livewire/project/shared/environment-variable/all.blade.php b/resources/views/livewire/project/shared/environment-variable/all.blade.php index 28c67c5b4..2ae3ad166 100644 --- a/resources/views/livewire/project/shared/environment-variable/all.blade.php +++ b/resources/views/livewire/project/shared/environment-variable/all.blade.php @@ -84,21 +84,21 @@ Inline comments with space before # (e.g., KEY=value #comment) are stripped. - @if ($showPreview) - @endif Save All Environment Variables @else - @if ($showPreview) - @endif @endcan diff --git a/resources/views/livewire/project/shared/environment-variable/show.blade.php b/resources/views/livewire/project/shared/environment-variable/show.blade.php index 6e93d296b..b873a6f05 100644 --- a/resources/views/livewire/project/shared/environment-variable/show.blade.php +++ b/resources/views/livewire/project/shared/environment-variable/show.blade.php @@ -155,7 +155,7 @@ @else -
+
toContain('wire:key="env-show-value-textarea-{{ $env->id }}"') ->toContain('wire:key="env-show-value-input-{{ $env->id }}"'); }); + +it('uses sans font for the developer bulk environment variable editor', function () { + $view = file_get_contents(resource_path('views/livewire/project/shared/environment-variable/all.blade.php')); + + expect($view) + ->toContain('class="whitespace-pre-wrap font-sans"') + ->not->toContain('wire:model="variables" monospace') + ->not->toContain('wire:model="variablesPreview" monospace'); +}); diff --git a/tests/Feature/PublicGitRepositoryBranchParsingTest.php b/tests/Feature/PublicGitRepositoryBranchParsingTest.php new file mode 100644 index 000000000..38f757b2e --- /dev/null +++ b/tests/Feature/PublicGitRepositoryBranchParsingTest.php @@ -0,0 +1,71 @@ +getSegment(3) === 'tree') { + $path = str($parsed->getPath())->trim('/'); + $branch = str($path)->after('tree/')->value(); + $baseDirectory = '/'; + } + + return [ + 'branch' => $branch, + 'base_directory' => $baseDirectory, + 'repository' => $parsed->getSegment(1).'/'.$parsed->getSegment(2), + ]; +} + +test('parses simple branch from GitHub URL', function () { + $result = parseBranchFromUrl('https://github.com/andrasbacsai/coolify-examples/tree/main'); + + expect($result['branch'])->toBe('main'); + expect($result['base_directory'])->toBe('/'); + expect($result['repository'])->toBe('andrasbacsai/coolify-examples'); +}); + +test('parses branch with slash from GitHub URL', function () { + $result = parseBranchFromUrl('https://github.com/andrasbacsai/coolify-examples-1/tree/fix/8854-env-var-fallback-volume'); + + expect($result['branch'])->toBe('fix/8854-env-var-fallback-volume'); + expect($result['base_directory'])->toBe('/'); + expect($result['repository'])->toBe('andrasbacsai/coolify-examples-1'); +}); + +test('parses branch with multiple slashes from GitHub URL', function () { + $result = parseBranchFromUrl('https://github.com/user/repo/tree/feature/team/new-widget'); + + expect($result['branch'])->toBe('feature/team/new-widget'); + expect($result['base_directory'])->toBe('/'); +}); + +test('defaults to main branch when no tree segment in URL', function () { + $result = parseBranchFromUrl('https://github.com/andrasbacsai/coolify-examples'); + + expect($result['branch'])->toBe('main'); + expect($result['base_directory'])->toBe('/'); +}); + +test('parses version-style branch with slash from GitHub URL', function () { + $result = parseBranchFromUrl('https://github.com/coollabsio/coolify-examples/tree/release/v2.0'); + + expect($result['branch'])->toBe('release/v2.0'); + expect($result['base_directory'])->toBe('/'); +}); + +test('parses branch from non-GitHub URL with tree segment', function () { + $result = parseBranchFromUrl('https://gitlab.com/user/repo/tree/hotfix/critical-bug'); + + expect($result['branch'])->toBe('hotfix/critical-bug'); + expect($result['base_directory'])->toBe('/'); +});