Skip to content

Commit 05a65c1

Browse files
arnavk23claudeIanButterworth
authored
Fix : Search for previous tag by Git tag, not by GitHub release (#472)
Co-authored-by: Claude <[email protected]> Co-authored-by: Ian Butterworth <[email protected]>
1 parent c84694b commit 05a65c1

File tree

4 files changed

+77
-10
lines changed

4 files changed

+77
-10
lines changed

DEVGUIDE.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,6 +1023,20 @@ for commit in repo.get_commits(): # Paginated API calls!
10231023

10241024
### Testing Requirements
10251025

1026+
**Local development setup**:
1027+
1028+
```bash
1029+
# Create and activate a virtual environment
1030+
python3 -m venv .venv
1031+
source .venv/bin/activate
1032+
1033+
# Install package with dependencies
1034+
pip install .
1035+
1036+
# Install dev tools
1037+
pip install pytest pytest-cov black flake8 mypy boto3
1038+
```
1039+
10261040
**Before submitting changes**:
10271041

10281042
```bash

tagbot/action/changelog.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from datetime import datetime, timedelta, timezone
55
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union
66

7+
from github import UnknownObjectException
78
from github.GitRelease import GitRelease
89
from github.Issue import Issue
910
from github.NamedUser import NamedUser
@@ -49,12 +50,13 @@ def _previous_release(self, version_tag: str) -> Optional[GitRelease]:
4950
cur_ver = VersionInfo.parse(version_tag[i_start:])
5051
prev_ver = VersionInfo(0)
5152
prev_rel = None
52-
tag_prefix = self._repo._tag_prefix()
53-
for r in self._repo._repo.get_releases():
54-
if not r.tag_name.startswith(tag_prefix):
53+
tags = self._repo.get_all_tags()
54+
55+
for tag_name in tags:
56+
if not tag_name.startswith(tag_prefix):
5557
continue
5658
try:
57-
ver = VersionInfo.parse(r.tag_name[i_start:])
59+
ver = VersionInfo.parse(tag_name[i_start:])
5860
except ValueError:
5961
continue
6062
if ver.prerelease or ver.build:
@@ -63,7 +65,17 @@ def _previous_release(self, version_tag: str) -> Optional[GitRelease]:
6365
# That means if we're creating a backport v1.1, an already existing v2.0,
6466
# despite being newer than v1.0, will not be selected.
6567
if ver < cur_ver and ver > prev_ver:
66-
prev_rel = r
68+
# Get the GitHub release for this tag if it exists
69+
try:
70+
prev_rel = self._repo._repo.get_release(tag_name)
71+
except UnknownObjectException:
72+
# Release doesn't exist - get commit datetime from the tag
73+
commit_time = self._repo._git.time_of_commit(tag_name)
74+
prev_rel = type(
75+
"obj",
76+
(object,),
77+
{"tag_name": tag_name, "created_at": commit_time},
78+
)()
6779
prev_ver = ver
6880
return prev_rel
6981

@@ -75,8 +87,8 @@ def _is_backport(self, version: str, tags: Optional[List[str]] = None) -> bool:
7587
)
7688

7789
if tags is None:
78-
# Populate the tags list with tag names from the releases
79-
tags = [r.tag_name for r in self._repo._repo.get_releases()]
90+
# Use Git tags instead of GitHub releases
91+
tags = self._repo.get_all_tags()
8092

8193
# Extract any package name prefix and version number from the input
8294
match = version_pattern.match(version)
@@ -266,7 +278,8 @@ def _collect_data(self, version_tag: str, sha: str) -> Dict[str, object]:
266278
prev_tag = None
267279
compare = None
268280
if previous:
269-
start = previous.created_at
281+
if previous.created_at:
282+
start = previous.created_at
270283
prev_tag = previous.tag_name
271284
compare = f"{self._repo._repo.html_url}/compare/{prev_tag}...{version_tag}"
272285
# When the last commit is a PR merge, the commit happens a second or two before

tagbot/action/repo.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,15 @@ def _highest_existing_version(self) -> Optional[VersionInfo]:
700700

701701
return highest
702702

703+
def get_all_tags(self) -> List[str]:
704+
"""Get all Git tag names in the repository.
705+
706+
Returns a list of tag names (without 'refs/tags/' prefix).
707+
Uses the tags cache to avoid repeated API calls.
708+
"""
709+
tags_cache = self._build_tags_cache()
710+
return list(tags_cache.keys())
711+
703712
def version_with_latest_commit(self, versions: Dict[str, str]) -> Optional[str]:
704713
"""Find the version with the most recent commit datetime.
705714

test/action/test_changelog.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ def test_slug():
4242
def test_previous_release():
4343
c = _changelog()
4444
tags = ["ignore", "v1.2.4-ignore", "v1.2.3", "v1.2.2", "v1.0.2", "v1.0.10"]
45-
c._repo._repo.get_releases = Mock(return_value=[Mock(tag_name=t) for t in tags])
45+
c._repo.get_all_tags = Mock(return_value=tags)
46+
# Mock get_release to return a minimal release-like object
47+
c._repo._repo.get_release = Mock(
48+
side_effect=lambda tag: type("obj", (object,), {"tag_name": tag})()
49+
)
4650
assert c._previous_release("v1.0.0") is None
4751
assert c._previous_release("v1.0.2") is None
4852
rel = c._previous_release("v1.2.5")
@@ -51,6 +55,29 @@ def test_previous_release():
5155
assert rel and rel.tag_name == "v1.0.2"
5256

5357

58+
def test_previous_release_no_github_release():
59+
"""Test that _previous_release falls back to commit time when no GitHub release."""
60+
from datetime import datetime
61+
from github import UnknownObjectException
62+
63+
c = _changelog()
64+
tags = ["v1.0.0", "v1.1.0"]
65+
c._repo.get_all_tags = Mock(return_value=tags)
66+
# Simulate no GitHub release existing for the tag
67+
c._repo._repo.get_release = Mock(
68+
side_effect=UnknownObjectException(404, "Not Found", {})
69+
)
70+
# Mock time_of_commit to return a datetime
71+
mock_time = datetime(2025, 1, 1, 12, 0, 0)
72+
c._repo._git.time_of_commit = Mock(return_value=mock_time)
73+
74+
rel = c._previous_release("v1.1.1")
75+
assert rel is not None
76+
assert rel.tag_name == "v1.1.0"
77+
assert rel.created_at == mock_time
78+
c._repo._git.time_of_commit.assert_called_with("v1.1.0")
79+
80+
5481
def test_previous_release_subdir():
5582
True
5683
c = _changelog(subdir="Foo")
@@ -67,7 +94,11 @@ def test_previous_release_subdir():
6794
"v2.0.1",
6895
"Foo-v2.0.0",
6996
]
70-
c._repo._repo.get_releases = Mock(return_value=[Mock(tag_name=t) for t in tags])
97+
c._repo.get_all_tags = Mock(return_value=tags)
98+
# Mock get_release to return a minimal release-like object
99+
c._repo._repo.get_release = Mock(
100+
side_effect=lambda tag: type("obj", (object,), {"tag_name": tag})()
101+
)
71102
assert c._previous_release("Foo-v1.0.0") is None
72103
assert c._previous_release("Foo-v1.0.2") is None
73104
rel = c._previous_release("Foo-v1.2.5")

0 commit comments

Comments
 (0)