mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
fix(env): keep quoted values when saving the developer view
Closes #10406
This commit is contained in:
@@ -1001,8 +1001,12 @@ class All extends Component
|
||||
if ($item->is_multiline) {
|
||||
return "$item->key=(Multiline environment variable, edit in normal view)";
|
||||
}
|
||||
$value = formatEnvValueForDevView($item->value);
|
||||
if ($value === null) {
|
||||
return "$item->key=(Value cannot be shown in this view, edit in normal view)";
|
||||
}
|
||||
|
||||
return "$item->key=$item->value";
|
||||
return "$item->key=$value";
|
||||
})->join("\n");
|
||||
}
|
||||
|
||||
@@ -1224,7 +1228,7 @@ class All extends Component
|
||||
$found = $this->resource->$method()->where('key', $key)->first();
|
||||
|
||||
if ($found) {
|
||||
if (! $found->is_shown_once && ! $found->is_multiline) {
|
||||
if (! $found->is_shown_once && ! $found->is_multiline && formatEnvValueForDevView($found->value) !== null) {
|
||||
$changed = false;
|
||||
|
||||
// Update value if it changed
|
||||
|
||||
@@ -93,8 +93,12 @@ class Show extends Component
|
||||
if ($item->is_multiline) {
|
||||
return "$item->key=(Multiline environment variable, edit in normal view)";
|
||||
}
|
||||
$value = formatEnvValueForDevView($item->value);
|
||||
if ($value === null) {
|
||||
return "$item->key=(Value cannot be shown in this view, edit in normal view)";
|
||||
}
|
||||
|
||||
return "$item->key=$item->value";
|
||||
return "$item->key=$value";
|
||||
})->join("\n");
|
||||
}
|
||||
|
||||
@@ -158,7 +162,7 @@ class Show extends Component
|
||||
$found = $this->environment->environment_variables()->where('key', $key)->first();
|
||||
|
||||
if ($found) {
|
||||
if (! $found->is_shown_once && ! $found->is_multiline) {
|
||||
if (! $found->is_shown_once && ! $found->is_multiline && formatEnvValueForDevView($found->value) !== null) {
|
||||
if ($found->value !== $value) {
|
||||
$found->value = $value;
|
||||
$found->save();
|
||||
|
||||
@@ -91,8 +91,12 @@ class Show extends Component
|
||||
if ($item->is_multiline) {
|
||||
return "$item->key=(Multiline environment variable, edit in normal view)";
|
||||
}
|
||||
$value = formatEnvValueForDevView($item->value);
|
||||
if ($value === null) {
|
||||
return "$item->key=(Value cannot be shown in this view, edit in normal view)";
|
||||
}
|
||||
|
||||
return "$item->key=$item->value";
|
||||
return "$item->key=$value";
|
||||
})->join("\n");
|
||||
}
|
||||
|
||||
@@ -150,7 +154,7 @@ class Show extends Component
|
||||
$found = $this->project->environment_variables()->where('key', $key)->first();
|
||||
|
||||
if ($found) {
|
||||
if (! $found->is_shown_once && ! $found->is_multiline) {
|
||||
if (! $found->is_shown_once && ! $found->is_multiline && formatEnvValueForDevView($found->value) !== null) {
|
||||
if ($found->value !== $value) {
|
||||
$found->value = $value;
|
||||
$found->save();
|
||||
|
||||
@@ -92,8 +92,12 @@ class Show extends Component
|
||||
if ($item->is_multiline) {
|
||||
return "$item->key=(Multiline environment variable, edit in normal view)";
|
||||
}
|
||||
$value = formatEnvValueForDevView($item->value);
|
||||
if ($value === null) {
|
||||
return "$item->key=(Value cannot be shown in this view, edit in normal view)";
|
||||
}
|
||||
|
||||
return "$item->key=$item->value";
|
||||
return "$item->key=$value";
|
||||
})->join("\n");
|
||||
}
|
||||
|
||||
@@ -162,7 +166,7 @@ class Show extends Component
|
||||
$found = $this->server->environment_variables()->where('key', $key)->first();
|
||||
|
||||
if ($found) {
|
||||
if (! $found->is_shown_once && ! $found->is_multiline) {
|
||||
if (! $found->is_shown_once && ! $found->is_multiline && formatEnvValueForDevView($found->value) !== null) {
|
||||
if ($found->value !== $value || $found->comment !== $comment) {
|
||||
$found->value = $value;
|
||||
$found->comment = $comment;
|
||||
|
||||
@@ -85,8 +85,12 @@ class Index extends Component
|
||||
if ($item->is_multiline) {
|
||||
return "$item->key=(Multiline environment variable, edit in normal view)";
|
||||
}
|
||||
$value = formatEnvValueForDevView($item->value);
|
||||
if ($value === null) {
|
||||
return "$item->key=(Value cannot be shown in this view, edit in normal view)";
|
||||
}
|
||||
|
||||
return "$item->key=$item->value";
|
||||
return "$item->key=$value";
|
||||
})->join("\n");
|
||||
}
|
||||
|
||||
@@ -149,7 +153,7 @@ class Index extends Component
|
||||
$found = $this->team->environment_variables()->where('key', $key)->first();
|
||||
|
||||
if ($found) {
|
||||
if (! $found->is_shown_once && ! $found->is_multiline) {
|
||||
if (! $found->is_shown_once && ! $found->is_multiline && formatEnvValueForDevView($found->value) !== null) {
|
||||
if ($found->value !== $value) {
|
||||
$found->value = $value;
|
||||
$found->save();
|
||||
|
||||
@@ -1258,6 +1258,32 @@ function parseEnvFormatToArray($env_file_contents)
|
||||
return $env_array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a value for a KEY=VALUE line of the developer view, so that parseEnvFormatToArray()
|
||||
* reads it back unchanged. Returns null when the format cannot hold the value: it has a line
|
||||
* break, or it needs quotes and has both quote types.
|
||||
*/
|
||||
function formatEnvValueForDevView(?string $value): ?string
|
||||
{
|
||||
$value ??= '';
|
||||
if (str_contains($value, "\n")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$needsQuotes = str_starts_with($value, '"') || str_starts_with($value, "'") || preg_match('/\s#/', $value) === 1;
|
||||
if (! $needsQuotes) {
|
||||
return $value;
|
||||
}
|
||||
if (! str_contains($value, "'")) {
|
||||
return "'{$value}'";
|
||||
}
|
||||
if (! str_contains($value, '"')) {
|
||||
return "\"{$value}\"";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract inline comments from environment variables in raw docker-compose YAML.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
use App\Livewire\Project\Shared\EnvironmentVariable\All;
|
||||
use App\Livewire\SharedVariables\Team\Index;
|
||||
use App\Models\Application;
|
||||
use App\Models\Environment;
|
||||
use App\Models\EnvironmentVariable;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Project;
|
||||
use App\Models\SharedEnvironmentVariable;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Livewire\Livewire;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
InstanceSettings::forceCreate(['id' => 0]);
|
||||
|
||||
$this->user = User::factory()->create();
|
||||
$this->team = Team::factory()->create();
|
||||
$this->team->members()->attach($this->user, ['role' => 'owner']);
|
||||
$this->project = Project::factory()->create(['team_id' => $this->team->id]);
|
||||
$this->environment = Environment::factory()->create(['project_id' => $this->project->id]);
|
||||
$this->application = Application::factory()->create(['environment_id' => $this->environment->id]);
|
||||
|
||||
$this->actingAs($this->user);
|
||||
session(['currentTeam' => $this->team]);
|
||||
});
|
||||
|
||||
$values = [
|
||||
'QUOTED' => '"abc"',
|
||||
'SINGLE_QUOTED' => "'\$FOO'",
|
||||
'HASH' => 'value #not-a-comment',
|
||||
'BOTH_QUOTES' => '\'"Company :: App" <noreply@example.com>\'',
|
||||
];
|
||||
|
||||
it('keeps values unchanged when the developer view is saved without edits', function () use ($values) {
|
||||
foreach ($values as $key => $value) {
|
||||
EnvironmentVariable::create([
|
||||
'key' => $key,
|
||||
'value' => $value,
|
||||
'is_literal' => true,
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
}
|
||||
|
||||
$component = Livewire::test(All::class, ['resource' => $this->application])
|
||||
->call('loadEnvironmentVariables')
|
||||
->call('switch');
|
||||
|
||||
$component->call('submit')->assertHasNoErrors();
|
||||
$component->call('submit')->assertHasNoErrors();
|
||||
|
||||
expect($this->application->environment_variables()->whereIn('key', array_keys($values))->get()->pluck('value', 'key')->all())->toBe($values)
|
||||
->and($this->application->environment_variables()->whereIn('key', array_keys($values))->whereNotNull('comment')->count())->toBe(0)
|
||||
->and($component->get('variables'))->toContain("QUOTED='\"abc\"'")
|
||||
->and($component->get('variables'))->toContain('BOTH_QUOTES=(Value cannot be shown in this view, edit in normal view)');
|
||||
});
|
||||
|
||||
it('keeps shared variable values unchanged when the developer view is saved without edits', function () use ($values) {
|
||||
foreach ($values as $key => $value) {
|
||||
SharedEnvironmentVariable::create([
|
||||
'key' => $key,
|
||||
'value' => $value,
|
||||
'type' => 'team',
|
||||
'team_id' => $this->team->id,
|
||||
]);
|
||||
}
|
||||
|
||||
$component = Livewire::test(Index::class)->call('switch');
|
||||
|
||||
$component->call('submit')->assertHasNoErrors();
|
||||
$component->call('submit')->assertHasNoErrors();
|
||||
|
||||
expect($this->team->environment_variables()->get()->pluck('value', 'key')->all())->toBe($values);
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
it('formats values so that parseEnvFormatToArray reads them back unchanged', function (string $value) {
|
||||
$formatted = formatEnvValueForDevView($value);
|
||||
|
||||
expect(parseEnvFormatToArray("KEY={$formatted}"))
|
||||
->toBe(['KEY' => ['value' => $value, 'comment' => null]]);
|
||||
})->with([
|
||||
'plain' => ['plain-value'],
|
||||
'empty' => [''],
|
||||
'double quoted' => ['"abc"'],
|
||||
'single quoted' => ["'\$FOO'"],
|
||||
'leading double quote only' => ['"abc'],
|
||||
'quotes inside' => ['He said "hi" and \'bye\''],
|
||||
'hash after whitespace' => ['value #not-a-comment'],
|
||||
'hash without whitespace' => ['value#hash'],
|
||||
'double quoted with suffix' => ['"Company :: App" <noreply@example.com>'],
|
||||
]);
|
||||
|
||||
it('returns null for values that the env format cannot hold', function (string $value) {
|
||||
expect(formatEnvValueForDevView($value))->toBeNull();
|
||||
})->with([
|
||||
'both quote types and a leading quote' => ['\'"Company :: App" <noreply@example.com>\''],
|
||||
'line break' => ["first\nsecond"],
|
||||
]);
|
||||
Reference in New Issue
Block a user