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 9493c95  Better integrate download buttons throughout the site
9493c95 is described below

commit 9493c9547fd86fdd8b20094abe48c2974cae5fee
Author: Sean B. Palmer <[email protected]>
AuthorDate: Fri May 2 09:32:38 2025 +0100

    Better integrate download buttons throughout the site
---
 atr/db/models.py                             |  1 +
 atr/routes/announce.py                       |  2 ++
 atr/routes/mapping.py                        |  4 ++--
 atr/routes/release.py                        | 19 +++++++++++++--
 atr/templates/candidate-resolve-release.html |  4 ++--
 atr/templates/check-selected.html            |  4 ++--
 atr/templates/finish-selected.html           | 12 +++++-----
 atr/templates/index-committer.html           |  2 +-
 atr/templates/releases-completed.html        | 35 +++++++++++-----------------
 atr/templates/releases.html                  | 20 +++++++---------
 playwright/test.py                           | 17 +++++++-------
 11 files changed, 64 insertions(+), 56 deletions(-)

diff --git a/atr/db/models.py b/atr/db/models.py
index d46aba4..66791cd 100644
--- a/atr/db/models.py
+++ b/atr/db/models.py
@@ -383,6 +383,7 @@ class Release(sqlmodel.SQLModel, table=True):
     stage: ReleaseStage
     phase: ReleasePhase
     created: datetime.datetime = 
sqlmodel.Field(sa_column=sqlalchemy.Column(UTCDateTime))
+    released: datetime.datetime | None = sqlmodel.Field(default=None, 
sa_column=sqlalchemy.Column(UTCDateTime))
 
     # Many-to-one: A release belongs to one project, a project can have 
multiple releases
     project_name: str = sqlmodel.Field(foreign_key="project.name")
diff --git a/atr/routes/announce.py b/atr/routes/announce.py
index c320d47..6427723 100644
--- a/atr/routes/announce.py
+++ b/atr/routes/announce.py
@@ -15,6 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
+import datetime
 import logging
 from typing import TYPE_CHECKING, Any, Protocol
 
@@ -172,6 +173,7 @@ async def selected_post(
             # That would require moving this, and the filesystem operations, 
into a task
             release.phase = models.ReleasePhase.RELEASE
             release.revision = None
+            release.released = datetime.datetime.now(datetime.UTC)
             await data.commit()
 
             # This must come after updating the release object
diff --git a/atr/routes/mapping.py b/atr/routes/mapping.py
index 9dfc81c..b34ad66 100644
--- a/atr/routes/mapping.py
+++ b/atr/routes/mapping.py
@@ -32,5 +32,5 @@ def release_as_url(release: models.Release) -> str:
         case models.ReleasePhase.RELEASE_PREVIEW:
             return util.as_url(finish.selected, 
project_name=release.project.name, version_name=release.version)
         case models.ReleasePhase.RELEASE:
-            view = routes_release.view  # type: ignore[has-type]
-            return util.as_url(view, project_name=release.project.name, 
version_name=release.version)
+            completed = routes_release.completed  # type: ignore[has-type]
+            return util.as_url(completed, project_name=release.project.name)
diff --git a/atr/routes/release.py b/atr/routes/release.py
index 73d53f2..ade1536 100644
--- a/atr/routes/release.py
+++ b/atr/routes/release.py
@@ -17,6 +17,7 @@
 
 """release.py"""
 
+import datetime
 import logging
 import logging.handlers
 
@@ -84,8 +85,13 @@ async def completed(project_name: str) -> str:
             _committee=True,
         ).all()
 
+    def sort_releases(release: models.Release) -> datetime.datetime:
+        return release.released or release.created
+
+    releases = sorted(releases, key=sort_releases, reverse=True)
+
     return await quart.render_template(
-        "releases-completed.html", releases=releases, 
format_datetime=routes.format_datetime
+        "releases-completed.html", project=project, releases=releases, 
format_datetime=routes.format_datetime
     )
 
 
