From 98ce96e2f86d54b223786fb0251ba1ef1274e1d6 Mon Sep 17 00:00:00 2001 From: Sanjay Santhanam <51058514+Sanjays2402@users.noreply.github.com> Date: Sat, 25 Jul 2026 21:28:47 -0700 Subject: [PATCH 1/3] Split string command and entrypoint after interpolation PR #1504 removed the shlex.split() of string `command`/`entrypoint` from normalize_service because it ran before variable interpolation and broke expressions such as ${VAR:?error message}. Nothing re-applied the split afterwards, so `podman-compose config` regressed to emitting `command: sleep infinity` as a plain string instead of the array docker-compose produces. normalize_service_final runs after rec_subs, so the split is applied there. container_to_args already handled both forms, so only the config output was affected. Signed-off-by: Sanjay Santhanam <51058514+Sanjays2402@users.noreply.github.com> --- podman_compose.py | 3 +++ tests/unit/test_normalize_final_build.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/podman_compose.py b/podman_compose.py index 3a90cc0a..d559b14a 100755 --- a/podman_compose.py +++ b/podman_compose.py @@ -2186,6 +2186,9 @@ def normalize_service_final(service: dict[str, Any], project_dir: str) -> dict[s if not isinstance(service["build"], dict): service["build"] = {} service["build"]["context"] = context + for key in ("command", "entrypoint"): + if isinstance(service.get(key), str): + service[key] = shlex.split(service[key]) return service diff --git a/tests/unit/test_normalize_final_build.py b/tests/unit/test_normalize_final_build.py index e93a78db..e56ab1f4 100644 --- a/tests/unit/test_normalize_final_build.py +++ b/tests/unit/test_normalize_final_build.py @@ -104,6 +104,22 @@ def test_normalize_service_final_returns_absolute_path_in_context(self, input, e project_dir = cwd self.assertEqual(normalize_service_final(input, project_dir), expected) + cases_command_normalization = [ + ({"image": "busybox", "command": "sleep infinity"}, ["sleep", "infinity"], None), + ({"image": "busybox", "command": ["sleep", "infinity"]}, ["sleep", "infinity"], None), + ({"image": "busybox", "entrypoint": "/bin/sh -c"}, None, ["/bin/sh", "-c"]), + ] + + @parameterized.expand(cases_command_normalization) + def test_normalize_service_final_splits_string_command( + self, input, expected_command, expected_entrypoint + ): + # Tests that string [service.command] and [service.entrypoint] are split into + # lists after variable interpolation + actual = normalize_service_final(input, cwd) + self.assertEqual(actual.get("command"), expected_command) + self.assertEqual(actual.get("entrypoint"), expected_entrypoint) + @parameterized.expand(cases_simple_normalization) def test_normalize_returns_absolute_path_in_context(self, input, expected): project_dir = cwd From 1ea4d1d95590a07a32c78e7fc0efa94679035880 Mon Sep 17 00:00:00 2001 From: Sanjay Santhanam <51058514+Sanjays2402@users.noreply.github.com> Date: Wed, 5 Aug 2026 07:42:07 -0700 Subject: [PATCH 2/3] tests: inline command normalization cases into expand() --- tests/unit/test_normalize_final_build.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/unit/test_normalize_final_build.py b/tests/unit/test_normalize_final_build.py index e56ab1f4..5ad4a902 100644 --- a/tests/unit/test_normalize_final_build.py +++ b/tests/unit/test_normalize_final_build.py @@ -104,13 +104,11 @@ def test_normalize_service_final_returns_absolute_path_in_context(self, input, e project_dir = cwd self.assertEqual(normalize_service_final(input, project_dir), expected) - cases_command_normalization = [ + @parameterized.expand([ ({"image": "busybox", "command": "sleep infinity"}, ["sleep", "infinity"], None), ({"image": "busybox", "command": ["sleep", "infinity"]}, ["sleep", "infinity"], None), ({"image": "busybox", "entrypoint": "/bin/sh -c"}, None, ["/bin/sh", "-c"]), - ] - - @parameterized.expand(cases_command_normalization) + ]) def test_normalize_service_final_splits_string_command( self, input, expected_command, expected_entrypoint ): From d6141ff8b2f0e38f4c36b18d6b92ca54e07b3a03 Mon Sep 17 00:00:00 2001 From: Sanjay Santhanam <51058514+Sanjays2402@users.noreply.github.com> Date: Fri, 14 Aug 2026 05:36:39 -0700 Subject: [PATCH 3/3] Update config expectations for split commands Signed-off-by: Sanjay Santhanam <51058514+Sanjays2402@users.noreply.github.com> --- ...command_entrypoint_after_interpolation.bugfix | 1 + .../test_podman_compose_compose_file_from_env.py | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 newsfragments/split_command_entrypoint_after_interpolation.bugfix diff --git a/newsfragments/split_command_entrypoint_after_interpolation.bugfix b/newsfragments/split_command_entrypoint_after_interpolation.bugfix new file mode 100644 index 00000000..9befd360 --- /dev/null +++ b/newsfragments/split_command_entrypoint_after_interpolation.bugfix @@ -0,0 +1 @@ +Split string command and entrypoint values into argument lists after interpolation, matching Compose semantics. diff --git a/tests/integration/compose_file_from_env/test_podman_compose_compose_file_from_env.py b/tests/integration/compose_file_from_env/test_podman_compose_compose_file_from_env.py index f6c99dc3..9de07d74 100644 --- a/tests/integration/compose_file_from_env/test_podman_compose_compose_file_from_env.py +++ b/tests/integration/compose_file_from_env/test_podman_compose_compose_file_from_env.py @@ -25,7 +25,9 @@ def test_compose_file_from_dotenv(self) -> None: out.decode("utf-8"), 'services:\n' ' dotenv-service:\n' - ' command: echo "from-dotenv"\n' + ' command:\n' + ' - echo\n' + ' - from-dotenv\n' ' image: nopush/podman-compose-test\n' '\n', ) @@ -47,7 +49,9 @@ def test_compose_file_from_explicit_env_file(self) -> None: out.decode("utf-8"), 'services:\n' ' explicit-env-service:\n' - ' command: echo "from-explicit-env"\n' + ' command:\n' + ' - echo\n' + ' - from-explicit-env\n' ' image: nopush/podman-compose-test\n' '\n', ) @@ -70,7 +74,9 @@ def test_explicit_f_overrides_compose_file_from_dotenv(self) -> None: out.decode("utf-8"), 'services:\n' ' explicit-service:\n' - ' command: echo "explicit"\n' + ' command:\n' + ' - echo\n' + ' - explicit\n' ' image: nopush/podman-compose-test\n' '\n', ) @@ -88,7 +94,9 @@ def test_env_var_compose_file_takes_precedence_over_dotenv(self) -> None: out.decode("utf-8"), 'services:\n' ' var-service:\n' - ' command: echo "var"\n' + ' command:\n' + ' - echo\n' + ' - var\n' ' image: nopush/podman-compose-test\n' '\n', )