You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tooling.apache.org by ar...@apache.org on 2026/09/15 10:34:27 UTC
(tooling-trusted-releases) branch arm updated: #1559 - Don't double count ATR releases
This is an automated email from the ASF dual-hosted git repository.
alitheg pushed a commit to branch arm
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git
The following commit(s) were added to refs/heads/arm by this push:
new 43dd6b5e #1559 - Don't double count ATR releases
43dd6b5e is described below
commit 43dd6b5edcecc7b0b6c9cb4895c99e523f3213a3
Author: Alastair McFarlane <ar...@apache.org>
AuthorDate: Tue Sep 15 11:34:00 2026 +0100
#1559 - Don't double count ATR releases
---
atr/svn/__init__.py | 38 +++++++++++++++++++-------------
atr/svn/catalog.py | 16 +++++++++++++-
tests/unit/test_dist_watcher_removals.py | 26 ++++++++++++++++++++++
3 files changed, 64 insertions(+), 16 deletions(-)
diff --git a/atr/svn/__init__.py b/atr/svn/__init__.py
index ed7d2afa..8a5e48a9 100644
--- a/atr/svn/__init__.py
+++ b/atr/svn/__init__.py
@@ -357,6 +357,28 @@ async def publish_release(source_dir: pathlib.Path, target_url: str, username: s
return revision
+async def committed_by_atr(url: str, revision: str) -> bool:
+ # A commit ATR made carries the asf:tool=atr revprop, set by commit() - the one signal that marks
+ # our own publishes, since ATR commits under the release manager's UID, not the tool account, so
+ # the author reads the same as a hand commit does
+ stdin_bytes = _authentication(url)
+ tool = await _run_svn_command(
+ "propget",
+ url,
+ "--revprop",
+ "-r",
+ revision,
+ "asf:tool",
+ "--username",
+ ASF_TOOL,
+ "--password-from-stdin",
+ "--non-interactive",
+ timeout_seconds=INFO_TIMEOUT_SECONDS,
+ stdin_bytes=stdin_bytes,
+ )
+ return tool.strip() == ASF_TOOL
+
+
async def publish_revision_matches(info: SvnInfo, author: str, message: str) -> bool:
stdin_bytes = _authentication(info.url)
revision = info.last_changed_rev_number
@@ -390,21 +412,7 @@ async def publish_revision_matches(info: SvnInfo, author: str, message: str) ->
created_paths = {(path.text or "").strip() for path in entry.findall("./paths/path") if path.get("action") == "A"}
if target_path not in created_paths:
return False
- tool = await _run_svn_command(
- "propget",
- info.url,
- "--revprop",
- "-r",
- str(revision),
- "asf:tool",
- "--username",
- ASF_TOOL,
- "--password-from-stdin",
- "--non-interactive",
- timeout_seconds=INFO_TIMEOUT_SECONDS,
- stdin_bytes=stdin_bytes,
- )
- return tool.strip() == ASF_TOOL
+ return await committed_by_atr(info.url, str(revision))
async def remove_files(
diff --git a/atr/svn/catalog.py b/atr/svn/catalog.py
index 93b088a5..af1c25da 100644
--- a/atr/svn/catalog.py
+++ b/atr/svn/catalog.py
@@ -75,7 +75,6 @@ class _StructuralChanges(NamedTuple):
async def catalogue_commit(commit: dict) -> None:
if str(commit.get("committer", "")) == svn.ASF_TOOL:
- # Our own publishes are already in the database
return
changed = commit.get("changed", {})
if not isinstance(changed, dict):
@@ -87,6 +86,8 @@ async def catalogue_commit(commit: dict) -> None:
added = _collapse_airflow_providers(added)
if not (added or removed or changes.removed_files):
return
+ if await _committed_by_atr(commit.get("id")):
+ return
date = _commit_date(commit)
# Resolve against ATR's projects in a read session first, so decomposition and
# project lookups stay out of the storage writer
@@ -172,6 +173,19 @@ def _commit_date(commit: dict) -> datetime.datetime:
return datetime.datetime.now(datetime.UTC)
+async def _committed_by_atr(revision: object) -> bool:
+ # Our own publishes carry the asf:tool=atr revprop; a failed read catalogues rather than drops,
+ # since a duplicate release is recoverable and a missed one isn't
+ url = config.get().SVN_PUBLISH_URL
+ if (url is None) or (revision is None):
+ return False
+ try:
+ return await svn.committed_by_atr(url, str(revision))
+ except Exception:
+ log.exception(f"dist watcher could not read asf:tool for r{revision}; treating it as external")
+ return False
+
+
def _companion(siblings: set[str], artifact: str, suffixes: tuple[str, ...]) -> str | None:
for suffix in suffixes:
if (artifact + suffix) in siblings:
diff --git a/tests/unit/test_dist_watcher_removals.py b/tests/unit/test_dist_watcher_removals.py
index be4aaf39..19f3e8e9 100644
--- a/tests/unit/test_dist_watcher_removals.py
+++ b/tests/unit/test_dist_watcher_removals.py
@@ -147,3 +147,29 @@ async def test_the_same_release_is_only_archived_once_across_several_deleted_sou
removed_files=[("foo/1.2.0", "foo-1.2.0-src.tar.gz"), ("foo/1.2.0", "foo-1.2.0-src.zip")],
)
assert len(archives) == 1
+
+
+# ATR's own publishes carry the asf:tool=atr revprop, so the watcher skips them rather than
+# re-cataloguing a managed release the publish already recorded
+
+
+@pytest.mark.asyncio
+async def test_a_commit_with_no_revision_is_treated_as_external(monkeypatch) -> None:
+ # Nothing to look a revprop up against, so it can't be recognised as ours - catalogue it
+ monkeypatch.setattr(catalog.config, "get", lambda: SimpleNamespace(SVN_PUBLISH_URL="https://dist.example/release"))
+ assert await catalog._committed_by_atr(None) is False
+
+
+@pytest.mark.asyncio
+async def test_a_commit_carrying_the_atr_tool_revprop_is_recognised_as_ours(monkeypatch) -> None:
+ monkeypatch.setattr(catalog.config, "get", lambda: SimpleNamespace(SVN_PUBLISH_URL="https://dist.example/release"))
+ monkeypatch.setattr(catalog.svn, "committed_by_atr", mock.AsyncMock(return_value=True))
+ assert await catalog._committed_by_atr(87431) is True
+
+
+@pytest.mark.asyncio
+async def test_a_revprop_read_failure_catalogues_rather_than_drops(monkeypatch) -> None:
+ # A dropped release can't be recovered but a duplicate can, so a failed lookup falls to cataloguing
+ monkeypatch.setattr(catalog.config, "get", lambda: SimpleNamespace(SVN_PUBLISH_URL="https://dist.example/release"))
+ monkeypatch.setattr(catalog.svn, "committed_by_atr", mock.AsyncMock(side_effect=RuntimeError("svn unreachable")))
+ assert await catalog._committed_by_atr(87431) is False
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tooling.apache.org
For additional commands, e-mail: commits-help@tooling.apache.org