fix(environment): keep Compose self-references editable

This commit is contained in:
Andras Bacsai
2026-08-15 10:59:04 +02:00
parent 096a1f5f08
commit ec8a24d178
2 changed files with 72 additions and 1 deletions
@@ -714,6 +714,12 @@ class All extends Component
// Extract all hard-coded variables
$hardcodedVars = extractHardcodedEnvironmentVariables($dockerComposeRaw);
// Compose self-references are inputs supplied through Coolify's .env file,
// not hard-coded values. Keep them editable in the environment variables UI.
$hardcodedVars = $hardcodedVars->reject(
fn (array $variable): bool => $this->isSelfReferencingComposeVariable($variable)
);
// Filter out magic variables (SERVICE_FQDN_*, SERVICE_URL_*, SERVICE_NAME_*)
$hardcodedVars = $hardcodedVars->filter(function ($var) {
$key = $var['key'];
@@ -755,7 +761,14 @@ class All extends Component
return [];
}
return extractHardcodedEnvironmentVariables($dockerComposeRaw)
$assignments = extractHardcodedEnvironmentVariables($dockerComposeRaw);
$editableKeys = $assignments
->filter(fn (array $variable): bool => $this->isSelfReferencingComposeVariable($variable))
->pluck('key')
->unique();
return $assignments
->reject(fn (array $variable): bool => $editableKeys->contains($variable['key']))
->pluck('key')
->reject(fn (string $key): bool => str($key)->startsWith(['SERVICE_FQDN_', 'SERVICE_URL_', 'SERVICE_NAME_']))
->unique()
@@ -763,6 +776,28 @@ class All extends Component
->all();
}
private function isSelfReferencingComposeVariable(array $variable): bool
{
$value = $variable['value'] ?? null;
if (! is_string($value)) {
return false;
}
if ($value === '$'.$variable['key']) {
return true;
}
$reference = extractBalancedBraceContent($value);
if ($reference === null || $reference['start'] !== 1 || $reference['end'] !== strlen($value) - 1) {
return false;
}
$splitReference = splitOnOperatorOutsideNested($reference['content']);
$referencedKey = $splitReference['variable'] ?? $reference['content'];
return $referencedKey === $variable['key'];
}
public function getDevView()
{
$this->variables = $this->formatEnvironmentVariables($this->getEnvironmentVariables(false, false));
@@ -182,6 +182,42 @@ YAML,
->toBe(['API_TOKEN']);
});
it('keeps Compose self-referencing environment variables editable without changing Compose', function () {
$dockerCompose = <<<'YAML'
services:
app:
image: nginx
environment:
API_TOKEN: ${API_TOKEN}
LOG_LEVEL: ${LOG_LEVEL:-info}
FIXED_VALUE: production
YAML;
$service = Service::factory()->create([
'environment_id' => $this->environment->id,
'docker_compose_raw' => $dockerCompose,
]);
foreach (['API_TOKEN', 'LOG_LEVEL'] as $key) {
EnvironmentVariable::create([
'key' => $key,
'resourceable_type' => Service::class,
'resourceable_id' => $service->id,
]);
}
$rows = Livewire::test(All::class, ['resource' => $service])
->call('loadEnvironmentVariables')
->instance()
->environmentVariablePageRows;
expect($rows->where('kind', 'managed')->pluck('environmentVariable.key')->all())
->toBe(['API_TOKEN', 'LOG_LEVEL'])
->and($rows->where('kind', 'hardcoded')->pluck('environmentVariable.key')->all())
->toBe(['FIXED_VALUE'])
->and($service->fresh()->docker_compose_raw)->toBe($dockerCompose);
});
it('shows a Compose-defined value as read-only when a managed variable has the same key', function () {
$service = Service::factory()->create([
'environment_id' => $this->environment->id,