From 256b51c8ee049f18ebdbf906d14fb9a14113b4e7 Mon Sep 17 00:00:00 2001 From: Monika Kairaityte Date: Wed, 2 Jul 2025 19:59:10 +0300 Subject: [PATCH 1/3] Properly surface errors from `push` command Failure exit code for `push` command is not currently forwarded as exit code for podman-compose. With this PR, podman-compose stops pushing when the underlying podman command fails and forwards its exit code. Signed-off-by: Monika Kairaityte --- ...rwarding-exit-code-for-push-command.bugfix | 1 + podman_compose.py | 8 +++++-- .../test_podman_compose_build_fail_multi.py | 22 +++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 newsfragments/fix-forwarding-exit-code-for-push-command.bugfix diff --git a/newsfragments/fix-forwarding-exit-code-for-push-command.bugfix b/newsfragments/fix-forwarding-exit-code-for-push-command.bugfix new file mode 100644 index 0000000..0972449 --- /dev/null +++ b/newsfragments/fix-forwarding-exit-code-for-push-command.bugfix @@ -0,0 +1 @@ +Implemented forwarding failure exit code from `push` command. diff --git a/podman_compose.py b/podman_compose.py index 61c0a55..2b33248 100755 --- a/podman_compose.py +++ b/podman_compose.py @@ -2799,14 +2799,18 @@ async def compose_pull(compose: PodmanCompose, args: argparse.Namespace) -> None @cmd_run(podman_compose, "push", "push stack images") -async def compose_push(compose: PodmanCompose, args: argparse.Namespace) -> None: +async def compose_push(compose: PodmanCompose, args: argparse.Namespace) -> int | None: services = set(args.services) + status = 0 for cnt in compose.containers: if "build" not in cnt: continue if services and cnt["_service"] not in services: continue - await compose.podman.run([], "push", [cnt["image"]]) + s = await compose.podman.run([], "push", [cnt["image"]]) + if s is not None and s != 0: + status = s + return status def is_context_git_url(path: str) -> bool: diff --git a/tests/integration/build_fail_multi/test_podman_compose_build_fail_multi.py b/tests/integration/build_fail_multi/test_podman_compose_build_fail_multi.py index 167becb..f1afad8 100644 --- a/tests/integration/build_fail_multi/test_podman_compose_build_fail_multi.py +++ b/tests/integration/build_fail_multi/test_podman_compose_build_fail_multi.py @@ -29,3 +29,25 @@ class TestComposeBuildFailMulti(unittest.TestCase, RunSubprocessMixin): ) self.assertIn("RUN false", str(output)) self.assertIn("while running runtime: exit status 1", str(error)) + + def test_push_command_fail(self) -> None: + # test that push command is able to return other than "0" return code + # "push" command fails due to several steps missing before running it (logging, tagging) + try: + output, error = self.run_subprocess_assert_returncode( + [ + podman_compose_path(), + "-f", + compose_yaml_path(), + "push", + "good", + ], + expected_returncode=125, + ) + finally: + self.run_subprocess_assert_returncode([ + podman_compose_path(), + "-f", + compose_yaml_path(), + "down", + ]) From c1ca9166c6a86431a9b0d0d1bb60487f079323a5 Mon Sep 17 00:00:00 2001 From: Monika Kairaityte Date: Wed, 2 Jul 2025 20:02:03 +0300 Subject: [PATCH 2/3] tests/integration: Add test for `run` command failure exit code Test is added to confirm that `run` command forwards non-zero failure exit code. Signed-off-by: Monika Kairaityte --- .../test_podman_compose_build_fail_multi.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/integration/build_fail_multi/test_podman_compose_build_fail_multi.py b/tests/integration/build_fail_multi/test_podman_compose_build_fail_multi.py index f1afad8..3dcb175 100644 --- a/tests/integration/build_fail_multi/test_podman_compose_build_fail_multi.py +++ b/tests/integration/build_fail_multi/test_podman_compose_build_fail_multi.py @@ -51,3 +51,26 @@ class TestComposeBuildFailMulti(unittest.TestCase, RunSubprocessMixin): compose_yaml_path(), "down", ]) + + def test_run_command_fail(self) -> None: + # test that run command is able to return other than "0" return code + try: + output, error = self.run_subprocess_assert_returncode( + [ + podman_compose_path(), + "-f", + compose_yaml_path(), + "run", + "bad", + ], + expected_returncode=125, + ) + self.assertIn("RUN false", str(output)) + self.assertIn("while running runtime: exit status 1", str(error)) + finally: + self.run_subprocess_assert_returncode([ + podman_compose_path(), + "-f", + compose_yaml_path(), + "down", + ]) From b3fd55047b9455639928f3ef216919638b115419 Mon Sep 17 00:00:00 2001 From: Monika Kairaityte Date: Wed, 2 Jul 2025 20:43:47 +0300 Subject: [PATCH 3/3] tests/integration: Rename dir `build_fail_multi` Renamed directory `build_fail_multi` to more appropriate `commands_fail_exit_code` as more tests were added to other commands:`push` and `run`. Names of tests were changed accordingly. Signed-off-by: Monika Kairaityte --- .../__init__.py | 0 .../bad/Dockerfile | 0 .../docker-compose.yml | 0 .../good/Dockerfile | 0 .../test_podman_compose_commands_fail_exit_code.py} | 8 ++++---- 5 files changed, 4 insertions(+), 4 deletions(-) rename tests/integration/{build_fail_multi => commands_fail_exit_code}/__init__.py (100%) rename tests/integration/{build_fail_multi => commands_fail_exit_code}/bad/Dockerfile (100%) rename tests/integration/{build_fail_multi => commands_fail_exit_code}/docker-compose.yml (100%) rename tests/integration/{build_fail_multi => commands_fail_exit_code}/good/Dockerfile (100%) rename tests/integration/{build_fail_multi/test_podman_compose_build_fail_multi.py => commands_fail_exit_code/test_podman_compose_commands_fail_exit_code.py} (91%) diff --git a/tests/integration/build_fail_multi/__init__.py b/tests/integration/commands_fail_exit_code/__init__.py similarity index 100% rename from tests/integration/build_fail_multi/__init__.py rename to tests/integration/commands_fail_exit_code/__init__.py diff --git a/tests/integration/build_fail_multi/bad/Dockerfile b/tests/integration/commands_fail_exit_code/bad/Dockerfile similarity index 100% rename from tests/integration/build_fail_multi/bad/Dockerfile rename to tests/integration/commands_fail_exit_code/bad/Dockerfile diff --git a/tests/integration/build_fail_multi/docker-compose.yml b/tests/integration/commands_fail_exit_code/docker-compose.yml similarity index 100% rename from tests/integration/build_fail_multi/docker-compose.yml rename to tests/integration/commands_fail_exit_code/docker-compose.yml diff --git a/tests/integration/build_fail_multi/good/Dockerfile b/tests/integration/commands_fail_exit_code/good/Dockerfile similarity index 100% rename from tests/integration/build_fail_multi/good/Dockerfile rename to tests/integration/commands_fail_exit_code/good/Dockerfile diff --git a/tests/integration/build_fail_multi/test_podman_compose_build_fail_multi.py b/tests/integration/commands_fail_exit_code/test_podman_compose_commands_fail_exit_code.py similarity index 91% rename from tests/integration/build_fail_multi/test_podman_compose_build_fail_multi.py rename to tests/integration/commands_fail_exit_code/test_podman_compose_commands_fail_exit_code.py index 3dcb175..0e4fa4e 100644 --- a/tests/integration/build_fail_multi/test_podman_compose_build_fail_multi.py +++ b/tests/integration/commands_fail_exit_code/test_podman_compose_commands_fail_exit_code.py @@ -8,14 +8,14 @@ from tests.integration.test_utils import podman_compose_path from tests.integration.test_utils import test_path -def compose_yaml_path(): +def compose_yaml_path() -> str: """ "Returns the path to the compose file used for this test module""" - base_path = os.path.join(test_path(), "build_fail_multi") + base_path = os.path.join(test_path(), "commands_fail_exit_code") return os.path.join(base_path, "docker-compose.yml") -class TestComposeBuildFailMulti(unittest.TestCase, RunSubprocessMixin): - def test_build_fail_multi(self): +class TestComposeCommandsFailExitCodes(unittest.TestCase, RunSubprocessMixin): + def test_build_command_fail(self) -> None: output, error = self.run_subprocess_assert_returncode( [ podman_compose_path(),