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/07/31 15:42:31 UTC

(tooling-trusted-releases) branch arm updated (78150019 -> 1afac7f8)

This is an automated email from the ASF dual-hosted git repository.

alitheg pushed a change to branch arm
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git


 discard 78150019 #1479 - record vote thread ID and present in the catalog
     add a0a5b64d Add a database table to record PubSub event processing failures
     new 1afac7f8 #1479 - record vote thread ID and present in the catalog

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (78150019)
            \
             N -- N -- N   refs/heads/arm (1afac7f8)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 atr/db/__init__.py                                 | 14 ++++++++++++
 atr/models/sql.py                                  | 14 ++++++++++++
 ....10_805ec889.py => 0115_2026.07.31_7169d961.py} | 25 +++++++++++-----------
 ....31_f6b39be8.py => 0116_2026.07.31_f6b39be8.py} |  8 +++----
 4 files changed, 45 insertions(+), 16 deletions(-)
 copy migrations/versions/{0106_2026.07.10_805ec889.py => 0115_2026.07.31_7169d961.py} (65%)
 rename migrations/versions/{0115_2026.07.31_f6b39be8.py => 0116_2026.07.31_f6b39be8.py} (89%)


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tooling.apache.org
For additional commands, e-mail: commits-help@tooling.apache.org


(tooling-trusted-releases) 01/01: #1479 - record vote thread ID and present in the catalog

Posted by ar...@apache.org.
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

commit 1afac7f8b0a4665253b4d96f039aff772393b3e6
Author: Alastair McFarlane <ar...@apache.org>
AuthorDate: Fri Jul 31 16:40:25 2026 +0100

    #1479 - record vote thread ID and present in the catalog
---
 atr/models/api.py                               |  2 ++
 atr/models/sql.py                               |  3 ++
 atr/shared/catalog.py                           |  1 +
 atr/storage/writers/vote.py                     | 28 +++++++++++-----
 atr/templates/_catalog_version.html             |  4 +++
 atr/templates/catalog_site/release.html         |  4 +++
 migrations/versions/0116_2026.07.31_f6b39be8.py | 44 +++++++++++++++++++++++++
 7 files changed, 77 insertions(+), 9 deletions(-)

diff --git a/atr/models/api.py b/atr/models/api.py
index f472310b..42de1878 100644
--- a/atr/models/api.py
+++ b/atr/models/api.py
@@ -69,6 +69,8 @@ class CatalogVersion(schema.Strict):
     cycle: str | None
     # The per-version CLE feed, or None when no released-phase record backs this version.
     cle_url: str | None = None
+    # The lists.apache.org vote thread, present only for releases voted through ATR.
+    vote_thread_url: str | None = None
     artifacts: Sequence[CatalogArtifact]
 
     @pydantic.field_validator("released", mode="before")
diff --git a/atr/models/sql.py b/atr/models/sql.py
index d69de170..e76a5d18 100644
--- a/atr/models/sql.py
+++ b/atr/models/sql.py
@@ -1511,6 +1511,9 @@ class Release(sqlmodel.SQLModel, table=True):
         **example(datetime.datetime(2025, 5, 7, 1, 2, 3, tzinfo=datetime.UTC)),
     )
     podling_thread_id: str | None = sqlmodel.Field(default=None, **example("hmk1lpwnnxn5zsbp8gwh7115h2qm7jrh"))
+    vote_thread_url: str | None = sqlmodel.Field(
+        default=None, **example("https://lists.apache.org/thread/hmk1lpwnnxn5zsbp8gwh7115h2qm7jrh")
+    )
 
     # 1-M: Release -C-> [Revision]
     # M-1: Revision -> Release
diff --git a/atr/shared/catalog.py b/atr/shared/catalog.py
index e6865d29..b699f227 100644
--- a/atr/shared/catalog.py
+++ b/atr/shared/catalog.py
@@ -229,6 +229,7 @@ def _versions(
                 cle_url=cle_release_url(atr_host, release.project_key, str(version))
                 if (atr_host is not None) and cle_eligible and (release is not None)
                 else None,
+                vote_thread_url=release.vote_thread_url if (release is not None) else None,
                 artifacts=[_artifact(row, downloadable, status == "archived") for row in rows],
             )
         )
