diff --git a/DEVGUIDE.md b/DEVGUIDE.md index cf88719b..34c2ab00 100644 --- a/DEVGUIDE.md +++ b/DEVGUIDE.md @@ -248,6 +248,11 @@ make pytest black flake8 mypy **Test locations**: `test/action/`, `test/web/`, `test/test_tagbot.py` +> **Warning**: `poetry run pytest` can silently run against a stale copy of +> tagbot installed in the venv's site-packages instead of the local sources, +> making test results not reflect your edits. Use `poetry run python -m pytest` +> (or `make pytest`), which puts the repo root first on `sys.path`. + ### What NOT to Do - ❌ Print statements (use logger) diff --git a/tagbot/action/repo.py b/tagbot/action/repo.py index edbfdc81..81b9bf31 100644 --- a/tagbot/action/repo.py +++ b/tagbot/action/repo.py @@ -1749,21 +1749,28 @@ def commit_sha_of_version(self, version: str) -> Optional[str]: return self._commit_sha_of_tree(tree) def branches_of_commit(self, sha: str) -> List[str]: - """Return short names of non-default remote branches that contain sha.""" + """Return short names of non-default remote branches that contain sha. + + Returns an empty list if the default branch contains the commit: a + commit reachable from the default branch is a mainline release, even + if other branches (e.g. feature branches based on it) also contain it + (see issue #574). + """ try: output = self._git.command("branch", "-r", "--contains", sha) default = f"origin/{self._repo.default_branch}" prefix = "origin/" prefix_len = len(prefix) - return [ - b.strip()[prefix_len:] - for b in output.splitlines() - if b.strip() and " -> " not in b and b.strip() != default + branches = [ + b.strip() for b in output.splitlines() if b.strip() and " -> " not in b ] + if default in branches: + return [] + return [b[prefix_len:] for b in branches] except Abort: logger.debug("Failed to get branches for commit", exc_info=True) return [] def is_backport_commit(self, sha: str) -> bool: - """Check if the commit is on a non-default branch.""" + """Check if the commit is only reachable from non-default branches.""" return bool(self.branches_of_commit(sha)) diff --git a/test/action/test_repo.py b/test/action/test_repo.py index 2323fc41..4b84c128 100644 --- a/test/action/test_repo.py +++ b/test/action/test_repo.py @@ -1517,9 +1517,10 @@ def test_is_backport_commit(): r._git.command.return_value = " origin/main\n" assert not r.is_backport_commit("abc123") - # Commit on default branch and another branch + # Commit on default branch and another branch → mainline, not a backport + # (e.g. a feature branch based on the release commit; issue #574) r._git.command.return_value = " origin/main\n origin/release-1.0\n" - assert r.is_backport_commit("abc123") + assert not r.is_backport_commit("abc123") # Commit on non-default branch only r._git.command.return_value = " origin/release-1.0\n" @@ -1557,8 +1558,12 @@ def test_branches_of_commit(): r._git.command.return_value = " origin/main\n" assert r.branches_of_commit("abc123") == [] - # One non-default branch + # Default branch plus other branches → mainline, so empty (issue #574) r._git.command.return_value = " origin/main\n origin/release-1.0\n" + assert r.branches_of_commit("abc123") == [] + + # One non-default branch + r._git.command.return_value = " origin/release-1.0\n" assert r.branches_of_commit("abc123") == ["release-1.0"] # Multiple non-default branches, no default