This is an automated email from the ASF dual-hosted git repository.
sbp pushed a commit to branch sbp
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git
The following commit(s) were added to refs/heads/sbp by this push:
new 83eeaf2a Record 3-way merge metadata on revisions
83eeaf2a is described below
commit 83eeaf2ab5846db68691a0ea2da6801990bb4a0b
Author: Sean B. Palmer <[email protected]>
AuthorDate: Fri Mar 6 15:30:37 2026 +0000
Record 3-way merge metadata on revisions
---
atr/models/sql.py | 1 +
atr/storage/writers/revision.py | 19 +++++++++++++----
migrations/versions/0055_2026.03.06_522f2417.py | 27 +++++++++++++++++++++++++
tests/unit/test_create_revision.py | 2 ++
tests/unit/test_create_revision_quarantine.py | 4 +++-
5 files changed, 48 insertions(+), 5 deletions(-)
diff --git a/atr/models/sql.py b/atr/models/sql.py
index cbb875b8..07625b6e 100644
--- a/atr/models/sql.py
+++ b/atr/models/sql.py
@@ -1287,6 +1287,7 @@ class Revision(sqlmodel.SQLModel, table=True):
child: Optional["Revision"] =
sqlmodel.Relationship(back_populates="parent")
description: str | None = sqlmodel.Field(default=None, **example("This is
a description"))
+ merge_base_revision_name: str | None = sqlmodel.Field(default=None,
**example("example-0.0.1 00001"))
tag: str | None = sqlmodel.Field(default=None, **example("rc1"))
was_quarantined: bool = sqlmodel.Field(default=False, **example(False))
diff --git a/atr/storage/writers/revision.py b/atr/storage/writers/revision.py
index e3bcde32..05d2a4cf 100644
--- a/atr/storage/writers/revision.py
+++ b/atr/storage/writers/revision.py
@@ -91,7 +91,7 @@ async def finalise_revision(
was_quarantined: bool = False,
) -> sql.Revision:
try:
- previous_attestable, _, merged_release = await _lock_and_merge(
+ previous_attestable, merge_base_revision_name, _, merged_release =
await _lock_and_merge(
data,
base_hashes=base_hashes,
base_inodes=base_inodes,
@@ -115,6 +115,7 @@ async def finalise_revision(
data,
asf_uid=asf_uid,
description=description,
+ merge_base_revision_name=merge_base_revision_name,
path_to_hash=path_to_hash,
path_to_size=path_to_size,
previous_attestable=previous_attestable,
@@ -132,6 +133,7 @@ async def _commit_new_revision(
*,
asf_uid: str,
description: str | None,
+ merge_base_revision_name: str | None,
path_to_hash: dict[str, str],
path_to_size: dict[str, int],
previous_attestable: atr.models.attestable.AttestableV1 | None,
@@ -154,6 +156,7 @@ async def _commit_new_revision(
created=datetime.datetime.now(datetime.UTC),
phase=release.phase,
description=description,
+ merge_base_revision_name=merge_base_revision_name,
was_quarantined=was_quarantined,
)
data.add(new_revision)
@@ -243,7 +246,7 @@ async def _lock_and_merge(
_release_name: str,
temp_dir_path: pathlib.Path,
version_name: str,
-) -> tuple[atr.models.attestable.AttestableV1 | None, str | None, sql.Release]:
+) -> tuple[atr.models.attestable.AttestableV1 | None, str | None, str | None,
sql.Release]:
# Acquire the write lock
# We need this write lock for moving the directory afterwards atomically
# But it also helps to make models.populate_revision_sequence_and_name
safe against races
@@ -255,12 +258,14 @@ async def _lock_and_merge(
prior_revision_name = latest.name if latest else None
# Merge with the prior revision if there was an intervening change
+ merge_base_revision_name: str | None = None
if (
merge_enabled
and (old_revision is not None)
and (prior_revision_name is not None)
and (prior_revision_name != old_revision.name)
):
+ merge_base_revision_name = prior_revision_name
prior_number = prior_revision_name.split()[-1]
prior_dir = paths.release_directory_base(merged_release) / prior_number
await merge.merge(
@@ -277,7 +282,7 @@ async def _lock_and_merge(
)
previous_attestable = await attestable.load(project_name,
version_name, prior_number)
- return previous_attestable, prior_revision_name, merged_release
+ return previous_attestable, merge_base_revision_name, prior_revision_name,
merged_release
class GeneralPublic:
@@ -417,7 +422,12 @@ class CommitteeParticipant(FoundationCommitter):
async with SafeSession(temp_dir) as data:
try:
- previous_attestable, prior_revision_name, merged_release =
await _lock_and_merge(
+ (
+ previous_attestable,
+ merge_base_revision_name,
+ prior_revision_name,
+ merged_release,
+ ) = await _lock_and_merge(
data,
base_hashes=base_hashes,
base_inodes=base_inodes,
@@ -458,6 +468,7 @@ class CommitteeParticipant(FoundationCommitter):
data,
asf_uid=asf_uid,
description=description,
+ merge_base_revision_name=merge_base_revision_name,
path_to_hash=path_to_hash,
path_to_size=path_to_size,
previous_attestable=previous_attestable,
diff --git a/migrations/versions/0055_2026.03.06_522f2417.py
b/migrations/versions/0055_2026.03.06_522f2417.py
new file mode 100644
index 00000000..6eff6918
--- /dev/null
+++ b/migrations/versions/0055_2026.03.06_522f2417.py
@@ -0,0 +1,27 @@
+"""Add 3-way merge metadata to revisions
+
+Revision ID: 0055_2026.03.06_522f2417
+Revises: 0054_2026.03.02_3799e8e6
+Create Date: 2026-03-06 15:22:14.422556+00:00
+"""
+
+from collections.abc import Sequence
+
+import sqlalchemy as sa
+from alembic import op
+
+# Revision identifiers, used by Alembic
+revision: str = "0055_2026.03.06_522f2417"
+down_revision: str | None = "0054_2026.03.02_3799e8e6"
+branch_labels: str | Sequence[str] | None = None
+depends_on: str | Sequence[str] | None = None
+
+
+def upgrade() -> None:
+ with op.batch_alter_table("revision", schema=None) as batch_op:
+ batch_op.add_column(sa.Column("merge_base_revision_name",
sa.VARCHAR(), nullable=True))
+
+
+def downgrade() -> None:
+ with op.batch_alter_table("revision", schema=None) as batch_op:
+ batch_op.drop_column("merge_base_revision_name")
diff --git a/tests/unit/test_create_revision.py
b/tests/unit/test_create_revision.py
index 9b1ec01f..8a94458d 100644
--- a/tests/unit/test_create_revision.py
+++ b/tests/unit/test_create_revision.py
@@ -43,11 +43,13 @@ class FakeRevision:
created: object,
phase: sql.ReleasePhase,
description: str | None,
+ merge_base_revision_name: str | None = None,
was_quarantined: bool = False,
):
self.asfuid = asfuid
self.created = created
self.description = description
+ self.merge_base_revision_name = merge_base_revision_name
self.name = ""
self.number = ""
self.parent_name: str | None = None
diff --git a/tests/unit/test_create_revision_quarantine.py
b/tests/unit/test_create_revision_quarantine.py
index 33342922..3ece74b8 100644
--- a/tests/unit/test_create_revision_quarantine.py
+++ b/tests/unit/test_create_revision_quarantine.py
@@ -137,7 +137,9 @@ async def
test_no_quarantine_returns_revision_when_no_archives(tmp_path: pathlib
mock.patch.object(revision.detection,
"detect_archives_requiring_quarantine", return_value=[]),
mock.patch.object(revision.interaction, "latest_revision",
new_callable=mock.AsyncMock, return_value=None),
mock.patch.object(revision, "_commit_new_revision",
new_callable=mock.AsyncMock, return_value=fake_revision),
- mock.patch.object(revision, "_lock_and_merge",
new_callable=mock.AsyncMock, return_value=(None, None, release)),
+ mock.patch.object(
+ revision, "_lock_and_merge", new_callable=mock.AsyncMock,
return_value=(None, None, None, release)
+ ),
mock.patch.object(revision, "SafeSession",
return_value=MockQuarantineSession(MockQuarantineData(None))),
mock.patch.object(revision.paths, "get_tmp_dir",
return_value=tmp_path),
mock.patch.object(revision.util, "chmod_directories"),
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]