diff --git a/atr/storage/writers/vote.py b/atr/storage/writers/vote.py
index a8abacf6..5080d299 100644
--- a/atr/storage/writers/vote.py
+++ b/atr/storage/writers/vote.py
@@ -540,12 +540,9 @@ class ReleaseManager(CommitteeParticipant):
                 and (release.podling_thread_id is None)
             ):
                 latest_vote_task = await interaction.release_current_vote_task(release, self.__data)
-                if latest_vote_task is not None:
-                    task_mid = interaction.task_mid_get(latest_vote_task)
-                    task_recipient = interaction.task_recipient_get(latest_vote_task)
-                    archive_url = await self.__write_as.cache.get_message_archive_url(task_mid, task_recipient)
-                    if archive_url is not None:
-                        podling_round_one_thread_id = archive_url.split("/")[-1]
+                archive_url = await self.__resolved_vote_thread_url(latest_vote_task)
+                if archive_url is not None:
+                    podling_round_one_thread_id = archive_url.split("/")[-1]
             await self.__data.rollback()
             return await self._resolve_trusted(
                 project_key,
@@ -711,9 +708,7 @@ class ReleaseManager(CommitteeParticipant):
                 # We only set the podling_thread_id to the thread_id of the vote thread
                 # Then we automatically start the Incubator PMC vote
                 # TODO: Note on the resolve vote page that resolving the Project PPMC vote starts the Incubator PMC vote
-                task_mid = interaction.task_mid_get(latest_vote_task)
-                task_recipient = interaction.task_recipient_get(latest_vote_task)
-                archive_url = await self.__write_as.cache.get_message_archive_url(task_mid, task_recipient)
+                archive_url = await self.__resolved_vote_thread_url(latest_vote_task)
                 if archive_url is None:
                     raise storage.AccessError("No archive URL found for podling vote", status=404)
                 thread_id = archive_url.split("/")[-1]
@@ -795,6 +790,7 @@ class ReleaseManager(CommitteeParticipant):
                 f" (sent to {incubator_vote_address})"
             )
         elif vote_result == "passed":
+            vote_thread_url = await self.__resolved_vote_thread_url(latest_vote_task)
             await self._resolve_transition(
                 release,
                 expected_phase=sql.ReleasePhase.RELEASE_CANDIDATE,
@@ -803,6 +799,7 @@ class ReleaseManager(CommitteeParticipant):
                 new_vote_mode=release.effective_vote_mode,
                 new_vote_resolved=datetime.datetime.now(datetime.UTC),
                 new_podling_thread_id=release.podling_thread_id,
+                new_vote_thread_url=vote_thread_url,
             )
             await self.__data.commit()
             await self.__data.refresh(release)
@@ -1111,6 +1108,7 @@ class ReleaseManager(CommitteeParticipant):
                     f" (sent to {incubator_vote_address})"
                 )
             elif vote_result == "passed":
+                vote_thread_url = await self.__resolved_vote_thread_url(latest_vote_task)
                 await self._resolve_transition(
                     release,
                     expected_phase=sql.ReleasePhase.RELEASE_CANDIDATE,
@@ -1119,6 +1117,7 @@ class ReleaseManager(CommitteeParticipant):
                     new_vote_mode=release.effective_vote_mode,
                     new_vote_resolved=datetime.datetime.now(datetime.UTC),
                     new_podling_thread_id=release.podling_thread_id,
+                    new_vote_thread_url=vote_thread_url,
                 )
                 await self.__data.commit()
                 await self.__data.refresh(release)
@@ -1201,6 +1200,15 @@ class ReleaseManager(CommitteeParticipant):
         )
         return release, voting_round, success_message, error_message
 