@@ -98,11 +104,20 @@ async def releases() -> str:
             stage=models.ReleaseStage.RELEASE,
             phase=models.ReleasePhase.RELEASE,
             _committee=True,
+            _project=True,
         ).all()
 
+    projects = {}
+    for release in releases:
+        if release.project.display_name not in projects:
+            projects[release.project.display_name] = (release.project, 1)
+        else:
+            projects[release.project.display_name] = (release.project, 
projects[release.project.display_name][1] + 1)
+
     return await quart.render_template(
         "releases.html",
-        releases=list(releases),
+        projects=projects,
+        releases=releases,
     )
 
 
diff --git a/atr/templates/candidate-resolve-release.html 
b/atr/templates/candidate-resolve-release.html
index 219fb91..a0a4f6e 100644
--- a/atr/templates/candidate-resolve-release.html
+++ b/atr/templates/candidate-resolve-release.html
@@ -57,10 +57,10 @@
       </div>
     </div>
     <div class="card-body">
-      <a href="{{ as_url(routes.candidate.view, 
project_name=release.project.name, version_name=release.version) }}"
-         class="btn btn-primary me-2"><i class="bi bi-eye me-1"></i> View 
files</a>
       <a href="{{ as_url(routes.download.all_selected, 
project_name=release.project.name, version_name=release.version) }}"
          class="btn btn-primary me-2"><i class="bi bi-download me-1"></i> 
Download files</a>
+      <a href="{{ as_url(routes.candidate.view, 
project_name=release.project.name, version_name=release.version) }}"
+         class="btn btn-secondary me-2"><i class="bi bi-eye me-1"></i> View 
files</a>
       <a href="{{ as_url(routes.vote.selected, 
project_name=release.project.name, version_name=release.version) }}"
          class="btn btn-success"><i class="bi bi-check-circle me-1"></i> Vote 
on release</a>
     </div>
diff --git a/atr/templates/check-selected.html 
b/atr/templates/check-selected.html
index 0aed8f0..564286d 100644
--- a/atr/templates/check-selected.html
+++ b/atr/templates/check-selected.html
@@ -98,10 +98,10 @@
     </div>
     {% if phase == "release_candidate" %}
       <div class="card-body">
-        <a href="{{ as_url(routes.candidate.view, 
project_name=release.project.name, version_name=release.version) }}"
-           class="btn btn-primary me-2"><i class="bi bi-eye me-1"></i> View 
files</a>
         <a href="{{ as_url(routes.download.all_selected, 
project_name=release.project.name, version_name=release.version) }}"
            class="btn btn-primary me-2"><i class="bi bi-download me-1"></i> 
Download files</a>
+        <a href="{{ as_url(routes.candidate.view, 
project_name=release.project.name, version_name=release.version) }}"
+           class="btn btn-secondary me-2"><i class="bi bi-eye me-1"></i> View 
files</a>
         <a href="{{ as_url(routes.resolve.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>
       </div>
diff --git a/atr/templates/finish-selected.html 
b/atr/templates/finish-selected.html
index ec9118b..e7645ed 100644
--- a/atr/templates/finish-selected.html
+++ b/atr/templates/finish-selected.html
@@ -35,18 +35,18 @@
         <span class="page-preview-meta-item">Created: {{ 
release.created.strftime("%Y-%m-%d %H:%M:%S UTC") }}</span>
       </div>
       <div>
+        <a title="Download all files"
+           href="{{ as_url(routes.download.all_selected, 
project_name=release.project.name, version_name=release.version) }}"
+           class="btn btn-primary me-2">
+          <i class="bi bi-download"></i>
+          Download all files
+        </a>
         <a title="Show files for {{ release.name }}"
            href="{{ as_url(routes.preview.view, 
project_name=release.project.name, version_name=release.version) }}"
            class="btn btn-secondary me-2">
           <i class="bi bi-archive"></i>
           Show files
         </a>
