From 650351e2dad706ce20e79bbf17689657a4f5a934 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Sat, 3 Oct 2026 19:22:36 +0200 Subject: [PATCH 1/3] fix(application): show the default file location when the field is empty or cleared --- app/Livewire/Project/Application/General.php | 2 +- .../project/application/general.blade.php | 6 +++-- ...ub-private-repository-deploy-key.blade.php | 2 +- .../new/github-private-repository.blade.php | 2 +- .../new/gitlab-private-repository.blade.php | 2 +- .../new/public-git-repository.blade.php | 2 +- ...pplicationGeneralBuildpackSelectorTest.php | 20 ++++++++++++++++ .../Browser/ApplicationConfigurationTest.php | 21 ++++++++++++++++ .../NewApplicationRepositoryPathsTest.php | 24 +++++++++++++++++++ 9 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 tests/v4/Browser/NewApplicationRepositoryPathsTest.php diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php index 531aa834c0..0f0f68e9b4 100644 --- a/app/Livewire/Project/Application/General.php +++ b/app/Livewire/Project/Application/General.php @@ -436,7 +436,7 @@ class General extends Component $this->portsMappings = $this->application->ports_mappings; $this->customNetworkAliases = $this->application->custom_network_aliases; $this->dockerfile = $this->application->dockerfile; - $this->dockerfileLocation = $this->application->dockerfile_location; + $this->dockerfileLocation = $this->application->dockerfile_location ?? ($this->buildPack === 'dockerfile' ? '/Dockerfile' : null); $this->dockerfileTargetBuild = $this->application->dockerfile_target_build; $this->dockerRegistryImageName = $this->application->docker_registry_image_name; $this->dockerRegistryImageTag = $this->application->docker_registry_image_tag; diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php index 1df2bedfb2..e0ec7b5add 100644 --- a/resources/views/livewire/project/application/general.blade.php +++ b/resources/views/livewire/project/application/general.blade.php @@ -159,7 +159,8 @@ this.baseDir = this.normalizePath(this.baseDir); }, normalizeComposeLocation() { - this.composeLocation = this.normalizePath(this.composeLocation); + const path = this.normalizePath(this.composeLocation); + this.composeLocation = path === '/' ? '/docker-compose.yaml' : path; } }" class="grid gap-4 lg:grid-cols-2"> + @blur="composeLocation = normalize(composeLocation).replace(/^\/$/, '/docker-compose.yaml')" />

Resolved file: + @blur="composeLocation = normalize(composeLocation).replace(/^\/$/, '/docker-compose.yaml')" />

Resolved file: + @blur="composeLocation = normalize(composeLocation).replace(/^\/$/, '/docker-compose.yaml')" />

Resolved file: + @blur="composeLocation = normalize(composeLocation).replace(/^\/$/, '/docker-compose.yaml')" />

