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 553cc0a Add unfinished releases to the sidebar
553cc0a is described below
commit 553cc0a13c6e00b3e07c55ab666442ef6ef73ef4
Author: Sean B. Palmer <[email protected]>
AuthorDate: Tue Apr 22 19:31:04 2025 +0100
Add unfinished releases to the sidebar
---
atr/db/__init__.py | 31 +++++++++++++++++++++++++++++++
atr/routes/candidate.py | 2 --
atr/routes/mapping.py | 35 +++++++++++++++++++++++++++++++++++
atr/server.py | 3 +++
atr/templates/includes/sidebar.html | 13 +++++++++++++
atr/templates/index-committer.html | 10 +---------
6 files changed, 83 insertions(+), 11 deletions(-)
diff --git a/atr/db/__init__.py b/atr/db/__init__.py
index 517e013..c19cfb9 100644
--- a/atr/db/__init__.py
+++ b/atr/db/__init__.py
@@ -31,6 +31,7 @@ import sqlmodel.sql.expression as expression
import atr.config as config
import atr.db.models as models
+import atr.user as user
import atr.util as util
if TYPE_CHECKING:
@@ -688,6 +689,36 @@ async def tasks_ongoing(project_name: str, version_name:
str, draft_revision: st
return result.scalar_one()
+async def unfinished_releases(asfuid: str) -> dict[str, list[models.Release]]:
+ releases: dict[str, list[models.Release]] = {}
+ async with session() as data:
+ user_projects = await user.projects(asfuid)
+ user_projects.sort(key=lambda p: p.display_name)
+
+ active_phases = [
+ models.ReleasePhase.RELEASE_CANDIDATE_DRAFT,
+ models.ReleasePhase.RELEASE_CANDIDATE,
+ models.ReleasePhase.RELEASE_PREVIEW,
+ ]
+ for project in user_projects:
+ stmt = (
+ sqlmodel.select(models.Release)
+ .where(
+ models.Release.project_id == project.id,
+
validate_instrumented_attribute(models.Release.phase).in_(active_phases),
+ )
+ .options(select_in_load(models.Release.project))
+
.order_by(validate_instrumented_attribute(models.Release.created).desc())
+ )
+ result = await data.execute(stmt)
+ active_releases = list(result.scalars().all())
+ if active_releases:
+ active_releases.sort(key=lambda r: r.created, reverse=True)
+ releases[project.short_display_name] = active_releases
+
+ return releases
+
+
def validate_instrumented_attribute(obj: Any) -> orm.InstrumentedAttribute:
"""Check if the given object is an InstrumentedAttribute."""
if not isinstance(obj, orm.InstrumentedAttribute):
diff --git a/atr/routes/candidate.py b/atr/routes/candidate.py
index 0a915df..d6c2b53 100644
--- a/atr/routes/candidate.py
+++ b/atr/routes/candidate.py
@@ -96,7 +96,6 @@ async def resolve_release(
latest_vote_task = task
break
- logging.warning(f"Latest vote task 2: {latest_vote_task}")
if latest_vote_task and (latest_vote_task.status ==
models.TaskStatus.COMPLETED) and latest_vote_task.result:
task_mid = _task_mid(latest_vote_task)
archive_url = await _task_archive_url(task_mid)
@@ -220,7 +219,6 @@ async def _resolve_get(session: routes.CommitterSession) ->
str:
latest_vote_task = task
break
- logging.warning(f"Latest vote task: {latest_vote_task}")
if latest_vote_task and (latest_vote_task.status ==
models.TaskStatus.COMPLETED) and latest_vote_task.result:
task_mid = _task_mid(latest_vote_task)
archive_url = await _task_archive_url(task_mid)
diff --git a/atr/routes/mapping.py b/atr/routes/mapping.py
new file mode 100644
index 0000000..878bbc7
--- /dev/null
+++ b/atr/routes/mapping.py
@@ -0,0 +1,35 @@
+# 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.
+
+import atr.db.models as models
+import atr.routes.candidate as candidate
+import atr.routes.draft as draft
+import atr.routes.preview as preview
+import atr.util as util
+
+
+def release_as_url(release: models.Release) -> str:
+ if release.phase.value == "release_candidate_draft":
+ return util.as_url(draft.compose, project_name=release.project.name,
version_name=release.version)
+ elif release.phase.value == "release_candidate":
+ resolve_release = candidate.resolve_release # type: ignore[has-type]
+ return util.as_url(resolve_release, project_name=release.project.name,
version_name=release.version)
+ elif release.phase.value == "release_preview":
+ announce_release = preview.announce_release # type: ignore[has-type]
+ return util.as_url(announce_release,
project_name=release.project.name, version_name=release.version)
+ else:
+ raise ValueError(f"Unknown release phase: {release.phase}")
diff --git a/atr/server.py b/atr/server.py
index f49ce1e..2fea323 100644
--- a/atr/server.py
+++ b/atr/server.py
@@ -106,6 +106,7 @@ def app_setup_context(app: base.QuartApp) -> None:
@app.context_processor
async def app_wide() -> dict[str, Any]:
import atr.metadata as metadata
+ import atr.routes.mapping as mapping
import atr.routes.modules as modules
return {
@@ -116,6 +117,8 @@ def app_setup_context(app: base.QuartApp) -> None:
"is_viewing_as_admin_fn": util.is_user_viewing_as_admin,
"is_committee_member_fn": user.is_committee_member,
"routes": modules,
+ "unfinished_releases_fn": db.unfinished_releases,
+ "release_as_url": mapping.release_as_url,
"version": metadata.version,
}
diff --git a/atr/templates/includes/sidebar.html
b/atr/templates/includes/sidebar.html
index 8c6dd5e..73404fc 100644
--- a/atr/templates/includes/sidebar.html
+++ b/atr/templates/includes/sidebar.html
@@ -29,6 +29,19 @@
</div>
<nav>
{% if current_user %}
+ {% set unfinished_releases = unfinished_releases_fn(current_user.uid) %}
+ {% if unfinished_releases %}
+ {% for project_short_display_name, releases in
unfinished_releases.items() %}
+ <h3>{{ project_short_display_name }}</h3>
+ <ul>
+ {% for release in releases %}
+ <li>
+ <a href="{{ release_as_url(release) }}">{{ release.version
}}</a>
+ </li>
+ {% endfor %}
+ </ul>
+ {% endfor %}
+ {% endif %}
<h3>Release candidate drafts</h3>
<ul>
<li>
diff --git a/atr/templates/index-committer.html
b/atr/templates/index-committer.html
index a361faa..25a534e 100644
--- a/atr/templates/index-committer.html
+++ b/atr/templates/index-committer.html
@@ -119,15 +119,7 @@
<div class="d-flex flex-wrap gap-3">
{% for release in active_releases %}
{% set current_phase_index = phase_index_map.get(release.phase,
-1) %}
- {% if release.phase.value == "release_candidate_draft" %}
- {% set release_link = as_url(routes.draft.compose,
project_name=release.project.name, version_name=release.version) %}
- {% elif release.phase.value == "release_candidate" %}
- {% set release_link = as_url(routes.candidate.resolve_release,
project_name=release.project.name, version_name=release.version) %}
- {% elif release.phase.value == "release_preview" %}
- {% set release_link = as_url(routes.preview.announce_release,
project_name=release.project.name, version_name=release.version) %}
- {% endif %}
-
- <a href="{{ release_link }}" class="text-decoration-none">
+ <a href="{{ release_as_url(release) }}"
class="text-decoration-none">
<div class="card h-100 shadow-sm hover-lift atr-cursor-pointer
page-card">
<div class="card-body text-center d-flex flex-column
justify-content-center">
<div class="fw-semibold mb-3 fs-4 page-version">{{
release.version }}</div>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]