|
16 | 16 | from git import GitCommandError |
17 | 17 | from git.objects import Commit |
18 | 18 | from git.repo import Repo |
| 19 | +from packaging import version |
19 | 20 | from pydriller.git import Git |
20 | 21 |
|
21 | 22 | from macaron.config.defaults import defaults |
22 | 23 | from macaron.config.global_config import global_config |
23 | 24 | from macaron.environment_variables import get_patched_env |
24 | | -from macaron.errors import CloneError |
| 25 | +from macaron.errors import CloneError, GitTagError |
25 | 26 |
|
26 | 27 | logger: logging.Logger = logging.getLogger(__name__) |
27 | 28 |
|
@@ -984,3 +985,73 @@ def get_tags_via_git_remote(repo: str) -> dict[str, str] | None: |
984 | 985 | logger.debug("Found %s tags via ls-remote of %s", len(tags), repo) |
985 | 986 |
|
986 | 987 | return tags |
| 988 | + |
| 989 | + |
| 990 | +def find_highest_git_tag(tags: set[str]) -> str: |
| 991 | + """ |
| 992 | + Find and return the highest (most recent) semantic version tag from a set of Git tags. |
| 993 | +
|
| 994 | + Parameters |
| 995 | + ---------- |
| 996 | + tags : set[str] |
| 997 | + A set of version strings (e.g., {"v1.0.0", "v2.3.4", "v2.1.0"}). Tags must follow PEP 440 or |
| 998 | + semver format, otherwise they will be skipped. |
| 999 | +
|
| 1000 | + Returns |
| 1001 | + ------- |
| 1002 | + str |
| 1003 | + The tag string corresponding to the highest version. |
| 1004 | +
|
| 1005 | + Raises |
| 1006 | + ------ |
| 1007 | + GitTagError |
| 1008 | + If no valid tag is found or if a tag is not a valid version. |
| 1009 | +
|
| 1010 | + Example |
| 1011 | + ------- |
| 1012 | + >>> find_highest_git_tag({"v2.0.0"}) |
| 1013 | + 'v2.0.0' |
| 1014 | +
|
| 1015 | + >>> find_highest_git_tag({"v4", "v4.2.1"}) |
| 1016 | + 'v4.2.1' |
| 1017 | +
|
| 1018 | + >>> find_highest_git_tag({"1.2.3", "2.0.0", "1.10.1"}) |
| 1019 | + '2.0.0' |
| 1020 | +
|
| 1021 | + >>> find_highest_git_tag({"0.1", "0.1.1", "0.0.9"}) |
| 1022 | + '0.1.1' |
| 1023 | +
|
| 1024 | + >>> find_highest_git_tag({"invalid", "1.0.0"}) |
| 1025 | + '1.0.0' |
| 1026 | +
|
| 1027 | + >>> find_highest_git_tag(set()) |
| 1028 | + Traceback (most recent call last): |
| 1029 | + ... |
| 1030 | + GitTagError: No tags provided. |
| 1031 | +
|
| 1032 | + >>> find_highest_git_tag({"invalid"}) |
| 1033 | + Traceback (most recent call last): |
| 1034 | + ... |
| 1035 | + GitTagError: No valid version tag found. |
| 1036 | + """ |
| 1037 | + if not tags: |
| 1038 | + raise GitTagError("No tags provided.") |
| 1039 | + |
| 1040 | + highest_tag = None |
| 1041 | + highest_parsed_tag = version.Version("0") |
| 1042 | + |
| 1043 | + for tag in tags: |
| 1044 | + try: |
| 1045 | + parsed_tag = version.Version(tag) |
| 1046 | + except version.InvalidVersion: |
| 1047 | + logger.debug("Invalid version tag encountered while finding the highest tag: %s", tag) |
| 1048 | + continue |
| 1049 | + |
| 1050 | + if parsed_tag > highest_parsed_tag: |
| 1051 | + highest_parsed_tag = parsed_tag |
| 1052 | + highest_tag = tag |
| 1053 | + |
| 1054 | + if highest_tag is None: |
| 1055 | + raise GitTagError("No valid version tag found.") |
| 1056 | + |
| 1057 | + return highest_tag |
0 commit comments