Resolved file: toBe('https://example.com,https://www.example.com'); }); +test('Dockerfile location defaults to the Dockerfile only for the Dockerfile build pack', function (string $buildPack, ?string $expected) { + $application = Application::factory()->create([ + 'environment_id' => $this->environment->id, + 'destination_id' => $this->destination->id, + 'destination_type' => StandaloneDocker::class, + 'build_pack' => $buildPack, + 'static_image' => 'nginx:alpine', + 'base_directory' => '/', + 'is_http_basic_auth_enabled' => false, + 'redirect' => 'no', + ]); + + Livewire::test(General::class, ['application' => $application]) + ->assertSuccessful() + ->assertSet('dockerfileLocation', $expected); +})->with([ + 'Dockerfile' => ['dockerfile', '/Dockerfile'], + 'Nixpacks' => ['nixpacks', null], +]); + test('networking section hints that internal ports can be set per domain', function () { $application = Application::factory()->create([ 'environment_id' => $this->environment->id, diff --git a/tests/v4/Browser/ApplicationConfigurationTest.php b/tests/v4/Browser/ApplicationConfigurationTest.php index cb60e12949..f7778b501b 100644 --- a/tests/v4/Browser/ApplicationConfigurationTest.php +++ b/tests/v4/Browser/ApplicationConfigurationTest.php @@ -156,6 +156,27 @@ it('saves custom docker run options from the UI', function () { ->screenshot(filename: 'application-docker-run-options'); }); +it('restores the default file location when the field is cleared', function (string $buildPack, string $column, string $default) { + $this->application->update(['build_pack' => $buildPack, $column => '/docker/custom']); + + loginAndSkipBoarding(); + + $page = visit(applicationConfigurationUrl( + $this->stack['project'], + $this->stack['environment'], + $this->application + )); + + $page->clear("input[placeholder='{$default}']") + ->keys("input[placeholder='{$default}']", 'Tab') + ->assertValue("input[placeholder='{$default}']", $default) + ->assertNoJavaScriptErrors() + ->screenshot(filename: "application-{$buildPack}-location-cleared"); +})->with([ + 'Dockerfile' => ['dockerfile', 'dockerfile_location', '/Dockerfile'], + 'Docker Compose' => ['dockercompose', 'docker_compose_location', '/docker-compose.yaml'], +]); + it('opens environment variables page for the application', function () { loginAndSkipBoarding(); diff --git a/tests/v4/Browser/NewApplicationRepositoryPathsTest.php b/tests/v4/Browser/NewApplicationRepositoryPathsTest.php new file mode 100644 index 0000000000..f7970fd02a --- /dev/null +++ b/tests/v4/Browser/NewApplicationRepositoryPathsTest.php @@ -0,0 +1,24 @@ +uuid}/environment/{$stack['environment']->uuid}/new?type=public&destination={$stack['destination']->uuid}"); + + $page->fill('repository_url', 'https://gitlab.com/example/monorepo.git') + ->click('Check repository') + ->assertSee('Build configuration'); + + selectListboxOption($page, 'build_pack', 'Docker Compose'); + + $page->clear("input[placeholder='/docker-compose.yaml']") + ->keys("input[placeholder='/docker-compose.yaml']", 'Tab') + ->assertValue("input[placeholder='/docker-compose.yaml']", '/docker-compose.yaml') + ->assertNoJavaScriptErrors() + ->screenshot(filename: 'new-application-compose-file-cleared'); +}); From 6f75c601c378601f5a69f3e46c0d79b191b0af1b Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Sat, 3 Oct 2026 19:22:36 +0200 Subject: [PATCH 2/3] feat(application): show the full file path inline and warn on a repeated base directory --- .../forms/repository-paths.blade.php | 47 +++++++++++++ .../project/application/general.blade.php | 70 ++----------------- ...ub-private-repository-deploy-key.blade.php | 25 +------ .../new/github-private-repository.blade.php | 25 +------ .../new/gitlab-private-repository.blade.php | 25 +------ .../new/public-git-repository.blade.php | 25 +------ .../Browser/ApplicationConfigurationTest.php | 32 +++++++++ .../NewApplicationRepositoryPathsTest.php | 31 ++++++++ 8 files changed, 129 insertions(+), 151 deletions(-) create mode 100644 resources/views/components/forms/repository-paths.blade.php diff --git a/resources/views/components/forms/repository-paths.blade.php b/resources/views/components/forms/repository-paths.blade.php new file mode 100644 index 0000000000..1b5c6f94f9 --- /dev/null +++ b/resources/views/components/forms/repository-paths.blade.php @@ -0,0 +1,47 @@ +@props([ + 'base', + 'file' => null, + 'fileLabel' => null, + 'defaultFile' => null, + 'disabled' => 'false', +]) + +

class('grid gap-4') }} x-data="{ + base: $wire.entangle('{{ $base }}'), + file: {{ $file ? "\$wire.entangle('{$file}')" : 'null' }}, + normalize(path) { + path = (path ?? '').trim().replace(/\/+$/, ''); + return path === '' || path.startsWith('/') ? path : '/' + path; + }, + get directory() { + return this.normalize(this.base); + }, + get location() { + return this.normalize(this.file) || '{{ $defaultFile }}'; + }, + get repeatsBase() { + return this.directory !== '' && this.location.startsWith(this.directory + '/'); + }, + get suggestion() { + return this.location.slice(this.directory.length); + }, +}"> + + @if ($file) + +

+ Resolved file: + +

+ + {{ $fileLabel }} is relative to the base directory, so it should not start with + again. + + + @endif + {{ $slot }} +
diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php index e0ec7b5add..650a348d8a 100644 --- a/resources/views/livewire/project/application/general.blade.php +++ b/resources/views/livewire/project/application/general.blade.php @@ -143,36 +143,9 @@
@if ($buildPack === 'dockercompose')
-
- - -
+
@else -
- - @if ($buildPack === 'dockerfile' && !$application->dockerfile) - - @endif - + @if ($buildPack === 'dockerfile') @endif @endif - -
+ @if ($this->application->is_github_based() && !$this->application->is_public_repository())
@if ($build_pack === 'dockercompose') -
- - -

- Resolved file: - -

-
+ @else diff --git a/resources/views/livewire/project/new/github-private-repository.blade.php b/resources/views/livewire/project/new/github-private-repository.blade.php index 22be530c95..80aeb0ee33 100644 --- a/resources/views/livewire/project/new/github-private-repository.blade.php +++ b/resources/views/livewire/project/new/github-private-repository.blade.php @@ -135,28 +135,9 @@
@if ($build_pack === 'dockercompose') -
- - -

- Resolved file: - -

-
+ @else diff --git a/resources/views/livewire/project/new/gitlab-private-repository.blade.php b/resources/views/livewire/project/new/gitlab-private-repository.blade.php index 4060fff309..608b543d9c 100644 --- a/resources/views/livewire/project/new/gitlab-private-repository.blade.php +++ b/resources/views/livewire/project/new/gitlab-private-repository.blade.php @@ -133,28 +133,9 @@
@if ($build_pack === 'dockercompose') -
- - -

- Resolved file: - -

-
+ @else diff --git a/resources/views/livewire/project/new/public-git-repository.blade.php b/resources/views/livewire/project/new/public-git-repository.blade.php index 7e120b8539..6e5995ba5d 100644 --- a/resources/views/livewire/project/new/public-git-repository.blade.php +++ b/resources/views/livewire/project/new/public-git-repository.blade.php @@ -84,28 +84,9 @@
@if ($build_pack === 'dockercompose') -
- - -

- Resolved file: - -

-
+ @else diff --git a/tests/v4/Browser/ApplicationConfigurationTest.php b/tests/v4/Browser/ApplicationConfigurationTest.php index f7778b501b..7d65f22ae8 100644 --- a/tests/v4/Browser/ApplicationConfigurationTest.php +++ b/tests/v4/Browser/ApplicationConfigurationTest.php @@ -156,6 +156,38 @@ it('saves custom docker run options from the UI', function () { ->screenshot(filename: 'application-docker-run-options'); }); +it('warns when the Dockerfile location repeats the base directory and applies the suggested fix', function () { + $this->application->update([ + 'build_pack' => 'dockerfile', + 'base_directory' => '/apps/api', + 'dockerfile_location' => '/apps/api/Dockerfile', + ]); + + loginAndSkipBoarding(); + + $page = visit(applicationConfigurationUrl( + $this->stack['project'], + $this->stack['environment'], + $this->application + )); + + $page->assertSee('Base directory is repeated') + ->assertSee('/apps/api/apps/api/Dockerfile') + ->screenshot(filename: 'application-dockerfile-location-repeats-base-directory') + ->click('Use /Dockerfile') + ->assertDontSee('Base directory is repeated') + ->assertSee('/apps/api/Dockerfile'); + + submitLivewireForm($page); + + $page->assertNoJavaScriptErrors() + ->screenshot(filename: 'application-dockerfile-location-fixed'); + + $this->application->refresh(); + expect($this->application->base_directory)->toBe('/apps/api') + ->and($this->application->dockerfile_location)->toBe('/Dockerfile'); +}); + it('restores the default file location when the field is cleared', function (string $buildPack, string $column, string $default) { $this->application->update(['build_pack' => $buildPack, $column => '/docker/custom']); diff --git a/tests/v4/Browser/NewApplicationRepositoryPathsTest.php b/tests/v4/Browser/NewApplicationRepositoryPathsTest.php index f7970fd02a..7571eb06a6 100644 --- a/tests/v4/Browser/NewApplicationRepositoryPathsTest.php +++ b/tests/v4/Browser/NewApplicationRepositoryPathsTest.php @@ -1,5 +1,6 @@ assertNoJavaScriptErrors() ->screenshot(filename: 'new-application-compose-file-cleared'); }); + +it('warns when the compose file repeats the base directory and creates the application with the suggested fix', function () { + $stack = seedBrowserResourceStack(); + loginAndSkipBoarding(); + + $page = visit("/project/{$stack['project']->uuid}/environment/{$stack['environment']->uuid}/new?type=public&destination={$stack['destination']->uuid}"); + + $page->fill('repository_url', 'https://gitlab.com/example/monorepo.git') + ->click('Check repository') + ->assertSee('Build configuration'); + + selectListboxOption($page, 'build_pack', 'Docker Compose'); + + $page->fill("input[placeholder='/']", 'apps/api/') + ->fill("input[placeholder='/docker-compose.yaml']", 'apps/api/compose.yaml') + ->assertSee('Base directory is repeated') + ->assertSee('/apps/api/apps/api/compose.yaml') + ->screenshot(filename: 'new-application-compose-file-repeats-base-directory') + ->click('button:has-text("Use /compose.yaml")') + ->assertDontSee('Base directory is repeated') + ->assertSee('/apps/api/compose.yaml') + ->assertNoJavaScriptErrors() + ->screenshot(filename: 'new-application-compose-file-fixed'); + + submitLivewireForm($page); + + $application = Application::query()->where('git_repository', 'https://gitlab.com/example/monorepo.git')->sole(); + expect($application->base_directory)->toBe('/apps/api') + ->and($application->docker_compose_location)->toBe('/compose.yaml'); +}); From b634d1061a40d3914848b9dc495448eaeb6ee7c4 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Sat, 3 Oct 2026 19:57:26 +0200 Subject: [PATCH 3/3] fix(ui): place the Docker build stage target next to the builder selection --- .../livewire/project/application/general.blade.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php index 650a348d8a..3638f4d2b1 100644 --- a/resources/views/livewire/project/application/general.blade.php +++ b/resources/views/livewire/project/application/general.blade.php @@ -191,11 +191,6 @@ - @if ($buildPack === 'dockerfile') - - @endif @if ($application->could_set_build_commands()) @if ($application->settings->is_static) + @if ($buildPack === 'dockerfile') + + @endif