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

sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-release.git


The following commit(s) were added to refs/heads/main by this push:
     new be99d8a  Make the vote resolution interface easier to use in the API
be99d8a is described below

commit be99d8a1a468232451005037e3f8ddd0217534d9
Author: Sean B. Palmer <[email protected]>
AuthorDate: Fri Sep 5 17:09:26 2025 +0100

    Make the vote resolution interface easier to use in the API
---
 atr/routes/resolve.py       | 31 ++++---------------------------
 atr/storage/__init__.py     |  9 +++++++++
 atr/storage/writers/vote.py | 44 ++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 55 insertions(+), 29 deletions(-)

diff --git a/atr/routes/resolve.py b/atr/routes/resolve.py
index 95eeb5c..bb9a864 100644
--- a/atr/routes/resolve.py
+++ b/atr/routes/resolve.py
@@ -20,7 +20,6 @@ import quart
 import werkzeug.wrappers.response as response
 
 import atr.db as db
-import atr.db.interaction as interaction
 import atr.forms as forms
 import atr.models.sql as sql
 import atr.revision as revision
@@ -144,22 +143,6 @@ async def submit_selected(
     """Resolve a vote."""
     await session.check_access(project_name)
 
-    release = await session.release(
-        project_name,
-        version_name,
-        with_project=True,
-        with_committee=True,
-        phase=sql.ReleasePhase.RELEASE_CANDIDATE,
-    )
-
-    is_podling = False
-    if release.project.committee is not None:
-        is_podling = release.project.committee.is_podling
-    podling_thread_id = release.podling_thread_id
-
-    latest_vote_task = await interaction.release_latest_vote_task(release)
-    if latest_vote_task is None:
-        raise RuntimeError("No vote task found, unable to send resolution 
message.")
     resolve_form = await ResolveVoteForm.create_form()
     if not (await resolve_form.validate_on_submit()):
         # TODO: Render the page again with errors
@@ -171,18 +154,12 @@ async def submit_selected(
         )
     email_body = util.unwrap(resolve_form.email_body.data)
     vote_result = util.unwrap(resolve_form.vote_result.data)
-    voting_round = None
-    if is_podling is True:
-        voting_round = 1 if (podling_thread_id is None) else 2
-    if release.committee is None:
-        raise ValueError("Project has no committee")
-    async with storage.write_as_committee_member(release.committee.name) as 
wacm:
-        release, success_message, error_message = await wacm.vote.resolve(
+
+    async with storage.write_as_project_committee_member(project_name) as wacm:
+        _release, voting_round, success_message, error_message = await 
wacm.vote.resolve(
             project_name,
-            release,
-            voting_round,
+            version_name,
             vote_result,
-            latest_vote_task,
             session.fullname,
             email_body,
         )
diff --git a/atr/storage/__init__.py b/atr/storage/__init__.py
index 947b001..4032a18 100644
--- a/atr/storage/__init__.py
+++ b/atr/storage/__init__.py
@@ -376,3 +376,12 @@ async def write_as_committee_member(
 ) -> AsyncGenerator[WriteAsCommitteeMember]:
     async with write(asf_uid) as w:
         yield w.as_committee_member(committee_name)
+
+
[email protected]
+async def write_as_project_committee_member(
+    project_name: str,
+    asf_uid: str | None | ArgumentNoneType = ArgumentNone,
+) -> AsyncGenerator[WriteAsCommitteeMember]:
+    async with write(asf_uid) as w:
+        yield await w.as_project_committee_member(project_name)
diff --git a/atr/storage/writers/vote.py b/atr/storage/writers/vote.py
index aa0119e..d90fbea 100644
--- a/atr/storage/writers/vote.py
+++ b/atr/storage/writers/vote.py
@@ -96,6 +96,46 @@ class CommitteeMember(CommitteeParticipant):
         self.__committee_name = committee_name
 
     async def resolve(
+        self,
+        project_name: str,
+        version_name: str,
+        vote_result: Literal["passed", "failed"],
+        asf_fullname: str,
+        resolution_body: str,
+    ) -> tuple[sql.Release, int | None, str, str | None]:
+        release = await self.__data.release(
+            name=sql.release_name(project_name, version_name),
+            phase=sql.ReleasePhase.RELEASE_CANDIDATE,
+            _project=True,
+            _committee=True,
+        ).demand(storage.AccessError("Release not found"))
+
+        is_podling = False
+        if release.project.committee is not None:
+            is_podling = release.project.committee.is_podling
+        podling_thread_id = release.podling_thread_id
+
+        latest_vote_task = await interaction.release_latest_vote_task(release)
+        if latest_vote_task is None:
+            raise RuntimeError("No vote task found, unable to send resolution 
message.")
+
+        voting_round = None
+        if is_podling is True:
+            voting_round = 1 if (podling_thread_id is None) else 2
+        if release.committee is None:
+            raise ValueError("Project has no committee")
+
+        return await self.resolve_release(
+            project_name,
+            release,
+            voting_round,
+            vote_result,
+            latest_vote_task,
+            asf_fullname,
+            resolution_body,
+        )
+
+    async def resolve_release(
         self,
         project_name: str,
         release: sql.Release,
@@ -104,7 +144,7 @@ class CommitteeMember(CommitteeParticipant):
         latest_vote_task: sql.Task,
         asf_fullname: str,
         resolution_body: str,
-    ) -> tuple[sql.Release, str, str | None]:
+    ) -> tuple[sql.Release, int | None, str, str | None]:
         # Attach the existing release to the session
         release = await self.__data.merge(release)
         # Update the release phase based on vote result
@@ -170,7 +210,7 @@ class CommitteeMember(CommitteeParticipant):
             asf_fullname=asf_fullname,
             extra_destination=extra_destination,
         )
-        return release, success_message, error_message
+        return release, voting_round, success_message, error_message
 
     async def resolve_api(self, project_name: str, version_name: str, 
resolution: Literal["passed", "failed"]) -> None:
         release_name = sql.release_name(project_name, version_name)


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to