mirror of
https://github.com/coollabsio/coolify.git
synced 2026-09-26 01:10:30 -04:00
fix(environment-variables): preserve generated Compose variables
Exclude protected Compose variables from bulk deletion and developer views, and propagate deletion failures correctly.
This commit is contained in:
@@ -818,19 +818,21 @@ class All extends Component
|
||||
{
|
||||
$isMember = auth()->user()?->isMember();
|
||||
|
||||
return $variables->map(function ($item) use ($isMember) {
|
||||
if ($isMember) {
|
||||
return "$item->key=(Hidden, only admins can view)";
|
||||
}
|
||||
if ($item->is_shown_once) {
|
||||
return "$item->key=(Locked Secret, delete and add again to change)";
|
||||
}
|
||||
if ($item->is_multiline) {
|
||||
return "$item->key=(Multiline environment variable, edit in normal view)";
|
||||
}
|
||||
return $variables
|
||||
->reject(fn ($item): bool => $this->isProtectedEnvironmentVariable($item->key))
|
||||
->map(function ($item) use ($isMember) {
|
||||
if ($isMember) {
|
||||
return "$item->key=(Hidden, only admins can view)";
|
||||
}
|
||||
if ($item->is_shown_once) {
|
||||
return "$item->key=(Locked Secret, delete and add again to change)";
|
||||
}
|
||||
if ($item->is_multiline) {
|
||||
return "$item->key=(Multiline environment variable, edit in normal view)";
|
||||
}
|
||||
|
||||
return "$item->key=$item->value";
|
||||
})->join("\n");
|
||||
return "$item->key=$item->value";
|
||||
})->join("\n");
|
||||
}
|
||||
|
||||
public function switch()
|
||||
@@ -908,8 +910,7 @@ class All extends Component
|
||||
$deletedCount = $this->deleteRemovedVariables(false, $variables);
|
||||
if ($deletedCount > 0) {
|
||||
$changesMade = true;
|
||||
} elseif ($deletedCount === 0 && $this->resource->environment_variables()->whereNotIn('key', array_keys($variables))->exists()) {
|
||||
// If we tried to delete but couldn't (due to Docker Compose), mark as error
|
||||
} elseif ($deletedCount < 0) {
|
||||
$errorOccurred = true;
|
||||
}
|
||||
|
||||
@@ -926,8 +927,7 @@ class All extends Component
|
||||
$deletedPreviewCount = $this->deleteRemovedVariables(true, $previewVariables);
|
||||
if ($deletedPreviewCount > 0) {
|
||||
$changesMade = true;
|
||||
} elseif ($deletedPreviewCount === 0 && $this->resource->environment_variables_preview()->whereNotIn('key', array_keys($previewVariables))->exists()) {
|
||||
// If we tried to delete but couldn't (due to Docker Compose), mark as error
|
||||
} elseif ($deletedPreviewCount < 0) {
|
||||
$errorOccurred = true;
|
||||
}
|
||||
|
||||
@@ -988,6 +988,12 @@ class All extends Component
|
||||
// Get all environment variables that will be deleted
|
||||
$variablesToDelete = $this->resource->$method()->whereNotIn('key', array_keys($variables))->get();
|
||||
|
||||
// Generated Compose variables are managed by Coolify and must survive a bulk
|
||||
// replacement even when they are omitted from the pasted environment file.
|
||||
$variablesToDelete = $variablesToDelete->reject(
|
||||
fn (EnvironmentVariable $environmentVariable): bool => $this->isProtectedEnvironmentVariable($environmentVariable->key)
|
||||
);
|
||||
|
||||
// If there are no variables to delete, return 0
|
||||
if ($variablesToDelete->isEmpty()) {
|
||||
return 0;
|
||||
@@ -1001,13 +1007,13 @@ class All extends Component
|
||||
if ($isUsed) {
|
||||
$this->dispatch('error', "Cannot delete environment variable '{$envVar->key}' <br><br>Please remove it from the Docker Compose file first.");
|
||||
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, no variables are used in Docker Compose, so we can delete them
|
||||
$this->resource->$method()->whereNotIn('key', array_keys($variables))->delete();
|
||||
$this->resource->$method()->whereKey($variablesToDelete->modelKeys())->delete();
|
||||
|
||||
return $variablesToDelete->count();
|
||||
}
|
||||
|
||||
@@ -121,3 +121,69 @@ it('is idempotent when loadEnvironmentVariables is called twice', function () {
|
||||
expect($component->instance()->environmentVariables->pluck('key')->all())
|
||||
->toContain('API_KEY');
|
||||
});
|
||||
|
||||
it('preserves generated compose variables during bulk replacement', function () {
|
||||
$application = Application::factory()->create([
|
||||
'environment_id' => $this->environment->id,
|
||||
'build_pack' => 'dockercompose',
|
||||
'docker_compose' => <<<'YAML'
|
||||
services:
|
||||
api:
|
||||
image: nginx:alpine
|
||||
environment:
|
||||
SERVICE_URL_API: /api
|
||||
YAML,
|
||||
]);
|
||||
|
||||
foreach ([
|
||||
'SERVICE_URL_API' => 'https://api.example.com/api',
|
||||
'SERVICE_FQDN_API' => 'api.example.com/api',
|
||||
'OLD_VARIABLE' => 'remove-me',
|
||||
] as $key => $value) {
|
||||
EnvironmentVariable::create([
|
||||
'key' => $key,
|
||||
'value' => $value,
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $application->id,
|
||||
]);
|
||||
}
|
||||
|
||||
Livewire::test(All::class, ['resource' => $application])
|
||||
->set('variables', 'NEW_VARIABLE=keep-me')
|
||||
->call('submit')
|
||||
->assertDispatched('success')
|
||||
->assertNotDispatched('error');
|
||||
|
||||
expect($application->environment_variables()->pluck('value', 'key')->all())
|
||||
->toBe([
|
||||
'SERVICE_URL_API' => 'https://api.example.com/api',
|
||||
'SERVICE_FQDN_API' => 'api.example.com/api',
|
||||
'NEW_VARIABLE' => 'keep-me',
|
||||
]);
|
||||
});
|
||||
|
||||
it('hides generated compose variables from developer view', function () {
|
||||
$application = Application::factory()->create([
|
||||
'environment_id' => $this->environment->id,
|
||||
'build_pack' => 'dockercompose',
|
||||
]);
|
||||
|
||||
foreach ([
|
||||
'SERVICE_URL_API' => 'https://api.example.com',
|
||||
'SERVICE_FQDN_API' => 'api.example.com',
|
||||
'API_URL' => '$SERVICE_URL_API',
|
||||
] as $key => $value) {
|
||||
EnvironmentVariable::create([
|
||||
'key' => $key,
|
||||
'value' => $value,
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $application->id,
|
||||
]);
|
||||
}
|
||||
|
||||
$component = Livewire::test(All::class, ['resource' => $application])
|
||||
->call('switch');
|
||||
|
||||
expect($component->get('variables'))
|
||||
->toBe('API_URL=$SERVICE_URL_API');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user