From 6cbb52f1bb2dc3371b05a789eb1c7493925361d3 Mon Sep 17 00:00:00 2001 From: Michael Johnson Date: Sat, 14 Nov 2026 15:34:10 -0500 Subject: [PATCH] fix: keep ${VAR:-default} whole when splitting compose volumes and ports --- dbuild/config.py | 29 +++++++++++++++++++++++++++-- tests/test_config.py | 10 ++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/dbuild/config.py b/dbuild/config.py index 72e6848..02950bf 100644 --- a/dbuild/config.py +++ b/dbuild/config.py @@ -34,6 +34,31 @@ _GLOBAL_CONFIG_PATH: Path | None = None + +def _split_compose(entry: str) -> list[str]: + """Split a short-form compose volume or port on ``:``, but not inside + ``${...}``: ``${DIR:-/srv/x}:/x:ro`` is source ``${DIR:-/srv/x}``, + target ``/x``, options ``ro``.""" + parts, cur, depth = [], "", 0 + i = 0 + while i < len(entry): + if entry.startswith("${", i): + depth += 1 + cur += "${" + i += 2 + continue + c = entry[i] + if c == "}" and depth: + depth -= 1 + if c == ":" and not depth: + parts.append(cur) + cur = "" + else: + cur += c + i += 1 + parts.append(cur) + return parts + def _confident_prefix() -> Path | None: """Return the install prefix only when it can be determined reliably. @@ -636,7 +661,7 @@ def _parse_service_data( # Volumes for v in service.get("volumes", []): if isinstance(v, str): - parts = v.split(":") + parts = _split_compose(v) src = parts[0] tgt = parts[1] if len(parts) > 1 else parts[0] opts = parts[2] if len(parts) > 2 else "" @@ -649,7 +674,7 @@ def _parse_service_data( # Ports for p in service.get("ports", []): if isinstance(p, str): - parts = p.split(":") + parts = _split_compose(p) pub = parts[0] tgt = parts[1] if len(parts) > 1 else parts[0] proto = "tcp" diff --git a/tests/test_config.py b/tests/test_config.py index 8c8632d..df72a5c 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -688,3 +688,13 @@ def test_local_architectures_override_global(self, _mock_org): if __name__ == "__main__": unittest.main() + + +def test_split_compose_keeps_env_defaults_whole(): + from dbuild.config import _split_compose + + assert _split_compose("${RP_PODMAN_DIR:-/path/to/containers/reverse-proxy/podman}:/var/run/podman:ro") == [ + "${RP_PODMAN_DIR:-/path/to/containers/reverse-proxy/podman}", "/var/run/podman", "ro"] + assert _split_compose("/a:/b:ro") == ["/a", "/b", "ro"] + assert _split_compose("${P:-80}:80") == ["${P:-80}", "80"] + assert _split_compose("127.0.0.1:53:53/udp") == ["127.0.0.1", "53", "53/udp"]