-        <a title="Download all files"
-           href="{{ as_url(routes.download.all_selected, 
project_name=release.project.name, version_name=release.version) }}"
-           class="btn btn-secondary me-2">
-          <i class="bi bi-download"></i>
-          Download all files
-        </a>
         <a title="Show revisions for {{ release.name }}"
            href="{{ as_url(routes.revisions.selected, 
project_name=release.project.name, version_name=release.version) }}"
            class="btn btn-secondary me-2">
diff --git a/atr/templates/index-committer.html 
b/atr/templates/index-committer.html
index 33ee3e5..d354aac 100644
--- a/atr/templates/index-committer.html
+++ b/atr/templates/index-committer.html
@@ -100,7 +100,7 @@
           {% if completed_releases %}
             <span class="text-muted me-2">/</span>
             <a href="{{ as_url(routes.release.completed, 
project_name=project.name) }}"
-               class="text-decoration-none">Show completed releases</a>
+               class="text-decoration-none">Completed releases</a>
           {% endif %}
         </p>
 
diff --git a/atr/templates/releases-completed.html 
b/atr/templates/releases-completed.html
index 12a48de..eadf8d1 100644
--- a/atr/templates/releases-completed.html
+++ b/atr/templates/releases-completed.html
@@ -1,32 +1,19 @@
 {% extends "layouts/base.html" %}
 
 {% block title %}
-  Completed releases of {{ project_display_name }} ~ ATR
+  Completed releases of {{ project.display_name }} ~ ATR
 {% endblock title %}
 
 {% block description %}
-  All of the completed releases of {{ project_display_name }} on ATR.
+  All of the completed releases of {{ project.display_name }} on ATR.
 {% endblock description %}
 
 {% block content %}
-  {% set project_name = releases[0].project.name if releases else "" %}
-  {% set project_display_name = releases[0].project.display_name if releases 
else "Unknown project" %}
+  <p>
+    <a href="{{ as_url(routes.root.index) }}" class="atr-back-link">← Back to 
Select a release</a>
+  </p>
 
-  <nav aria-label="breadcrumb" class="mb-5">
-    <ol class="breadcrumb">
-      <li class="breadcrumb-item">
-        <a href="{{ as_url(routes.root.index) }}">ATR</a>
-      </li>
-      {% if project_name %}
-        <li class="breadcrumb-item">
-          <a href="{{ as_url(routes.projects.view, name=project_name) }}">{{ 
project_display_name }}</a>
-        </li>
-      {% endif %}
-      <li class="breadcrumb-item active" aria-current="page">Completed 
releases</li>
-    </ol>
-  </nav>
-
-  <h1>Completed releases of {{ project_display_name }}</h1>
+  <h1>Completed releases of {{ project.display_name }}</h1>
 
   {% if releases %}
     <p class="mb-4">The following releases have been completed and published 
for this project.</p>
@@ -38,9 +25,13 @@
               <strong class="card-title fs-5">{{ release.version }}</strong>
               <p class="card-text text-muted">Released on {{ 
format_datetime(release.created) }}</p>
               <div class="mt-auto">
+                <a href="{{ as_url(routes.download.all_selected, 
project_name=release.project.name, version_name=release.version) }}"
+                   class="btn btn-outline-primary w-100 mb-2">
+                  <i class="bi bi-download me-1"></i> Download files
+                </a>
                 <a href="{{ as_url(routes.release.view, 
project_name=release.project.name, version_name=release.version) }}"
-                   class="btn btn-outline-primary w-100">
-                  <i class="bi bi-folder2-open me-1"></i> View release files
+                   class="btn btn-outline-secondary w-100">
+                  <i class="bi bi-folder2-open me-1"></i> View files
                 </a>
               </div>
             </div>
@@ -50,7 +41,7 @@
     </div>
   {% else %}
     <div class="alert alert-info mt-4" role="alert">
