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 71d9c38  Make phase routes redirect to the correct phase if necessary
71d9c38 is described below

commit 71d9c38b62e12930c349b1005f3400f8912cf1dd
Author: Sean B. Palmer <[email protected]>
AuthorDate: Thu Sep 11 19:08:50 2025 +0100

    Make phase routes redirect to the correct phase if necessary
---
 atr/routes/compose.py                             | 12 ++++-
 atr/routes/finish.py                              | 12 ++++-
 atr/routes/mapping.py                             | 30 ++++++++---
 atr/routes/vote.py                                | 19 ++++---
 atr/templates/check-selected-candidate-forms.html | 66 ++++++++++++-----------
 atr/templates/check-selected-release-info.html    |  2 +-
 6 files changed, 92 insertions(+), 49 deletions(-)

diff --git a/atr/routes/compose.py b/atr/routes/compose.py
index c93436d..b147808 100644
--- a/atr/routes/compose.py
+++ b/atr/routes/compose.py
@@ -17,6 +17,7 @@
 
 from typing import TYPE_CHECKING
 
+import asfquart.base as base
 import werkzeug.wrappers.response as response
 import wtforms
 
@@ -28,6 +29,7 @@ import atr.models.sql as sql
 import atr.revision as revision
 import atr.routes as routes
 import atr.routes.draft as draft
+import atr.routes.mapping as mapping
 import atr.storage as storage
 import atr.template as template
 import atr.util as util
