mirror of
https://github.com/coollabsio/coolify.git
synced 2026-10-06 05:58:14 -04:00
fix(service): save switches without saving pending Compose edits (#11985)
This commit is contained in:
@@ -408,6 +408,89 @@ function addTraefikDockerNetworkLabel(Collection $labels, string $network): Coll
|
||||
return $labels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove one-time fields from long-form volume entries without reformatting the rest of the source.
|
||||
* Fall back to a YAML dump when the source uses a form that the line edit cannot handle safely.
|
||||
*
|
||||
* @param array<string, mixed> $cleanedYaml
|
||||
* @param array<int, string> $fields
|
||||
*/
|
||||
function removeComposeVolumeFieldsPreservingComments(string $source, array $cleanedYaml, array $fields): string
|
||||
{
|
||||
$context = [];
|
||||
$removeIndent = null;
|
||||
$blockIndent = null;
|
||||
$result = [];
|
||||
|
||||
foreach (preg_split('/(?<=\n)/', $source) as $line) {
|
||||
$text = rtrim($line, "\r\n");
|
||||
$indent = strspn($text, ' ');
|
||||
|
||||
if ($removeIndent !== null) {
|
||||
if (trim($text) === '' || $indent > $removeIndent) {
|
||||
continue;
|
||||
}
|
||||
$removeIndent = null;
|
||||
}
|
||||
|
||||
if ($blockIndent !== null) {
|
||||
if (trim($text) === '' || $indent > $blockIndent) {
|
||||
$result[] = $line;
|
||||
|
||||
continue;
|
||||
}
|
||||
$blockIndent = null;
|
||||
}
|
||||
|
||||
if (trim($text) === '' || str_starts_with(ltrim($text), '#')) {
|
||||
$result[] = $line;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
while ($context && end($context)['indent'] >= $indent) {
|
||||
array_pop($context);
|
||||
}
|
||||
|
||||
$body = substr($text, $indent);
|
||||
$isListItem = preg_match('/^-\s+/', $body) === 1;
|
||||
if ($isListItem) {
|
||||
$context[] = ['indent' => $indent, 'key' => '[]'];
|
||||
$body = preg_replace('/^-\s+/', '', $body);
|
||||
}
|
||||
|
||||
if (preg_match('/^([\w.-]+|"[^"]+"|\x27[^\x27]+\x27)\s*:(.*)$/', $body, $matches)) {
|
||||
$key = trim($matches[1], "\"'");
|
||||
$path = array_column($context, 'key');
|
||||
if (! $isListItem && count($path) === 4 && $path[0] === 'services' && $path[2] === 'volumes' && $path[3] === '[]' && in_array($key, $fields, true)) {
|
||||
$removeIndent = $indent;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = trim($matches[2]);
|
||||
if ($value === '' || str_starts_with($value, '#')) {
|
||||
$context[] = ['indent' => $indent, 'key' => $key];
|
||||
} elseif (preg_match('/^[|>][+-]?(?:\s+#.*)?$/', $value)) {
|
||||
$blockIndent = $indent;
|
||||
}
|
||||
}
|
||||
|
||||
$result[] = $line;
|
||||
}
|
||||
|
||||
$candidate = implode('', $result);
|
||||
try {
|
||||
if (Yaml::parse($candidate) === $cleanedYaml) {
|
||||
return $candidate;
|
||||
}
|
||||
} catch (Exception) {
|
||||
// Use the validated parsed result if the line edit is not valid YAML.
|
||||
}
|
||||
|
||||
return Yaml::dump($cleanedYaml, 10, 2);
|
||||
}
|
||||
|
||||
function applicationParser(Application $resource, int $pull_request_id = 0, ?int $preview_id = null, ?string $commit = null): Collection
|
||||
{
|
||||
$uuid = data_get($resource, 'uuid');
|
||||
@@ -1558,6 +1641,7 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
|
||||
// Parse the original compose again to create a clean version without Coolify additions
|
||||
try {
|
||||
$originalYaml = Yaml::parse($originalCompose);
|
||||
$originalYamlBeforeCleanup = $originalYaml;
|
||||
// Remove content, isDirectory, and is_directory from all volume definitions
|
||||
if (isset($originalYaml['services'])) {
|
||||
foreach ($originalYaml['services'] as $serviceName => &$service) {
|
||||
@@ -1572,7 +1656,9 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
|
||||
}
|
||||
}
|
||||
}
|
||||
$resource->docker_compose_raw = Yaml::dump($originalYaml, 10, 2);
|
||||
if ($originalYaml !== $originalYamlBeforeCleanup) {
|
||||
$resource->docker_compose_raw = removeComposeVolumeFieldsPreservingComments($originalCompose, $originalYaml, ['content', 'isDirectory', 'is_directory']);
|
||||
}
|
||||
} catch (Exception) {
|
||||
// If parsing fails, keep the original docker_compose_raw unchanged
|
||||
}
|
||||
@@ -2797,6 +2883,7 @@ function serviceParser(Service $resource): Collection
|
||||
// Parse the original compose again to create a clean version without Coolify additions
|
||||
try {
|
||||
$originalYaml = Yaml::parse($originalCompose);
|
||||
$originalYamlBeforeCleanup = $originalYaml;
|
||||
// Remove content, isDirectory, and is_directory from all volume definitions
|
||||
if (isset($originalYaml['services'])) {
|
||||
foreach ($originalYaml['services'] as $serviceName => &$service) {
|
||||
@@ -2811,7 +2898,9 @@ function serviceParser(Service $resource): Collection
|
||||
}
|
||||
}
|
||||
}
|
||||
$resource->docker_compose_raw = Yaml::dump($originalYaml, 10, 2);
|
||||
if ($originalYaml !== $originalYamlBeforeCleanup) {
|
||||
$resource->docker_compose_raw = removeComposeVolumeFieldsPreservingComments($originalCompose, $originalYaml, ['content', 'isDirectory', 'is_directory']);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// If parsing fails, keep the original docker_compose_raw unchanged
|
||||
}
|
||||
|
||||
@@ -1617,7 +1617,7 @@ function sanitizeLogsForExport(string $text): string
|
||||
return remove_iip($text);
|
||||
}
|
||||
|
||||
function getTopLevelNetworks(Service|Application $resource)
|
||||
function getTopLevelNetworks(Service|Application $resource): Collection
|
||||
{
|
||||
if ($resource->getMorphClass() === Service::class) {
|
||||
if ($resource->docker_compose_raw) {
|
||||
@@ -1743,6 +1743,8 @@ function getTopLevelNetworks(Service|Application $resource)
|
||||
|
||||
return $topLevelNetworks->keys();
|
||||
}
|
||||
|
||||
return collect();
|
||||
}
|
||||
function sourceIsLocal(Stringable $source)
|
||||
{
|
||||
@@ -3312,8 +3314,11 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
'configs' => $topLevelConfigs->toArray(),
|
||||
'secrets' => $topLevelSecrets->toArray(),
|
||||
];
|
||||
$originalYaml = $yaml;
|
||||
$yaml = data_forget($yaml, 'services.*.volumes.*.content');
|
||||
$resource->docker_compose_raw = Yaml::dump($yaml, 10, 2);
|
||||
if ($yaml !== $originalYaml) {
|
||||
$resource->docker_compose_raw = removeComposeVolumeFieldsPreservingComments($resource->docker_compose_raw, $yaml, ['content']);
|
||||
}
|
||||
$resource->docker_compose = Yaml::dump($finalServices, 10, 2);
|
||||
|
||||
$resource->save();
|
||||
@@ -4088,7 +4093,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
'configs' => $topLevelConfigs->toArray(),
|
||||
'secrets' => $topLevelSecrets->toArray(),
|
||||
];
|
||||
$resource->docker_compose_raw = Yaml::dump($yaml, 10, 2);
|
||||
$resource->docker_compose = Yaml::dump($finalServices, 10, 2);
|
||||
data_forget($resource, 'environment_variables');
|
||||
data_forget($resource, 'environment_variables_preview');
|
||||
|
||||
Reference in New Issue
Block a user