From dd471c891831833c97dce2b051e479817727630a Mon Sep 17 00:00:00 2001 From: Monika Kairaityte Date: Mon, 16 Jun 2025 12:05:33 +0300 Subject: [PATCH] Fix dockerfile definition if directory name ends with ".git" After changes in 92f0a8583afe9e2f263be4b8dac274b94056e332, the dockerfile parameter is igored if the (local) work directory's name ends in `.git`. This commit fixes the regression and adds more tests. Signed-off-by: Monika Kairaityte --- ...ix-dockerfile-definition-regression.bugfix | 1 + podman_compose.py | 19 +++-- tests/unit/test_container_to_build_args.py | 10 --- tests/unit/test_is_context_git_url.py | 80 +++++++++++++++++++ tests/unit/test_is_path_git_url.py | 75 ++++++++++++++--- 5 files changed, 161 insertions(+), 24 deletions(-) create mode 100644 newsfragments/fix-dockerfile-definition-regression.bugfix create mode 100644 tests/unit/test_is_context_git_url.py diff --git a/newsfragments/fix-dockerfile-definition-regression.bugfix b/newsfragments/fix-dockerfile-definition-regression.bugfix new file mode 100644 index 0000000..948be0b --- /dev/null +++ b/newsfragments/fix-dockerfile-definition-regression.bugfix @@ -0,0 +1 @@ +Fixed regression of dockerfile definition if working directory name ends with ".git". diff --git a/podman_compose.py b/podman_compose.py index ab462f3..079351d 100755 --- a/podman_compose.py +++ b/podman_compose.py @@ -1799,7 +1799,7 @@ def normalize_service_final(service: dict[str, Any], project_dir: str) -> dict[s build = service["build"] context = build if isinstance(build, str) else build.get("context", ".") - if not is_path_git_url(context): + if not is_context_git_url(context): context = os.path.normpath(os.path.join(project_dir, context)) if not isinstance(service["build"], dict): service["build"] = {} @@ -2788,9 +2788,18 @@ async def compose_push(compose: PodmanCompose, args: argparse.Namespace) -> None await compose.podman.run([], "push", [cnt["image"]]) -def is_path_git_url(path: str) -> bool: +def is_context_git_url(path: str) -> bool: r = urllib.parse.urlparse(path) - return r.scheme == 'git' or r.path.endswith('.git') + if r.scheme in ('git', 'http', 'https', 'ssh', 'file', 'rsync'): + return True + # URL contains a ":" character, a hint of a valid URL + if r.scheme != "" and r.netloc == "" and r.path != "": + return True + if r.scheme == "": # tweak path URL to get username from url parser + r = urllib.parse.urlparse("ssh://" + path) + if r.username is not None and r.username != "": + return True + return False def adjust_build_ssh_key_paths(compose: PodmanCompose, agent_or_key: str) -> str: @@ -2834,8 +2843,8 @@ def container_to_build_args( cleanup_callbacks.append(cleanup_temp_dockfile) build_args = [] - - if not is_path_git_url(ctx): + # if givent context was not recognized as git url, try joining paths to get a file locally + if not is_context_git_url(ctx): custom_dockerfile_given = False if dockerfile: dockerfile = os.path.join(ctx, dockerfile) diff --git a/tests/unit/test_container_to_build_args.py b/tests/unit/test_container_to_build_args.py index 3b079ea..bc553a0 100644 --- a/tests/unit/test_container_to_build_args.py +++ b/tests/unit/test_container_to_build_args.py @@ -210,16 +210,6 @@ class TestContainerToBuildArgs(unittest.TestCase): with self.assertRaises(OSError): container_to_build_args(c, cnt, args, lambda path: False) - def test_context_invalid_git_url_git_is_not_suffix(self): - c = create_compose_mock() - - cnt = get_minimal_container() - cnt['build']['context'] = "https://github.com/test_repo.git/not_suffix" - args = get_minimal_args() - - with self.assertRaises(OSError): - container_to_build_args(c, cnt, args, lambda path: False) - def test_build_ssh_absolute_path(self): c = create_compose_mock() diff --git a/tests/unit/test_is_context_git_url.py b/tests/unit/test_is_context_git_url.py new file mode 100644 index 0000000..3c6db9b --- /dev/null +++ b/tests/unit/test_is_context_git_url.py @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: GPL-2.0 + +import unittest + +from parameterized import parameterized + +from podman_compose import is_context_git_url + + +class TestIsContextGitUrl(unittest.TestCase): + @parameterized.expand([ + ("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", True), + ("no_prefix", "http://host.xz/path/to/repo", True), + ("wrong_prefix_git", "gitt://host.xz/path/to/repo", False), + ("wrong_prefix_http", "htt://host.xz/path/to/repo.git", False), + ("user_path_ending_with_git", "path/to/workdir.git", False), + ("", "/path/to/workdir.git", False), + ("", "/path/to:workdir.git", False), + ("", "/path/to@workdir.git", False), + ("", "~/path/to/workdir.git", False), + # many of possible ways git url can look like + ("", "http://example.com/my-project.git", True), + ("", "file:///absolute/path/to/my-project.git", True), + ("", "ssh:user@example.com:my-project", True), + ("", "git@github.com:user/project.git", True), + ("", "https://github.com/user/project.git", True), + ("", "http://github.com/user/project.git", True), + ("", "git@192.168.101.127:user/project.git", True), + ("", "https://192.168.101.127/user/project.git", True), + ("", "http://192.168.101.127/user/project.git", True), + ("", "ssh://user@host.xz:port/path/to/repo.git/", True), + ("", "ssh://user@host.xz/path/to/repo.git/", True), + ("", "ssh://host.xz:port/path/to/repo.git/", True), + ("", "ssh://host.xz/path/to/repo.git/", True), + ("", "ssh://user@host.xz/path/to/repo.git/", True), + ("", "ssh://host.xz/path/to/repo.git/", True), + ("", "ssh://user@host.xz/~user/path/to/repo.git/", True), + ("", "ssh://host.xz/~user/path/to/repo.git/", True), + ("", "ssh://user@host.xz/~/path/to/repo.git", True), + ("", "ssh://host.xz/~/path/to/repo.git", True), + ("", "git://host.xz/path/to/repo.git/", True), + ("", "git://host.xz/~user/path/to/repo.git/", True), + ("", "http://host.xz/path/to/repo.git/", True), + ("", "https://host.xz/path/to/repo.git/", True), + ("", "git@custom-gitlab:my-group/myrepo.git", True), + ("", "ssh://user@host.xz:port/path/to/repo.git/", True), + ("", "ssh://user@host.xz/path/to/repo.git/", True), + ("", "ssh://host.xz:port/path/to/repo.git/", True), + ("", "ssh://host.xz/path/to/repo.git/", True), + ("", "ssh://user@host.xz/path/to/repo.git/", True), + ("", "ssh://host.xz/path/to/repo.git/", True), + ("", "ssh://user@host.xz/~user/path/to/repo.git/", True), + ("", "ssh://host.xz/~user/path/to/repo.git/", True), + ("", "ssh://user@host.xz/~/path/to/repo.git", True), + ("", "ssh://host.xz/~/path/to/repo.git", True), + ("", "git://host.xz/path/to/repo.git/", True), + ("", "git://host.xz/~user/path/to/repo.git/", True), + ("", "http://host.xz/path/to/repo.git/", True), + ("", "https://host.xz/path/to/repo.git/", True), + ("", "ssh:user@example.com:my-project", True), + ("", "user@host.xz:/path/to/repo.git/", True), + ("", "host.xz:/path/to/repo.git/", True), + ("", "host:path/to/repo.git", True), + ("", "host:path/to/repo", True), + ("", "user@host.xz:~user/path/to/repo.git/", True), + ("", "host.xz:~user/path/to/repo.git/", True), + ("", "user@host.xz:path/to/repo.git", True), + ("", "user@path/to/repo", True), + ("", "host.xz:path/to/repo.git", True), + ("", "rsync://host.xz/path/to/repo.git/", True), + ("", "file:///path/to/repo.git/", True), + ("", "file://~/path/to/repo.git/", True), + ("", "github.com:containers/podman-compose.git", True), + ("", "github:containers/podman-compose.git", True), + ("", "https://github.com/test_repo.git/git_not_suffix", True), + ]) + def test_is_path_git_url(self, test_name: str, path: str, result: bool) -> None: + self.assertEqual(is_context_git_url(path), result) diff --git a/tests/unit/test_is_path_git_url.py b/tests/unit/test_is_path_git_url.py index 80df447..bd0b76c 100644 --- a/tests/unit/test_is_path_git_url.py +++ b/tests/unit/test_is_path_git_url.py @@ -4,19 +4,76 @@ import unittest from parameterized import parameterized -from podman_compose import is_path_git_url +from podman_compose import is_context_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), + ("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), + ("empty_url_path", "http://#fragment", True), + ("no_prefix", "http://host.xz/path/to/repo", True), + ("wrong_prefix_git", "gitt://host.xz/path/to/repo", False), + ("wrong_prefix_http", "htt://host.xz/path/to/repo.git", False), + ("user_path_ending_with_git", "path/to/workdir.git", False), + ("", "/path/to/workdir.git", False), + ("", "/path/to:workdir.git", False), + ("", "/path/to@workdir.git", False), + ("", "~/path/to/workdir.git", False), + # many of possible ways git url can look like + ("", "http://example.com/my-project.git", True), + ("", "file:///absolute/path/to/my-project.git", True), + ("", "ssh:user@example.com:my-project", True), + ("", "git@github.com:user/project.git", True), + ("", "https://github.com/user/project.git", True), + ("", "http://github.com/user/project.git", True), + ("", "git@192.168.101.127:user/project.git", True), + ("", "https://192.168.101.127/user/project.git", True), + ("", "http://192.168.101.127/user/project.git", True), + ("", "ssh://user@host.xz:port/path/to/repo.git/", True), + ("", "ssh://user@host.xz/path/to/repo.git/", True), + ("", "ssh://host.xz:port/path/to/repo.git/", True), + ("", "ssh://host.xz/path/to/repo.git/", True), + ("", "ssh://user@host.xz/path/to/repo.git/", True), + ("", "ssh://host.xz/path/to/repo.git/", True), + ("", "ssh://user@host.xz/~user/path/to/repo.git/", True), + ("", "ssh://host.xz/~user/path/to/repo.git/", True), + ("", "ssh://user@host.xz/~/path/to/repo.git", True), + ("", "ssh://host.xz/~/path/to/repo.git", True), + ("", "git://host.xz/path/to/repo.git/", True), + ("", "git://host.xz/~user/path/to/repo.git/", True), + ("", "http://host.xz/path/to/repo.git/", True), + ("", "https://host.xz/path/to/repo.git/", True), + ("", "git@custom-gitlab:my-group/myrepo.git", True), + ("", "ssh://user@host.xz:port/path/to/repo.git/", True), + ("", "ssh://user@host.xz/path/to/repo.git/", True), + ("", "ssh://host.xz:port/path/to/repo.git/", True), + ("", "ssh://host.xz/path/to/repo.git/", True), + ("", "ssh://user@host.xz/path/to/repo.git/", True), + ("", "ssh://host.xz/path/to/repo.git/", True), + ("", "ssh://user@host.xz/~user/path/to/repo.git/", True), + ("", "ssh://host.xz/~user/path/to/repo.git/", True), + ("", "ssh://user@host.xz/~/path/to/repo.git", True), + ("", "ssh://host.xz/~/path/to/repo.git", True), + ("", "git://host.xz/path/to/repo.git/", True), + ("", "git://host.xz/~user/path/to/repo.git/", True), + ("", "http://host.xz/path/to/repo.git/", True), + ("", "https://host.xz/path/to/repo.git/", True), + ("", "ssh:user@example.com:my-project", True), + ("", "user@host.xz:/path/to/repo.git/", True), + ("", "host.xz:/path/to/repo.git/", True), + ("", "host:path/to/repo.git", True), + ("", "host:path/to/repo", True), + ("", "user@host.xz:~user/path/to/repo.git/", True), + ("", "host.xz:~user/path/to/repo.git/", True), + ("", "user@host.xz:path/to/repo.git", True), + ("", "user@path/to/repo", True), + ("", "host.xz:path/to/repo.git", True), + ("", "rsync://host.xz/path/to/repo.git/", True), + ("", "file:///path/to/repo.git/", True), + ("", "file://~/path/to/repo.git/", True), + ("", "github.com:containers/podman-compose.git", True), + ("", "github:containers/podman-compose.git", True), ]) def test_is_path_git_url(self, test_name: str, path: str, result: bool) -> None: - self.assertEqual(is_path_git_url(path), result) + self.assertEqual(is_context_git_url(path), result)