@@ -122,7 +124,15 @@ async def selected(session: routes.CommitterSession, 
project_name: str, version_
     """Show the contents of the release candidate draft."""
     await session.check_access(project_name)
 
-    release = await session.release(project_name, version_name, 
with_committee=True, with_project_release_policy=True)
+    async with db.session() as data:
+        release = await data.release(
+            project_name=project_name,
+            version=version_name,
+            _committee=True,
+            _project_release_policy=True,
+        ).demand(base.ASFQuartException("Release does not exist", 
errorcode=404))
+    if release.phase != sql.ReleasePhase.RELEASE_CANDIDATE_DRAFT:
+        return await mapping.release_as_redirect(session, release)
     return await check(session, release)
 
 
diff --git a/atr/routes/finish.py b/atr/routes/finish.py
index f774e36..2b8bf3a 100644
--- a/atr/routes/finish.py
+++ b/atr/routes/finish.py
@@ -21,6 +21,7 @@ from collections.abc import Awaitable, Callable
 from typing import Any, Final
 
 import aiofiles.os
+import asfquart.base as base
 import quart
 import quart.wrappers.response as quart_response
 import werkzeug.datastructures as datastructures
@@ -35,6 +36,7 @@ import atr.log as log
 import atr.models.sql as sql
 import atr.revision as revision
 import atr.routes as routes
+import atr.routes.mapping as mapping
 import atr.routes.root as root
 import atr.template as template
 import atr.util as util
@@ -126,8 +128,14 @@ async def selected(
         return await session.redirect(selected, project_name=project_name, 
version_name=version_name)
 
     async with db.session() as data:
-        release = await session.release(project_name, version_name, 
phase=sql.ReleasePhase.RELEASE_PREVIEW, data=data)
-        user_ssh_keys = await data.ssh_key(asf_uid=session.uid).all()
+        release = await data.release(
+            project_name=project_name,
+            version=version_name,
+            _committee=True,
+        ).demand(base.ASFQuartException("Release does not exist", 
errorcode=404))
+    if release.phase != sql.ReleasePhase.RELEASE_PREVIEW:
+        return await mapping.release_as_redirect(session, release)
+    user_ssh_keys = await data.ssh_key(asf_uid=session.uid).all()
 
     latest_revision_dir = util.release_directory(release)
     try:
diff --git a/atr/routes/mapping.py b/atr/routes/mapping.py
index acfa23c..3f96462 100644
--- a/atr/routes/mapping.py
+++ b/atr/routes/mapping.py
@@ -15,7 +15,12 @@
 # specific language governing permissions and limitations
 # under the License.
 
+from collections.abc import Callable
+
+import werkzeug.wrappers.response as response
+
 import atr.models.sql as sql
+import atr.routes as routes
 import atr.routes.compose as compose
 import atr.routes.finish as finish
 import atr.routes.release as routes_release
@@ -23,14 +28,27 @@ import atr.routes.vote as vote
 import atr.util as util
 
 
-def release_as_url(release: sql.Release) -> str:
+async def release_as_redirect(session: routes.CommitterSession, release: 
sql.Release) -> response.Response:
+    route = release_as_route(release)
+    if route is routes_release.finished:
+        return await session.redirect(route, project_name=release.project.name)
+    return await session.redirect(route, project_name=release.project.name, 
version_name=release.version)
+
+
+def release_as_route(release: sql.Release) -> Callable:
     match release.phase:
         case sql.ReleasePhase.RELEASE_CANDIDATE_DRAFT:
-            return util.as_url(compose.selected, 
project_name=release.project.name, version_name=release.version)
+            return compose.selected
         case sql.ReleasePhase.RELEASE_CANDIDATE:
-            return util.as_url(vote.selected, 
project_name=release.project.name, version_name=release.version)
+            return vote.selected
         case sql.ReleasePhase.RELEASE_PREVIEW:
-            return util.as_url(finish.selected, 
project_name=release.project.name, version_name=release.version)
+            return finish.selected
         case sql.ReleasePhase.RELEASE:
-            finished = routes_release.finished  # type: ignore[has-type]
-            return util.as_url(finished, project_name=release.project.name)
+            return routes_release.finished
+
+
+def release_as_url(release: sql.Release) -> str:
+    route = release_as_route(release)
+    if route is routes_release.finished:
+        return util.as_url(route, project_name=release.project.name)
+    return util.as_url(route, project_name=release.project.name, 
version_name=release.version)
diff --git a/atr/routes/vote.py b/atr/routes/vote.py
index cb1ce7d..b9d685a 100644
--- a/atr/routes/vote.py
+++ b/atr/routes/vote.py
@@ -15,9 +15,11 @@
 # specific language governing permissions and limitations
 # under the License.
 
+import asfquart.base as base
 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.log as log
@@ -25,6 +27,7 @@ import atr.models.results as results
 import atr.models.sql as sql
 import atr.routes as routes
 import atr.routes.compose as compose
+import atr.routes.mapping as mapping
 import atr.storage as storage
 import atr.util as util
 
@@ -42,13 +45,15 @@ async def selected(session: routes.CommitterSession, 
project_name: str, version_
     """Show the contents of the release candidate draft."""
     await session.check_access(project_name)
 
-    release = await session.release(
-        project_name,
-        version_name,
-        with_committee=True,
-        phase=sql.ReleasePhase.RELEASE_CANDIDATE,
-        with_project_release_policy=True,
-    )
+    async with db.session() as data:
+        release = await data.release(
+            project_name=project_name,
+            version=version_name,
+            _committee=True,
+            _project_release_policy=True,
+        ).demand(base.ASFQuartException("Release does not exist", 
errorcode=404))
+    if release.phase != sql.ReleasePhase.RELEASE_CANDIDATE:
+        return await mapping.release_as_redirect(session, release)
     latest_vote_task = await interaction.release_latest_vote_task(release)
     archive_url = None
     task_mid = None
diff --git a/atr/templates/check-selected-candidate-forms.html 
b/atr/templates/check-selected-candidate-forms.html
index c4de407..17ca5fd 100644
--- a/atr/templates/check-selected-candidate-forms.html
+++ b/atr/templates/check-selected-candidate-forms.html
@@ -3,40 +3,42 @@
 
 {% endif %}
 
-<h2>Cast your vote</h2>
+{% if form %}
+  <h2>Cast your vote</h2>
 
-<form method="post"
-      action="{{ as_url(routes.vote.selected_post, project_name=project_name, 
version_name=version_name) }}"
-      class="atr-canary py-4 px-5 mb-4 border rounded">
-  {{ form.hidden_tag() }}
+  <form method="post"
+        action="{{ as_url(routes.vote.selected_post, 
project_name=project_name, version_name=version_name) }}"
+        class="atr-canary py-4 px-5 mb-4 border rounded">
+    {{ form.hidden_tag() }}
 
