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 38cb18e  Allow manual votes to be resolved
38cb18e is described below

commit 38cb18ed65caa38840114ee67362398f9e83b395
Author: Sean B. Palmer <[email protected]>
AuthorDate: Wed Jul 2 16:17:37 2025 +0100

    Allow manual votes to be resolved
---
 atr/routes/resolve.py             | 62 +++++++++++++++++++++++++++++++++++----
 atr/templates/resolve-manual.html |  9 ------
 2 files changed, 57 insertions(+), 14 deletions(-)

diff --git a/atr/routes/resolve.py b/atr/routes/resolve.py
index 809b9b6..423b43a 100644
--- a/atr/routes/resolve.py
+++ b/atr/routes/resolve.py
@@ -52,7 +52,6 @@ class ResolveVoteForm(util.QuartFormTyped):
 class ResolveVoteManualForm(util.QuartFormTyped):
     """Form for resolving a vote manually."""
 
-    email_body = wtforms.TextAreaField("Email body", render_kw={"rows": 24})
     vote_result = wtforms.RadioField(
         "Vote result",
         choices=[("passed", "Passed"), ("failed", "Failed")],
@@ -126,11 +125,32 @@ async def manual_selected_post(
             version_name=version_name,
             error="Invalid form submission.",
         )
-    # email_body = util.unwrap(resolve_form.email_body.data)
+    vote_result = util.unwrap(resolve_form.vote_result.data)
+    vote_thread_url = util.unwrap(resolve_form.vote_thread_url.data)
+    vote_result_url = util.unwrap(resolve_form.vote_result_url.data)
+    await _committees_check(vote_thread_url, vote_result_url)
+
+    async with db.session() as data:
+        async with data.begin():
+            release = await data.merge(release)
+            if vote_result == "passed":
+                release.phase = models.ReleasePhase.RELEASE_PREVIEW
+                success_message = "Vote marked as passed"
+                description = "Create a preview revision from the last 
candidate draft"
+                async with revision.create_and_manage(
+                    project_name, release.version, session.uid, 
description=description
+                ) as _creating:
+                    pass
+            else:
+                release.phase = models.ReleasePhase.RELEASE_CANDIDATE_DRAFT
+                success_message = "Vote marked as failed"
+    if vote_result == "passed":
+        destination = finish.selected
+    else:
+        destination = compose.selected
+
     return await session.redirect(
-        manual_selected,
-        project_name=project_name,
-        version_name=version_name,
+        destination, project_name=project_name, version_name=version_name, 
success=success_message
     )
 
 
@@ -270,6 +290,38 @@ def task_mid_get(latest_vote_task: models.Task) -> str | 
None:
     return result.mid
 
 
+async def _committees_check(vote_thread_url: str, vote_result_url: str) -> 
None:
+    if not vote_thread_url.startswith("https://lists.apache.org/thread/";):
+        raise RuntimeError("Vote thread URL is not a valid Apache email thread 
URL")
+    if not vote_result_url.startswith("https://lists.apache.org/thread/";):
+        raise RuntimeError("Vote result URL is not a valid Apache email thread 
URL")
+
+    vote_thread_id = 
vote_thread_url.removeprefix("https://lists.apache.org/thread/";)
+    result_thread_id = 
vote_result_url.removeprefix("https://lists.apache.org/thread/";)
+
+    vote_committee_label = None
+    result_committee_label = None
+    async for _mid, msg in util.thread_messages(vote_thread_id):
+        if "list_raw" in msg:
+            list_raw = msg["list_raw"]
+            vote_committee_label = list_raw.split(".apache.org", 
1)[0].split(".", 1)[-1]
+            break
+
+    async for _mid, msg in util.thread_messages(result_thread_id):
+        if "list_raw" in msg:
+            list_raw = msg["list_raw"]
+            result_committee_label = list_raw.split(".apache.org", 
1)[0].split(".", 1)[-1]
+            break
+
+    if vote_committee_label != result_committee_label:
+        raise RuntimeError("Vote committee and result committee do not match")
+
+    if vote_committee_label is None:
+        raise RuntimeError("Vote committee not found")
+    if result_committee_label is None:
+        raise RuntimeError("Result committee not found")
+
+
 async def _resolve_vote(
     session: routes.CommitterSession,
     project_name: str,
diff --git a/atr/templates/resolve-manual.html 
b/atr/templates/resolve-manual.html
index ba8afd1..42cfefd 100644
--- a/atr/templates/resolve-manual.html
+++ b/atr/templates/resolve-manual.html
@@ -58,15 +58,6 @@
       </div>
     </div>
 
-    <div class="mb-3 pb-3 row border-bottom">
-      {{ forms.label(resolve_form.email_body, col="md3") }}
-      <div class="col-sm-8">
-        {{ forms.widget(resolve_form.email_body, rows="12", 
classes="form-control font-monospace") }}
-        {{ forms.errors(resolve_form.email_body, classes="invalid-feedback 
d-block") }}
-        {{ forms.description(resolve_form.email_body) }}
-      </div>
-    </div>
-
     <div class="row">
       <div class="col-sm-9 offset-sm-3">{{ forms.widget(resolve_form.submit, 
classes="btn btn-primary") }}</div>
     </div>


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

Reply via email to