From d5395f05008a9c0dafb5eaab3c24970340a50691 Mon Sep 17 00:00:00 2001
From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com>
Date: Thu, 2 Jul 2026 17:16:40 +0200
Subject: [PATCH 1/2] fix(services): preserve template keys for selection
---
app/Livewire/Project/New/Select.php | 12 ++++++--
.../livewire/project/new/select.blade.php | 10 +++----
.../ServiceTemplatesLastUpdatedHintTest.php | 29 +++++++++++++++++++
3 files changed, 43 insertions(+), 8 deletions(-)
diff --git a/app/Livewire/Project/New/Select.php b/app/Livewire/Project/New/Select.php
index cff886f98..34601f5dd 100644
--- a/app/Livewire/Project/New/Select.php
+++ b/app/Livewire/Project/New/Select.php
@@ -112,14 +112,17 @@ public function loadServices()
$default_logo = 'images/default.webp';
$logo = data_get($service, 'logo', $default_logo);
$local_logo_path = public_path($logo);
+ $serviceKey = (string) $key;
return [
- 'name' => str($key)->headline(),
+ 'id' => $serviceKey,
+ 'name' => str($serviceKey)->headline(),
+ 'docsSlug' => str($serviceKey)->lower()->value(),
'logo' => asset($logo),
'logo_github_url' => file_exists($local_logo_path)
? 'https://raw.githubusercontent.com/coollabsio/coolify/refs/heads/main/public/'.$logo
: asset($default_logo),
- 'templateLastUpdated' => $templateLastUpdatedMap[(string) $key] ?? null,
+ 'templateLastUpdated' => $templateLastUpdatedMap[$serviceKey] ?? null,
] + (array) $service;
})->all();
@@ -336,7 +339,10 @@ private function formatLastModified(string $path): ?string
public function setType(string $type)
{
- $type = str($type)->lower()->slug()->value();
+ if (! str($type)->startsWith('one-click-service-')) {
+ $type = str($type)->lower()->slug()->value();
+ }
+
if ($this->loading) {
return;
}
diff --git a/resources/views/livewire/project/new/select.blade.php b/resources/views/livewire/project/new/select.blade.php
index debe3326f..600433dcd 100644
--- a/resources/views/livewire/project/new/select.blade.php
+++ b/resources/views/livewire/project/new/select.blade.php
@@ -154,7 +154,7 @@ class="text-xs text-neutral-500 dark:text-neutral-400">
-
@@ -214,7 +214,7 @@ class="px-2 py-0.5 text-xs rounded-full bg-amber-100 text-amber-800 dark:bg-ambe
- assertSee('serviceTemplatesLastUpdated');
$view->assertSee('service.templateLastUpdated');
});
+
+it('keeps service template keys for service selection and docs links', function () {
+ $services = collect((new Select)->loadServices()['services']);
+ $denoKv = $services->firstWhere('id', 'denoKV');
+
+ expect($denoKv)
+ ->not->toBeNull()
+ ->and($denoKv['docsSlug'])->toBe('denokv');
+
+ View::share('errors', new ViewErrorBag);
+
+ $view = $this->view('livewire.project.new.select', [
+ 'current_step' => 'type',
+ 'environments' => collect(),
+ ]);
+
+ $view->assertSee("setType('one-click-service-' + service.id)", false);
+ $view->assertSee('service.docsSlug || this.extractBaseServiceName(service.name)', false);
+});
+
+it('preserves one click service key casing when selecting a service template', function () {
+ $component = new Select;
+ $component->servers = collect();
+ $component->allServers = collect();
+
+ $component->setType('one-click-service-denoKV');
+
+ expect($component->type)->toBe('one-click-service-denoKV');
+});
From 954c1e369c60184cf8f342a948c9ff0edf2ce314 Mon Sep 17 00:00:00 2001
From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com>
Date: Thu, 2 Jul 2026 17:39:16 +0200
Subject: [PATCH 2/2] fix(env-vars): avoid service preview variable lookups
(#10837)
---
.../Shared/EnvironmentVariable/All.php | 21 +++++++-
.../Feature/EnvironmentVariableSearchTest.php | 54 +++++++++++++------
2 files changed, 56 insertions(+), 19 deletions(-)
diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/All.php b/app/Livewire/Project/Shared/EnvironmentVariable/All.php
index 3a5145023..b45d9aba3 100644
--- a/app/Livewire/Project/Shared/EnvironmentVariable/All.php
+++ b/app/Livewire/Project/Shared/EnvironmentVariable/All.php
@@ -93,6 +93,10 @@ public function getEnvironmentVariablesPreviewProperty()
private function getEnvironmentVariables(bool $isPreview, bool $withSearch = true): Collection
{
+ if ($isPreview && ! $this->supportsPreviewEnvironmentVariables()) {
+ return collect();
+ }
+
$query = $isPreview
? $this->resource->environment_variables_preview()
: $this->resource->environment_variables();
@@ -119,12 +123,21 @@ private function searchTerm(): string
return trim($this->search);
}
+ private function supportsPreviewEnvironmentVariables(): bool
+ {
+ return $this->showPreview && $this->resource instanceof Application;
+ }
+
public function getHasEnvironmentVariablesProperty(): bool
{
- return $this->environmentVariables->isNotEmpty() ||
+ $hasPreviewEnvironmentVariables = $this->supportsPreviewEnvironmentVariables() && (
$this->environmentVariablesPreview->isNotEmpty() ||
+ $this->hardcodedEnvironmentVariablesPreview->isNotEmpty()
+ );
+
+ return $this->environmentVariables->isNotEmpty() ||
$this->hardcodedEnvironmentVariables->isNotEmpty() ||
- $this->hardcodedEnvironmentVariablesPreview->isNotEmpty();
+ $hasPreviewEnvironmentVariables;
}
private function nullLockedValues($envs)
@@ -158,6 +171,10 @@ public function getHardcodedEnvironmentVariablesPreviewProperty()
protected function getHardcodedVariables(bool $isPreview)
{
+ if ($isPreview && ! $this->supportsPreviewEnvironmentVariables()) {
+ return collect([]);
+ }
+
// Only for services and docker-compose applications
if ($this->resource->type() !== 'service' &&
($this->resourceClass !== 'App\Models\Application' ||
diff --git a/tests/Feature/EnvironmentVariableSearchTest.php b/tests/Feature/EnvironmentVariableSearchTest.php
index 613144d53..7d139ebee 100644
--- a/tests/Feature/EnvironmentVariableSearchTest.php
+++ b/tests/Feature/EnvironmentVariableSearchTest.php
@@ -56,7 +56,7 @@
->toBe(['API_KEY']);
});
-it('treats production environment variable search wildcards literally', function () {
+it('treats production environment variable search underscore wildcards literally', function () {
$application = Application::factory()->create([
'environment_id' => $this->environment->id,
]);
@@ -75,23 +75,11 @@
'resourceable_id' => $application->id,
]);
- EnvironmentVariable::create([
- 'key' => 'PERCENT%KEY',
- 'value' => 'percent-secret',
- 'resourceable_type' => Application::class,
- 'resourceable_id' => $application->id,
- ]);
-
$component = Livewire::test(All::class, ['resource' => $application])
->set('search', 'api_key');
expect($component->instance()->environmentVariables->pluck('key')->all())
->toBe(['API_KEY']);
-
- $component->set('search', '%KEY');
-
- expect($component->instance()->environmentVariables->pluck('key')->all())
- ->toBe(['PERCENT%KEY']);
});
it('filters preview environment variables by key case-insensitively', function () {
@@ -142,6 +130,34 @@
->toBe(['API_TOKEN']);
});
+it('searches service environment variables without requiring preview variables', function () {
+ $service = Service::factory()->create([
+ 'environment_id' => $this->environment->id,
+ ]);
+
+ EnvironmentVariable::create([
+ 'key' => 'API_KEY',
+ 'value' => 'secret',
+ 'resourceable_type' => Service::class,
+ 'resourceable_id' => $service->id,
+ ]);
+
+ EnvironmentVariable::create([
+ 'key' => 'DATABASE_URL',
+ 'value' => 'postgres://example',
+ 'resourceable_type' => Service::class,
+ 'resourceable_id' => $service->id,
+ ]);
+
+ $component = Livewire::test(All::class, ['resource' => $service])
+ ->set('search', 'api')
+ ->assertSee('Production Environment Variables')
+ ->assertDontSee('Preview Deployments Environment Variables');
+
+ expect($component->instance()->environmentVariables->pluck('key')->all())
+ ->toBe(['API_KEY']);
+});
+
it('does not show the empty production message when search only matches hardcoded variables', function () {
$service = Service::factory()->create([
'environment_id' => $this->environment->id,
@@ -155,11 +171,13 @@
YAML,
]);
- Livewire::test(All::class, ['resource' => $service])
+ $component = Livewire::test(All::class, ['resource' => $service])
->set('search', 'api')
->assertSee('Production Environment Variables')
- ->assertSee('API_TOKEN')
->assertDontSee('No environment variables found.');
+
+ expect($component->instance()->hardcodedEnvironmentVariables->pluck('key')->all())
+ ->toBe(['API_TOKEN']);
});
it('keeps developer view unfiltered after searching', function () {
@@ -242,10 +260,12 @@
'resourceable_id' => $application->id,
]);
- Livewire::test(All::class, ['resource' => $application])
+ $component = Livewire::test(All::class, ['resource' => $application])
->set('search', 'api')
->assertSee('Production Environment Variables')
- ->assertSee('API_KEY')
->assertDontSee('Preview Deployments Environment Variables')
->assertDontSee('PREVIEW_TOKEN');
+
+ expect($component->instance()->environmentVariables->pluck('key')->all())
+ ->toBe(['API_KEY']);
});