From 92f0a8583afe9e2f263be4b8dac274b94056e332 Mon Sep 17 00:00:00 2001 From: Monika Kairaityte Date: Tue, 25 Mar 2025 19:46:59 +0200 Subject: [PATCH] Fix using git URL as build context Podman-compose actually did not work with git URL as build context. Signed-off-by: Monika Kairaityte --- .../127-fix-git-build-url-context.bugfix | 1 + podman_compose.py | 30 ++++++++++++++----- tests/unit/test_is_path_git_url.py | 22 ++++++++++++++ 3 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 newsfragments/127-fix-git-build-url-context.bugfix create mode 100644 tests/unit/test_is_path_git_url.py diff --git a/newsfragments/127-fix-git-build-url-context.bugfix b/newsfragments/127-fix-git-build-url-context.bugfix new file mode 100644 index 0000000..cc277cd --- /dev/null +++ b/newsfragments/127-fix-git-build-url-context.bugfix @@ -0,0 +1 @@ +- Fix using git URL as build context diff --git a/podman_compose.py b/podman_compose.py index be9d93a..b206d7f 100755 --- a/podman_compose.py +++ b/podman_compose.py @@ -26,6 +26,7 @@ import signal import subprocess import sys import tempfile +import urllib.parse from asyncio import Task from enum import Enum @@ -1663,7 +1664,9 @@ def normalize_service_final(service: dict, project_dir: str) -> dict: if "build" in service: build = service["build"] context = build if isinstance(build, str) else build.get("context", ".") - context = os.path.normpath(os.path.join(project_dir, context)) + + if not is_path_git_url(context): + context = os.path.normpath(os.path.join(project_dir, context)) if not isinstance(service["build"], dict): service["build"] = {} service["build"]["context"] = context @@ -2501,12 +2504,17 @@ async def compose_push(compose, args): await compose.podman.run([], "push", [cnt["image"]]) +def is_path_git_url(path): + r = urllib.parse.urlparse(path) + return r.scheme == 'git' or r.path.endswith('.git') + + def container_to_build_args(compose, cnt, args, path_exists, cleanup_callbacks=None): build_desc = cnt["build"] if not hasattr(build_desc, "items"): build_desc = {"context": build_desc} ctx = build_desc.get("context", ".") - dockerfile = build_desc.get("dockerfile") + dockerfile = build_desc.get("dockerfile", "") dockerfile_inline = build_desc.get("dockerfile_inline") if dockerfile_inline is not None: dockerfile_inline = str(dockerfile_inline) @@ -2524,7 +2532,10 @@ def container_to_build_args(compose, cnt, args, path_exists, cleanup_callbacks=N if cleanup_callbacks is not None: list.append(cleanup_callbacks, cleanup_temp_dockfile) - else: + + build_args = [] + + if not is_path_git_url(ctx): if dockerfile: dockerfile = os.path.join(ctx, dockerfile) else: @@ -2540,11 +2551,16 @@ def container_to_build_args(compose, cnt, args, path_exists, cleanup_callbacks=N dockerfile = os.path.join(ctx, dockerfile) if path_exists(dockerfile): break - if not path_exists(dockerfile): - raise OSError("Dockerfile not found in " + ctx) - dockerfile = os.path.normpath(os.path.join(ctx, dockerfile)) - build_args = ["-f", dockerfile, "-t", cnt["image"]] + if path_exists(dockerfile): + # normalize dockerfile path, as the user could have provided unpredictable file formats + dockerfile = os.path.normpath(os.path.join(ctx, dockerfile)) + build_args.extend(["-f", dockerfile]) + else: + raise OSError(f"Dockerfile not found in {ctx}") + + build_args.extend(["-t", cnt["image"]]) + if "platform" in cnt: build_args.extend(["--platform", cnt["platform"]]) for secret in build_desc.get("secrets", []): diff --git a/tests/unit/test_is_path_git_url.py b/tests/unit/test_is_path_git_url.py new file mode 100644 index 0000000..831a7a7 --- /dev/null +++ b/tests/unit/test_is_path_git_url.py @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0 + +import unittest + +from parameterized import parameterized + +from podman_compose import is_path_git_url + + +class TestIsPathGitUrl(unittest.TestCase): + @parameterized.expand([ + ("prefix_git", "git://host.xz/path/to/repo", True), + ("prefix_almost_git", "gitt://host.xz/path/to/repo", False), + ("prefix_wrong", "http://host.xz/path/to/repo", False), + ("suffix_git", "http://host.xz/path/to/repo.git", True), + ("suffix_wrong", "http://host.xz/path/to/repo", False), + ("suffix_with_url_fragment", "http://host.xz/path/to/repo.git#fragment", True), + ("suffix_and_prefix", "git://host.xz/path/to/repo.git", True), + ("empty_url_path", "http://#fragment", False), + ]) + def test_is_path_git_url(self, test_name, path, result): + self.assertEqual(is_path_git_url(path), result)