fix(storage): keep long-form bind sources with variable or vault references

Docker Compose resolves these values from the .env file, so the parser no
longer rejects or misresolves them. Also copy the expected parser fixtures
when parser-snapshot-diff compares with a git ref.
This commit is contained in:
Andras Bacsai
2026-10-20 13:47:41 +02:00
parent 2ade696375
commit 4e55c58a28
3 changed files with 18 additions and 0 deletions
+6
View File
@@ -9,6 +9,7 @@ use App\Models\LocalPersistentVolume;
use App\Models\Service;
use App\Models\ServiceApplication;
use App\Models\ServiceDatabase;
use App\Support\RemoteSecretReferences;
use App\Support\ValidationPatterns;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\File;
@@ -255,6 +256,7 @@ function resolveComposeVolumeSourceDefault(string $source): string
/**
* Resolves a long-syntax bind source as Docker Compose does with the `.env` file that Coolify writes:
* `${VAR:-default}` is the value of VAR when the resource sets it to a non-empty value, else the default.
* A value with a variable or vault reference stays for Docker Compose to resolve.
*
* @throws Exception If the value of the variable is not a shell-safe path
*/
@@ -273,6 +275,10 @@ function resolveComposeLongBindSource(string $source, Application|Service $resou
if (blank($value)) {
return resolveComposeVolumeSourceDefault($source);
}
// Docker Compose expands variable references in the `.env` file, and Coolify writes vault secrets to it.
if (str_contains($value, '$') || RemoteSecretReferences::containsReference($value)) {
return $source;
}
try {
validateShellSafePath($value, 'volume source');
+1
View File
@@ -71,6 +71,7 @@ case "$mode" in
cp -r "$repo/templates/compose" "$base/templates/compose"
mkdir -p "$base/tests/Fixtures/parser"
cp "$repo"/tests/Fixtures/parser/*.yaml "$base/tests/Fixtures/parser/"
cp -r "$repo/tests/Fixtures/parser/expected" "$base/tests/Fixtures/parser/expected"
cp "$repo/tests/Feature/ServiceParserReferenceTest.php" "$base/tests/Feature/"
parse_into "$base" "$work/base-expected" "$mode"
;;
@@ -224,3 +224,14 @@ it('uses the value that the resource sets for a ${VAR:-default} long-form bind s
it('rejects an unsafe value for a ${VAR:-default} long-form bind source', function (string $kind) {
parseLongBind($kind, longBindCompose("'\${AO_CONFIG_FILE:-./agent-orchestrator.yaml}'", LONG_BIND_READ_ONLY_OPTIONS), ['AO_CONFIG_FILE' => '/opt/x;touch /tmp/pwned']);
})->with(['service', 'application'])->throws(Exception::class, 'the value of AO_CONFIG_FILE is not a safe path');
it('keeps a ${VAR:-default} long-form bind source for Docker Compose when the value of the variable is not a plain path', function (string $kind, string $value) {
$source = '${AO_CONFIG_FILE:-./agent-orchestrator.yaml}';
$result = parseLongBind($kind, longBindCompose("'{$source}'", LONG_BIND_READ_ONLY_OPTIONS), ['BASE_DIR' => '/srv', 'AO_CONFIG_FILE' => $value]);
expect($result['volume']['source'])->toBe($source);
})->with(['service', 'application'])->with([
'braced variable reference' => ['${BASE_DIR}/ao.yaml'],
'variable reference' => ['$BASE_DIR/ao.yaml'],
'vault reference' => ['{{vault.AO_CONFIG_FILE}}'],
]);