diff --git a/src/fosslight_util/download.py b/src/fosslight_util/download.py index f3e10df..254968c 100644 --- a/src/fosslight_util/download.py +++ b/src/fosslight_util/download.py @@ -319,7 +319,7 @@ def get_remote_refs(git_url: str): re.IGNORECASE, ) _SEMVER_IN_REF = re.compile( - r'(?:^v\.? ?|[-_])' + r'(?:^v\.? ?|[-_]v\.? ?|[-_])' r'(\d+)\.(\d+)\.(\d+)' r'(?:(?:-[0-9A-Za-z.-]+)|(?:\.[A-Za-z][0-9A-Za-z.-]*))?' r'(?:\+[0-9A-Za-z.-]+)?' @@ -467,6 +467,48 @@ def _strip_leading_v_prefix(s: str) -> str: return re.sub(r'^(?:v\.? ?)?', '', s.strip(), flags=re.IGNORECASE) +def _repo_name_from_git_url(git_url: str) -> str: + """Extract repository name from common git HTTPS/SSH URLs.""" + if not git_url: + return "" + url = git_url.strip().rstrip('/') + if url.endswith('.git'): + url = url[:-4] + m = re.match(r'git@[^:]+:[^/]+/([^/@?#]+)$', url) + if m: + return m.group(1) + path = urllib.parse.urlparse(url).path.strip('/') + if path: + segments = path.split('/') + if len(segments) >= 2: + return segments[-1] + return "" + + +def _repo_prefixed_version_refs(repo_name: str, base: str) -> List[str]: + """Build {repo}-v{version} style tag candidates from checkout hint.""" + if not repo_name or not base: + return [] + base = base.strip() + core = _strip_leading_v_prefix(base) + if not core: + return [] + repo = repo_name.strip() + candidates = [ + f"{repo}-v{core}", + f"{repo}-v.{core}", + f"{repo}_{core}", + f"{repo}-{core}", + ] + seen = set() + result = [] + for candidate in candidates: + if candidate not in seen: + seen.add(candidate) + result.append(candidate) + return result + + def _match_exact_or_v_prefix_ref(base: str, ref_set: set) -> Optional[str]: """Exact ref match, or ref equal to base after optional leading v/v./v.""" if base in ref_set: @@ -558,11 +600,16 @@ def decide_checkout(checkout_to="", tag="", branch="", git_url=""): tag_set = set(ref_dict.get("tags", [])) branch_set = set(ref_dict.get("branches", [])) ref_set = tag_set | branch_set + repo_name = _repo_name_from_git_url(git_url) for raw in (tag, branch, checkout_to): b = (raw or "").strip() if not b: continue + for candidate in _repo_prefixed_version_refs(repo_name, b): + ref, clar = _try_resolve_checkout_base(candidate, ref_set) + if ref is not None: + return ref, clar ref, clar = _try_resolve_checkout_base(b, ref_set) if ref is not None: return ref, clar diff --git a/tests/test_decide_checkout.py b/tests/test_decide_checkout.py new file mode 100644 index 0000000..620776a --- /dev/null +++ b/tests/test_decide_checkout.py @@ -0,0 +1,69 @@ +# Copyright (c) 2026 LG Electronics Inc. +# SPDX-License-Identifier: Apache-2.0 +"""Tests for git checkout ref resolution (decide_checkout).""" + +import pytest + +from fosslight_util.download import ( + _repo_name_from_git_url, + _repo_prefixed_version_refs, + _try_resolve_checkout_base, + clarified_version_from_oss_version, + decide_checkout, +) + + +@pytest.mark.parametrize( + "git_url,expected", + [ + ("https://github.com/rrousselGit/freezed", "freezed"), + ("https://github.com/rrousselGit/freezed.git", "freezed"), + ("git@github.com:rrousselGit/freezed.git", "freezed"), + ], +) +def test_repo_name_from_git_url(git_url, expected): + assert _repo_name_from_git_url(git_url) == expected + + +def test_repo_prefixed_version_refs(): + assert _repo_prefixed_version_refs("freezed", "2.4.4") == [ + "freezed-v2.4.4", + "freezed-v.2.4.4", + "freezed_2.4.4", + "freezed-2.4.4", + ] + assert _repo_prefixed_version_refs("freezed", "v2.4.4")[0] == "freezed-v2.4.4" + + +def test_try_resolve_checkout_base_matches_repo_prefixed_tag(): + ref_set = {"freezed-v2.4.4", "freezed_annotation-v2.4.4", "master"} + ref, clar = _try_resolve_checkout_base("freezed-v2.4.4", ref_set) + assert ref == "freezed-v2.4.4" + assert clar == "2.4.4" + + +def test_try_resolve_checkout_base_semver_matches_repo_prefixed_tag(): + ref_set = {"freezed-v2.4.4", "freezed_annotation-v2.4.4", "master"} + ref, clar = _try_resolve_checkout_base("2.4.4", ref_set) + assert ref == "freezed-v2.4.4" + assert clar == "2.4.4" + + +def test_clarified_version_from_repo_prefixed_tag(): + assert clarified_version_from_oss_version("freezed-v2.4.4") == "2.4.4" + + +def test_decide_checkout_resolves_repo_prefixed_tag(monkeypatch): + tags = ["freezed-v2.4.4", "freezed_annotation-v2.4.4"] + monkeypatch.setattr( + "fosslight_util.download.get_remote_refs", + lambda _url: {"tags": tags, "branches": ["master"]}, + ) + + ref, clar = decide_checkout( + checkout_to="2.4.4", + git_url="https://github.com/rrousselGit/freezed", + ) + + assert ref == "freezed-v2.4.4" + assert clar == "2.4.4"