+    async def __resolved_vote_thread_url(self, vote_task: sql.Task | None) -> str | None:
+        # The thread only becomes linkable once its vote email is archived. The lookup is cached
+        # and best effort, so a thread that isn't archived yet just leaves the release unlinked.
+        if vote_task is None:
+            return None
+        task_mid = interaction.task_mid_get(vote_task)
+        task_recipient = interaction.task_recipient_get(vote_task)
+        return await self.__write_as.cache.get_message_archive_url(task_mid, task_recipient)
+
     async def _resolve_transition(
         self,
         release: sql.Release,
@@ -1211,6 +1219,7 @@ class ReleaseManager(CommitteeParticipant):
         new_vote_mode: sql.VoteMode | None,
         new_vote_resolved: datetime.datetime | None,
         new_podling_thread_id: str | None,
+        new_vote_thread_url: str | None = None,
     ) -> None:
         via = sql.validate_instrumented_attribute
         stmt = sqlmodel.update(sql.Release).where(
@@ -1237,6 +1246,7 @@ class ReleaseManager(CommitteeParticipant):
                 vote_mode=new_vote_mode,
                 vote_resolved=new_vote_resolved,
                 podling_thread_id=new_podling_thread_id,
+                vote_thread_url=new_vote_thread_url,
                 activity_at=activity_at_value,
                 inactivity_notice_key=notice_key_value,
             )
diff --git a/atr/templates/_catalog_version.html b/atr/templates/_catalog_version.html
index 336753e3..3254d0a1 100644
--- a/atr/templates/_catalog_version.html
+++ b/atr/templates/_catalog_version.html
@@ -11,6 +11,10 @@
         <strong class="fs-5">{{ version.version }}</strong>
         <span>
           <span class="badge {{ status_classes[version.status] }}">{{ version.status | capitalize }}</span>
+          {% if version.vote_thread_url %}
+            <a href="{{ version.vote_thread_url }}" target="_blank" rel="noopener"
+               class="badge rounded-pill bg-secondary-subtle text-secondary-emphasis border border-secondary-subtle text-decoration-none ms-2">Vote</a>
+          {% endif %}
           {% if version.released %}
             <a class="btn btn-sm btn-outline-secondary ms-2"
                href="{{ as_url(api.cle_release, project_key=project.key, version_key=version.version) }}">CLE</a>
diff --git a/atr/templates/catalog_site/release.html b/atr/templates/catalog_site/release.html
index f86304aa..b571edae 100644
--- a/atr/templates/catalog_site/release.html
+++ b/atr/templates/catalog_site/release.html
@@ -18,6 +18,10 @@
         <span class="d-flex align-items-center gap-2">
           <a href="artifacts.json"
              class="badge rounded-pill bg-secondary-subtle text-secondary-emphasis border border-secondary-subtle text-decoration-none">Metadata</a>
+          {% if release_version.vote_thread_url %}
+            <a href="{{ release_version.vote_thread_url }}" target="_blank" rel="noopener"
+               class="badge rounded-pill bg-secondary-subtle text-secondary-emphasis border border-secondary-subtle text-decoration-none">Vote</a>
+          {% endif %}
           <span class="badge {{ status_classes[release_version.status] }}">{{ release_version.status | capitalize }}</span>
         </span>
       </div>
diff --git a/migrations/versions/0116_2026.07.31_f6b39be8.py b/migrations/versions/0116_2026.07.31_f6b39be8.py
new file mode 100644
index 00000000..7010f4a5
--- /dev/null
+++ b/migrations/versions/0116_2026.07.31_f6b39be8.py
@@ -0,0 +1,44 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+"""Add the vote thread URL to releases
+
+Revision ID: 0116_2026.07.31_f6b39be8
+Revises: 0115_2026.07.31_7169d961
+Create Date: 2026-07-31 15:18:34.624854+00:00
+"""
+
+from collections.abc import Sequence
+
+import sqlalchemy as sa
+from alembic import op
+
+# Revision identifiers, used by Alembic
+revision: str = "0116_2026.07.31_f6b39be8"
+down_revision: str | None = "0115_2026.07.31_7169d961"
+branch_labels: str | Sequence[str] | None = None
+depends_on: str | Sequence[str] | None = None
+
+
+def upgrade() -> None:
+    with op.batch_alter_table("release", schema=None) as batch_op:
+        batch_op.add_column(sa.Column("vote_thread_url", sa.String(), nullable=True))
+
+
+def downgrade() -> None:
+    with op.batch_alter_table("release", schema=None) as batch_op:
+        batch_op.drop_column("vote_thread_url")


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tooling.apache.org
For additional commands, e-mail: commits-help@tooling.apache.org