fix(parsers): enhance volume string handling by preserving mode in application and service parsers. Update related unit tests for validation.

This commit is contained in:
Andras Bacsai
2025-08-27 16:54:44 +02:00
parent 115a03947f
commit cde528bf5e
3 changed files with 54 additions and 1 deletions

View File

@@ -551,8 +551,14 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
if ($type->value() === 'bind') { if ($type->value() === 'bind') {
if ($source->value() === '/var/run/docker.sock') { if ($source->value() === '/var/run/docker.sock') {
$volume = $source->value().':'.$target->value(); $volume = $source->value().':'.$target->value();
if (isset($parsed['mode']) && $parsed['mode']) {
$volume .= ':'.$parsed['mode']->value();
}
} elseif ($source->value() === '/tmp' || $source->value() === '/tmp/') { } elseif ($source->value() === '/tmp' || $source->value() === '/tmp/') {
$volume = $source->value().':'.$target->value(); $volume = $source->value().':'.$target->value();
if (isset($parsed['mode']) && $parsed['mode']) {
$volume .= ':'.$parsed['mode']->value();
}
} else { } else {
if ((int) $resource->compose_parsing_version >= 4) { if ((int) $resource->compose_parsing_version >= 4) {
$mainDirectory = str(base_configuration_dir().'/applications/'.$uuid); $mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
@@ -586,6 +592,9 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
} }
} }
$volume = "$source:$target"; $volume = "$source:$target";
if (isset($parsed['mode']) && $parsed['mode']) {
$volume .= ':'.$parsed['mode']->value();
}
} }
} elseif ($type->value() === 'volume') { } elseif ($type->value() === 'volume') {
if ($topLevel->get('volumes')->has($source->value())) { if ($topLevel->get('volumes')->has($source->value())) {
@@ -609,6 +618,9 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
$target = $parsed['target']; $target = $parsed['target'];
$source = $name; $source = $name;
$volume = "$source:$target"; $volume = "$source:$target";
if (isset($parsed['mode']) && $parsed['mode']) {
$volume .= ':'.$parsed['mode']->value();
}
} elseif (is_array($volume)) { } elseif (is_array($volume)) {
data_set($volume, 'source', $name); data_set($volume, 'source', $name);
} }
@@ -1562,8 +1574,14 @@ function serviceParser(Service $resource): Collection
if ($type->value() === 'bind') { if ($type->value() === 'bind') {
if ($source->value() === '/var/run/docker.sock') { if ($source->value() === '/var/run/docker.sock') {
$volume = $source->value().':'.$target->value(); $volume = $source->value().':'.$target->value();
if (isset($parsed['mode']) && $parsed['mode']) {
$volume .= ':'.$parsed['mode']->value();
}
} elseif ($source->value() === '/tmp' || $source->value() === '/tmp/') { } elseif ($source->value() === '/tmp' || $source->value() === '/tmp/') {
$volume = $source->value().':'.$target->value(); $volume = $source->value().':'.$target->value();
if (isset($parsed['mode']) && $parsed['mode']) {
$volume .= ':'.$parsed['mode']->value();
}
} else { } else {
if ((int) $resource->compose_parsing_version >= 4) { if ((int) $resource->compose_parsing_version >= 4) {
$mainDirectory = str(base_configuration_dir().'/services/'.$uuid); $mainDirectory = str(base_configuration_dir().'/services/'.$uuid);
@@ -1594,6 +1612,9 @@ function serviceParser(Service $resource): Collection
} }
} }
$volume = "$source:$target"; $volume = "$source:$target";
if (isset($parsed['mode']) && $parsed['mode']) {
$volume .= ':'.$parsed['mode']->value();
}
} }
} elseif ($type->value() === 'volume') { } elseif ($type->value() === 'volume') {
if ($topLevel->get('volumes')->has($source->value())) { if ($topLevel->get('volumes')->has($source->value())) {
@@ -1614,6 +1635,9 @@ function serviceParser(Service $resource): Collection
$target = $parsed['target']; $target = $parsed['target'];
$source = $name; $source = $name;
$volume = "$source:$target"; $volume = "$source:$target";
if (isset($parsed['mode']) && $parsed['mode']) {
$volume .= ':'.$parsed['mode']->value();
}
} elseif (is_array($volume)) { } elseif (is_array($volume)) {
data_set($volume, 'source', $name); data_set($volume, 'source', $name);
} }

View File

@@ -6,7 +6,8 @@
:resource="$resource" :isFirst="$loop->first" isReadOnly='true' isService='true' /> :resource="$resource" :isFirst="$loop->first" isReadOnly='true' isService='true' />
@else @else
<livewire:project.shared.storages.show wire:key="storage-{{ $storage->id }}" :storage="$storage" <livewire:project.shared.storages.show wire:key="storage-{{ $storage->id }}" :storage="$storage"
:resource="$resource" isReadOnly="{{ data_get($storage, 'is_readonly') }}" :resource="$resource"
isReadOnly="{{ data_get($storage, 'is_readonly') || $resource?->build_pack === 'dockercompose' ? true : false }}"
startedAt="{{ data_get($resource, 'started_at') }}" /> startedAt="{{ data_get($resource, 'started_at') }}" />
@endif @endif
@endforeach @endforeach

View File

@@ -191,3 +191,31 @@ test('parses complex real-world examples', function () {
expect($result['target']->value())->toBe('/var/lib/app-data'); expect($result['target']->value())->toBe('/var/lib/app-data');
expect($result['mode'])->toBeNull(); expect($result['mode'])->toBeNull();
}); });
test('preserves mode when reconstructing volume strings', function () {
// Test cases that specifically verify mode preservation
$testCases = [
'/var/run/docker.sock:/var/run/docker.sock:ro' => ['source' => '/var/run/docker.sock', 'target' => '/var/run/docker.sock', 'mode' => 'ro'],
'/etc/localtime:/etc/localtime:ro' => ['source' => '/etc/localtime', 'target' => '/etc/localtime', 'mode' => 'ro'],
'/tmp:/tmp:rw' => ['source' => '/tmp', 'target' => '/tmp', 'mode' => 'rw'],
'gitea-data:/data:ro' => ['source' => 'gitea-data', 'target' => '/data', 'mode' => 'ro'],
'./config:/app/config:cached' => ['source' => './config', 'target' => '/app/config', 'mode' => 'cached'],
'volume:/data:delegated' => ['source' => 'volume', 'target' => '/data', 'mode' => 'delegated'],
];
foreach ($testCases as $input => $expected) {
$result = parseDockerVolumeString($input);
// Verify parsing
expect($result['source']->value())->toBe($expected['source']);
expect($result['target']->value())->toBe($expected['target']);
expect($result['mode']->value())->toBe($expected['mode']);
// Verify reconstruction would preserve the mode
$reconstructed = $result['source']->value().':'.$result['target']->value();
if ($result['mode']) {
$reconstructed .= ':'.$result['mode']->value();
}
expect($reconstructed)->toBe($input);
}
});