mirror of
https://github.com/tiennm99/coolify.git
synced 2026-04-17 17:21:04 +00:00
fix: preserve empty strings and remove empty sections in docker-compose
- Preserve empty string environment variables instead of converting to null Empty strings and null have different semantics in Docker Compose: * Empty string (VAR: ""): Variable is set to "" in container (e.g., HTTP_PROXY="" means "no proxy") * Null (VAR: null): Variable is unset/removed from container environment - Remove empty top-level sections (volumes, configs, secrets) from generated compose files These sections now only appear when they contain actual content, following Docker Compose best practices - Add safety check for missing volumes in validateComposeFile to prevent iteration errors - Add comprehensive unit tests for both fixes Fixes #7126
This commit is contained in:
@@ -1073,6 +1073,9 @@ function validateComposeFile(string $compose, int $server_id): string|Throwable
|
||||
}
|
||||
$yaml_compose = Yaml::parse($compose);
|
||||
foreach ($yaml_compose['services'] as $service_name => $service) {
|
||||
if (! isset($service['volumes'])) {
|
||||
continue;
|
||||
}
|
||||
foreach ($service['volumes'] as $volume_name => $volume) {
|
||||
if (data_get($volume, 'type') === 'bind' && data_get($volume, 'content')) {
|
||||
unset($yaml_compose['services'][$service_name]['volumes'][$volume_name]['content']);
|
||||
|
||||
@@ -1164,13 +1164,17 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
|
||||
$environment = $environment->filter(function ($value, $key) {
|
||||
return ! str($key)->startsWith('SERVICE_FQDN_');
|
||||
})->map(function ($value, $key) use ($resource) {
|
||||
// if value is empty, set it to null so if you set the environment variable in the .env file (Coolify's UI), it will used
|
||||
// Preserve empty strings; only override if database value exists and is non-empty
|
||||
// This is important because empty strings and null have different semantics in Docker:
|
||||
// - Empty string: Variable is set to "" (e.g., HTTP_PROXY="" means "no proxy")
|
||||
// - Null: Variable is unset/removed from container environment
|
||||
if (str($value)->isEmpty()) {
|
||||
if ($resource->environment_variables()->where('key', $key)->exists()) {
|
||||
$value = $resource->environment_variables()->where('key', $key)->first()->value;
|
||||
} else {
|
||||
$value = null;
|
||||
$dbEnv = $resource->environment_variables()->where('key', $key)->first();
|
||||
// Only use database override if it exists AND has a non-empty value
|
||||
if ($dbEnv && str($dbEnv->value)->isNotEmpty()) {
|
||||
$value = $dbEnv->value;
|
||||
}
|
||||
// Keep empty string as-is (don't convert to null)
|
||||
}
|
||||
|
||||
return $value;
|
||||
@@ -1299,6 +1303,18 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
|
||||
return array_search($key, $customOrder);
|
||||
});
|
||||
|
||||
// Remove empty top-level sections (volumes, networks, configs, secrets)
|
||||
// Keep only non-empty sections to match Docker Compose best practices
|
||||
$topLevel = $topLevel->filter(function ($value, $key) {
|
||||
// Always keep 'services' section
|
||||
if ($key === 'services') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Keep section only if it has content
|
||||
return $value instanceof Collection ? $value->isNotEmpty() : ! empty($value);
|
||||
});
|
||||
|
||||
$cleanedCompose = Yaml::dump(convertToArray($topLevel), 10, 2);
|
||||
$resource->docker_compose = $cleanedCompose;
|
||||
|
||||
@@ -2122,13 +2138,17 @@ function serviceParser(Service $resource): Collection
|
||||
$environment = $environment->filter(function ($value, $key) {
|
||||
return ! str($key)->startsWith('SERVICE_FQDN_');
|
||||
})->map(function ($value, $key) use ($resource) {
|
||||
// if value is empty, set it to null so if you set the environment variable in the .env file (Coolify's UI), it will used
|
||||
// Preserve empty strings; only override if database value exists and is non-empty
|
||||
// This is important because empty strings and null have different semantics in Docker:
|
||||
// - Empty string: Variable is set to "" (e.g., HTTP_PROXY="" means "no proxy")
|
||||
// - Null: Variable is unset/removed from container environment
|
||||
if (str($value)->isEmpty()) {
|
||||
if ($resource->environment_variables()->where('key', $key)->exists()) {
|
||||
$value = $resource->environment_variables()->where('key', $key)->first()->value;
|
||||
} else {
|
||||
$value = null;
|
||||
$dbEnv = $resource->environment_variables()->where('key', $key)->first();
|
||||
// Only use database override if it exists AND has a non-empty value
|
||||
if ($dbEnv && str($dbEnv->value)->isNotEmpty()) {
|
||||
$value = $dbEnv->value;
|
||||
}
|
||||
// Keep empty string as-is (don't convert to null)
|
||||
}
|
||||
|
||||
return $value;
|
||||
@@ -2251,6 +2271,18 @@ function serviceParser(Service $resource): Collection
|
||||
return array_search($key, $customOrder);
|
||||
});
|
||||
|
||||
// Remove empty top-level sections (volumes, networks, configs, secrets)
|
||||
// Keep only non-empty sections to match Docker Compose best practices
|
||||
$topLevel = $topLevel->filter(function ($value, $key) {
|
||||
// Always keep 'services' section
|
||||
if ($key === 'services') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Keep section only if it has content
|
||||
return $value instanceof Collection ? $value->isNotEmpty() : ! empty($value);
|
||||
});
|
||||
|
||||
$cleanedCompose = Yaml::dump(convertToArray($topLevel), 10, 2);
|
||||
$resource->docker_compose = $cleanedCompose;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user