From d67fcd1dff7fee7bee21bf54e49f7d03f574d431 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Tue, 25 Nov 2025 11:23:18 +0100 Subject: [PATCH] feat: add magic variable detection and update UI behavior accordingly --- .../Shared/EnvironmentVariable/Show.php | 6 + .../environment-variable/show.blade.php | 130 +++++++++------- .../EnvironmentVariableMagicVariableTest.php | 141 ++++++++++++++++++ 3 files changed, 227 insertions(+), 50 deletions(-) create mode 100644 tests/Unit/EnvironmentVariableMagicVariableTest.php diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php index 75149a0d4..2a18be13c 100644 --- a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php +++ b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php @@ -24,6 +24,8 @@ class Show extends Component public bool $isLocked = false; + public bool $isMagicVariable = false; + public bool $isSharedVariable = false; public string $type; @@ -146,9 +148,13 @@ public function syncData(bool $toModel = false) public function checkEnvs() { $this->isDisabled = false; + $this->isMagicVariable = false; + if (str($this->env->key)->startsWith('SERVICE_FQDN') || str($this->env->key)->startsWith('SERVICE_URL') || str($this->env->key)->startsWith('SERVICE_NAME')) { $this->isDisabled = true; + $this->isMagicVariable = true; } + if ($this->env->is_shown_once) { $this->isLocked = true; } 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 cc95939de..86faeeeb4 100644 --- a/resources/views/livewire/project/shared/environment-variable/show.blade.php +++ b/resources/views/livewire/project/shared/environment-variable/show.blade.php @@ -40,10 +40,12 @@ - - + @if (!$isMagicVariable) + + + @endif @else @if ($is_shared) @else @if ($isSharedVariable) - + @if (!$isMagicVariable) + + @endif @else @if (!$env->is_nixpacks) - @if (!$env->is_nixpacks) - - @if ($is_multiline === false) - + @if (!$isMagicVariable) + @if (!$env->is_nixpacks) + + @if ($is_multiline === false) + + @endif @endif @endif @endif @@ -86,10 +92,12 @@ - - + @if (!$isMagicVariable) + + + @endif @else @if ($is_shared) @else @if ($isSharedVariable) - + @if (!$isMagicVariable) + + @endif @else - - @if ($is_multiline === false) - + @if (!$isMagicVariable) + + @if ($is_multiline === false) + + @endif @endif @endif @endif @@ -133,8 +145,10 @@ @endif - + @if (!$isMagicVariable) + + @endif @else
@@ -164,8 +178,10 @@ @endif
- + @if (!$isMagicVariable) + + @endif @endcan @can('update', $this->env) @@ -179,10 +195,12 @@ - - + @if (!$isMagicVariable) + + + @endif @else @if ($is_shared) @else @if ($isSharedVariable) - + @if (!$isMagicVariable) + + @endif @else @if (!$env->is_nixpacks) - @if (!$env->is_nixpacks) - - @if ($is_multiline === false) - + @if (!$isMagicVariable) + @if (!$env->is_nixpacks) + + @if ($is_multiline === false) + + @endif @endif @endif @endif @@ -214,8 +236,9 @@ @endif -
- @if ($isDisabled) + @if (!$isMagicVariable) +
+ @if ($isDisabled) Update Lock - @endif -
+ @endif +
+ @endif @else
@@ -247,10 +271,12 @@ - - + @if (!$isMagicVariable) + + + @endif @else @if ($is_shared) @else @if ($isSharedVariable) - + @if (!$isMagicVariable) + + @endif @else - - @if ($is_multiline === false) - + @if (!$isMagicVariable) + + @if ($is_multiline === false) + + @endif @endif @endif @endif diff --git a/tests/Unit/EnvironmentVariableMagicVariableTest.php b/tests/Unit/EnvironmentVariableMagicVariableTest.php new file mode 100644 index 000000000..ae85ba45f --- /dev/null +++ b/tests/Unit/EnvironmentVariableMagicVariableTest.php @@ -0,0 +1,141 @@ +shouldReceive('getAttribute') + ->with('key') + ->andReturn('SERVICE_FQDN_DB'); + $mock->shouldReceive('getAttribute') + ->with('is_shown_once') + ->andReturn(false); + $mock->shouldReceive('getMorphClass') + ->andReturn(EnvironmentVariable::class); + + $component = new Show; + $component->env = $mock; + $component->checkEnvs(); + + expect($component->isMagicVariable)->toBeTrue(); + expect($component->isDisabled)->toBeTrue(); +}); + +test('SERVICE_URL variables are identified as magic variables', function () { + $mock = Mockery::mock(EnvironmentVariable::class); + $mock->shouldReceive('getAttribute') + ->with('key') + ->andReturn('SERVICE_URL_API'); + $mock->shouldReceive('getAttribute') + ->with('is_shown_once') + ->andReturn(false); + $mock->shouldReceive('getMorphClass') + ->andReturn(EnvironmentVariable::class); + + $component = new Show; + $component->env = $mock; + $component->checkEnvs(); + + expect($component->isMagicVariable)->toBeTrue(); + expect($component->isDisabled)->toBeTrue(); +}); + +test('SERVICE_NAME variables are identified as magic variables', function () { + $mock = Mockery::mock(EnvironmentVariable::class); + $mock->shouldReceive('getAttribute') + ->with('key') + ->andReturn('SERVICE_NAME'); + $mock->shouldReceive('getAttribute') + ->with('is_shown_once') + ->andReturn(false); + $mock->shouldReceive('getMorphClass') + ->andReturn(EnvironmentVariable::class); + + $component = new Show; + $component->env = $mock; + $component->checkEnvs(); + + expect($component->isMagicVariable)->toBeTrue(); + expect($component->isDisabled)->toBeTrue(); +}); + +test('regular variables are not magic variables', function () { + $mock = Mockery::mock(EnvironmentVariable::class); + $mock->shouldReceive('getAttribute') + ->with('key') + ->andReturn('DATABASE_URL'); + $mock->shouldReceive('getAttribute') + ->with('is_shown_once') + ->andReturn(false); + $mock->shouldReceive('getMorphClass') + ->andReturn(EnvironmentVariable::class); + + $component = new Show; + $component->env = $mock; + $component->checkEnvs(); + + expect($component->isMagicVariable)->toBeFalse(); + expect($component->isDisabled)->toBeFalse(); +}); + +test('locked variables are not magic variables unless they start with SERVICE_', function () { + $mock = Mockery::mock(EnvironmentVariable::class); + $mock->shouldReceive('getAttribute') + ->with('key') + ->andReturn('SECRET_KEY'); + $mock->shouldReceive('getAttribute') + ->with('is_shown_once') + ->andReturn(true); + $mock->shouldReceive('getMorphClass') + ->andReturn(EnvironmentVariable::class); + + $component = new Show; + $component->env = $mock; + $component->checkEnvs(); + + expect($component->isMagicVariable)->toBeFalse(); + expect($component->isLocked)->toBeTrue(); +}); + +test('SERVICE_FQDN with port suffix is identified as magic variable', function () { + $mock = Mockery::mock(EnvironmentVariable::class); + $mock->shouldReceive('getAttribute') + ->with('key') + ->andReturn('SERVICE_FQDN_DB_5432'); + $mock->shouldReceive('getAttribute') + ->with('is_shown_once') + ->andReturn(false); + $mock->shouldReceive('getMorphClass') + ->andReturn(EnvironmentVariable::class); + + $component = new Show; + $component->env = $mock; + $component->checkEnvs(); + + expect($component->isMagicVariable)->toBeTrue(); + expect($component->isDisabled)->toBeTrue(); +}); + +test('SERVICE_URL with port suffix is identified as magic variable', function () { + $mock = Mockery::mock(EnvironmentVariable::class); + $mock->shouldReceive('getAttribute') + ->with('key') + ->andReturn('SERVICE_URL_API_8080'); + $mock->shouldReceive('getAttribute') + ->with('is_shown_once') + ->andReturn(false); + $mock->shouldReceive('getMorphClass') + ->andReturn(EnvironmentVariable::class); + + $component = new Show; + $component->env = $mock; + $component->checkEnvs(); + + expect($component->isMagicVariable)->toBeTrue(); + expect($component->isDisabled)->toBeTrue(); +});