diff --git a/src/poetry/vcs/git/backend.py b/src/poetry/vcs/git/backend.py index 20d7a1223af..2e3032147f8 100644 --- a/src/poetry/vcs/git/backend.py +++ b/src/poetry/vcs/git/backend.py @@ -7,6 +7,7 @@ from pathlib import Path from subprocess import CalledProcessError from typing import TYPE_CHECKING +from urllib.parse import urljoin from dulwich import porcelain from dulwich.client import HTTPUnauthorized @@ -331,16 +332,24 @@ def _clone_submodules(cls, repo: Repo) -> None: repo_root = Path(repo.path) modules_config = repo_root.joinpath(".gitmodules") + # A relative URL by definition starts with ../ or ./ + relative_submodule_regex = re.compile(r"^\.{1,2}/") + if modules_config.exists(): config = ConfigFile.from_path(str(modules_config)) url: bytes path: bytes submodules = parse_submodules(config) + for path, url, name in submodules: path_relative = Path(path.decode("utf-8")) path_absolute = repo_root.joinpath(path_relative) + url_string = url.decode("utf-8") + if relative_submodule_regex.search(url_string): + url_string = urljoin(f"{Git.get_remote_url(repo)}/", url_string) + source_root = path_absolute.parent source_root.mkdir(parents=True, exist_ok=True) @@ -357,7 +366,7 @@ def _clone_submodules(cls, repo: Repo) -> None: continue cls.clone( - url=url.decode("utf-8"), + url=url_string, source_root=source_root, name=path_relative.name, revision=revision, diff --git a/tests/integration/test_utils_vcs_git.py b/tests/integration/test_utils_vcs_git.py index 5c399d5311b..ec0f558f3fe 100644 --- a/tests/integration/test_utils_vcs_git.py +++ b/tests/integration/test_utils_vcs_git.py @@ -240,6 +240,30 @@ def test_git_clone_clones_submodules(source_url: str) -> None: assert len(list(submodule_package_directory.glob("*"))) > 1 +def test_git_clone_clones_submodules_with_relative_urls(source_url: str) -> None: + with Git.clone(url=source_url, branch="relative_submodule") as repo: + submodule_package_directory = ( + Path(repo.path) / "submodules" / "relative-url-submodule" + ) + + assert submodule_package_directory.exists() + assert submodule_package_directory.joinpath("README.md").exists() + assert len(list(submodule_package_directory.glob("*"))) > 1 + + +def test_git_clone_clones_submodules_with_relative_urls_and_explicit_base( + source_url: str, +) -> None: + with Git.clone(url=source_url, branch="relative_submodule") as repo: + submodule_package_directory = ( + Path(repo.path) / "submodules" / "relative-url-submodule-with-base" + ) + + assert submodule_package_directory.exists() + assert submodule_package_directory.joinpath("README.md").exists() + assert len(list(submodule_package_directory.glob("*"))) > 1 + + def test_system_git_fallback_on_http_401( mocker: MockerFixture, source_url: str,