-  <div class="row mb-3 pb-3 border-bottom">
-    {{ forms.label(form.vote_value, col="md3") }}
-    <div class="col-md-9">
-      <div class="btn-group" role="group" aria-label="Vote options">
-        {% for subfield in form.vote_value %}
-          {% set btn_class = "btn-outline-secondary" %}
-          {% if subfield.data == "+1" %}
-            {% set btn_class = "btn-outline-success" %}
-          {% endif %}
-          {% if subfield.data == "-1" %}
-            {% set btn_class = "btn-outline-danger" %}
-          {% endif %}
-          {{ forms.widget(subfield, classes="btn-check", autocomplete="off") }}
-          {{ forms.label(subfield, classes="btn " + btn_class) }}
-        {% endfor %}
+    <div class="row mb-3 pb-3 border-bottom">
+      {{ forms.label(form.vote_value, col="md3") }}
+      <div class="col-md-9">
+        <div class="btn-group" role="group" aria-label="Vote options">
+          {% for subfield in form.vote_value %}
+            {% set btn_class = "btn-outline-secondary" %}
+            {% if subfield.data == "+1" %}
+              {% set btn_class = "btn-outline-success" %}
+            {% endif %}
+            {% if subfield.data == "-1" %}
+              {% set btn_class = "btn-outline-danger" %}
+            {% endif %}
+            {{ forms.widget(subfield, classes="btn-check", autocomplete="off") 
}}
+            {{ forms.label(subfield, classes="btn " + btn_class) }}
+          {% endfor %}
+        </div>
+        {{ forms.errors(form.vote_value, classes="text-danger small mt-1") }}
       </div>
-      {{ forms.errors(form.vote_value, classes="text-danger small mt-1") }}
     </div>
-  </div>
-  <div class="row mb-3">
-    {{ forms.label(form.vote_comment, col="md3") }}
-    <div class="col-md-9">
-      {{ forms.widget(form.vote_comment, rows="3") }}
-      {{ forms.errors(form.vote_comment, classes="text-danger small mt-1") }}
+    <div class="row mb-3">
+      {{ forms.label(form.vote_comment, col="md3") }}
+      <div class="col-md-9">
+        {{ forms.widget(form.vote_comment, rows="3") }}
+        {{ forms.errors(form.vote_comment, classes="text-danger small mt-1") }}
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-9 offset-md-3">{{ form.submit(class_="btn 
btn-primary") }}</div>
     </div>
-  </div>
-  <div class="row">
-    <div class="col-md-9 offset-md-3">{{ form.submit(class_="btn btn-primary") 
}}</div>
-  </div>
-</form>
+  </form>
+{% endif %}
diff --git a/atr/templates/check-selected-release-info.html 
b/atr/templates/check-selected-release-info.html
index 957d4fc..cc93c84 100644
--- a/atr/templates/check-selected-release-info.html
+++ b/atr/templates/check-selected-release-info.html
@@ -86,7 +86,7 @@
         {% if release.vote_manual %}
           <a href="{{ as_url(routes.resolve.manual_selected, 
project_name=release.project.name, version_name=release.version) }}"
              class="btn btn-success"><i class="bi bi-clipboard-check 
me-1"></i> Resolve vote</a>
-        {% else %}
+        {% elif hidden_form %}
           <form action="{{ as_url(routes.resolve.tabulated_selected_post, 
project_name=release.project.name, version_name=release.version) }}"
                 method="post"
                 class="mb-0">


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

Reply via email to