-      <i class="bi bi-info-circle me-2"></i> There are no completed releases 
recorded for {{ project_display_name }}.
+      <i class="bi bi-info-circle me-2"></i> There are no completed releases 
recorded for {{ project.display_name }}.
     </div>
   {% endif %}
 {% endblock content %}
diff --git a/atr/templates/releases.html b/atr/templates/releases.html
index e36e55b..20fda49 100644
--- a/atr/templates/releases.html
+++ b/atr/templates/releases.html
@@ -27,19 +27,17 @@
   <h1>Releases</h1>
 
   {% if releases %}
-    {% for release in releases %}
+    {% for project_display_name in projects|sort %}
+      {% set project, count = projects[project_display_name] %}
       <div class="card mb-3 bg-light">
         <div class="card-body">
-          <h3 class="card-title mb-2">{{ release.project.display_name }} {{ 
release.version }}</h3>
-          <div class="d-flex flex-wrap gap-3 pb-3 mb-2 border-bottom 
text-secondary fs-6">
-            <span class="page-release-meta-item">Created: {{ 
release.created.strftime("%Y-%m-%d %H:%M:%S UTC") }}</span>
-          </div>
-          <div class="d-flex gap-3 align-items-center pt-2">
-            <a class="btn btn-outline-primary"
-               href="{{ as_url(routes.release.view, 
project_name=release.project.name, version_name=release.version) }}"><i 
class="bi bi-eye me-1"></i> View files</a>
-            <a class="btn btn-outline-primary"
-               href="{{ as_url(routes.download.all_selected, 
project_name=release.project.name, version_name=release.version) }}"><i 
class="bi bi-download me-1"></i> Download files</a>
-          </div>
+          <h3 class="card-title mb-3">{{ project_display_name }}</h3>
+          <p class="card-text">
+            <a href="{{ as_url(routes.release.completed, 
project_name=project.name) }}"
+               class="btn btn-outline-primary">
+              <i class="bi bi-folder2-open me-1"></i> Completed releases 
(<code>{{ count }}</code>)
+            </a>
+          </p>
         </div>
       </div>
     {% endfor %}
diff --git a/playwright/test.py b/playwright/test.py
index 342a94c..9615988 100644
--- a/playwright/test.py
+++ b/playwright/test.py
@@ -241,17 +241,18 @@ def lifecycle_06_announce_preview(page: sync_api.Page, 
credentials: Credentials,
 
 
 def lifecycle_07_release_exists(page: sync_api.Page, credentials: Credentials, 
version_name: str) -> None:
-    logging.info(f"Checking for release tooling-test-example {version_name} on 
the /releases page")
+    logging.info(
+        f"Checking for release tooling-test-example {version_name} on 
/releases/completed/tooling-test-example"
+    )
+    go_to_path(page, "/releases/completed/tooling-test-example")
+    logging.info("Releases completed page loaded successfully")
 
-    release_card_locator = page.locator(f'div.card:has(h3:has-text("Apache 
Tooling Test Example {version_name}"))')
+    release_card_locator = 
page.locator(f'div.card:has(strong.card-title:has-text("{version_name}"))')
     sync_api.expect(release_card_locator).to_be_visible()
     logging.info(f"Found card for tooling-test-example {version_name} release")
-    logging.info(f"Release tooling-test-example {version_name} confirmed 
exists on /releases page")
-
-    logging.info(f"Verifying release tooling-test-example {version_name} card 
exists")
-    release_card_locator = page.locator(f'div.card:has(h3:has-text("Apache 
Tooling Test Example {version_name}"))')
-    sync_api.expect(release_card_locator).to_be_visible()
-    logging.info(f"Release tooling-test-example {version_name} card found")
+    logging.info(
+        f"Release tooling-test-example {version_name} confirmed exists on 
/releases/completed/tooling-test-example"
+    )
 
 
 def main() -> None:


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

Reply via email to