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 ee33438  Set project name dynamically based on committee metadata
ee33438 is described below

commit ee334383f8bc0373036cfd73b7f91a850e16a162
Author: Sean B. Palmer <[email protected]>
AuthorDate: Fri May 30 16:42:34 2025 +0100

    Set project name dynamically based on committee metadata
---
 atr/blueprints/admin/admin.py                      | 24 +++++++++++++++----
 .../admin/templates/cleanup-incubating-names.html  | 25 ++++++++++++++++++++
 atr/db/__init__.py                                 |  9 +++-----
 atr/db/models.py                                   |  9 ++++----
 atr/routes/__init__.py                             |  2 +-
 atr/routes/projects.py                             |  3 +--
 atr/routes/resolve.py                              | 13 -----------
 atr/templates/index-committer.html                 |  2 +-
 atr/templates/project-select.html                  |  2 +-
 migrations/versions/0006_2025.05.30_9672a901.py    | 27 ++++++++++++++++++++++
 10 files changed, 83 insertions(+), 33 deletions(-)

diff --git a/atr/blueprints/admin/admin.py b/atr/blueprints/admin/admin.py
index 743c4d3..65be76f 100644
--- a/atr/blueprints/admin/admin.py
+++ b/atr/blueprints/admin/admin.py
@@ -326,6 +326,24 @@ async def admin_performance() -> str:
     return await template.render("performance.html", stats=sorted_summary)
 
 
[email protected]("/cleanup-incubating-names", methods=["GET", "POST"])
+async def admin_cleanup_incubating_names() -> str | response.Response:
+    form = await util.EmptyForm.create_form()
+    if await form.validate_on_submit():
+        updated_count = 0
+        async with db.session() as data:
+            async with data.begin():
+                projects = await data.project().all()
+                for project_model in projects:
+                    if project_model.full_name and 
project_model.full_name.endswith(" (Incubating)"):
+                        project_model.full_name = 
project_model.full_name.removesuffix(" (Incubating)")
+                        data.add(project_model)
+                        updated_count += 1
+        await quart.flash(f"Successfully removed ' (Incubating)' from 
{updated_count} project full_names.", "success")
+        return 
quart.redirect(quart.url_for("admin.admin_cleanup_incubating_names"))
+    return await template.render("cleanup-incubating-names.html", form=form)
+
+
 @admin.BLUEPRINT.route("/projects/update", methods=["GET", "POST"])
 async def admin_projects_update() -> str | response.Response | 
tuple[Mapping[str, Any], int]:
     """Update projects from remote data."""
@@ -556,9 +574,7 @@ async def _update_committees() -> tuple[int, int]:  # noqa: 
C901
                 podling = await data.project(name=podling_name).get()
                 if not podling:
                     # Create the associated podling project
-                    podling = models.Project(
-                        name=podling_name, full_name=podling_data.name, 
committee=ppmc, is_podling=True
-                    )
+                    podling = models.Project(name=podling_name, 
full_name=podling_data.name, committee=ppmc)
                     data.add(podling)
                     added_count += 1
                 else:
@@ -594,7 +610,7 @@ async def _update_committees() -> tuple[int, int]:  # noqa: 
C901
 
                 project_model = await data.project(name=project_name).get()
                 if not project_model:
-                    project_model = models.Project(name=project_name, 
committee=pmc, is_podling=pmc.is_podling)
+                    project_model = models.Project(name=project_name, 
committee=pmc)
                     data.add(project_model)
                     added_count += 1
                 else:
diff --git a/atr/blueprints/admin/templates/cleanup-incubating-names.html 
b/atr/blueprints/admin/templates/cleanup-incubating-names.html
new file mode 100644
index 0000000..03853a9
--- /dev/null
+++ b/atr/blueprints/admin/templates/cleanup-incubating-names.html
@@ -0,0 +1,25 @@
+{% extends "layouts/base-admin.html" %}
+
+{% block title %}Cleanup incubating project names ~ ATR admin{% endblock %}
+
+{% block content %}
+        <h1 class="mb-4">Cleanup incubating project names</h1>
+        <p>
+          This action will remove the " (Incubating)" suffix from the full 
name of all projects where it is present.
+          This is a one time operation to help standardise project names in 
the database.
+        </p>
+
+        <div class="card shadow-sm mb-4">
+          <div class="card-body">
+            <h5 class="card-title">Confirm cleanup</h5>
+            <p class="card-text">
+              Press the button below to proceed with removing the " 
(Incubating)" suffix from project full names.
+              This change will be applied to the database immediately.
+            </p>
+            <form method="post" action="{{ 
url_for('admin.admin_cleanup_incubating_names') }}">
+              {{ form.csrf_token }}
+              <button type="submit" class="btn btn-danger">Cleanup project 
names</button>
+            </form>
+          </div>
+        </div>
+{% endblock %}
diff --git a/atr/db/__init__.py b/atr/db/__init__.py
index 5d31707..a50fcf2 100644
--- a/atr/db/__init__.py
+++ b/atr/db/__init__.py
@@ -270,10 +270,9 @@ class Session(sqlalchemy.ext.asyncio.AsyncSession):
         self,
         name: Opt[str] = NOT_SET,
         full_name: Opt[str] = NOT_SET,
-        is_podling: Opt[bool] = NOT_SET,
         committee_name: Opt[str] = NOT_SET,
         release_policy_id: Opt[int] = NOT_SET,
-        _committee: bool = False,
+        _committee: bool = True,
         _releases: bool = False,
         _distribution_channels: bool = False,
         _super_project: bool = False,
@@ -286,8 +285,6 @@ class Session(sqlalchemy.ext.asyncio.AsyncSession):
             query = query.where(models.Project.name == name)
         if is_defined(full_name):
             query = query.where(models.Project.full_name == full_name)
-        if is_defined(is_podling):
-            query = query.where(models.Project.is_podling == is_podling)
         if is_defined(committee_name):
             query = query.where(models.Project.committee_name == 
committee_name)
         if is_defined(release_policy_id):
@@ -359,9 +356,9 @@ class Session(sqlalchemy.ext.asyncio.AsyncSession):
         release_policy_id: Opt[int] = NOT_SET,
         votes: Opt[list[models.VoteEntry]] = NOT_SET,
         latest_revision_number: Opt[str | None] = NOT_SET,
-        _project: bool = False,
+        _project: bool = True,
+        _committee: bool = True,
         _release_policy: bool = False,
-        _committee: bool = False,
         _tasks: bool = False,
         _revisions: bool = False,
     ) -> Query[models.Release]:
diff --git a/atr/db/models.py b/atr/db/models.py
index 037eed5..88b4e00 100644
--- a/atr/db/models.py
+++ b/atr/db/models.py
@@ -170,11 +170,7 @@ class Project(sqlmodel.SQLModel, table=True):
     # We always include "Apache" in the full_name
     full_name: str | None = sqlmodel.Field(default=None)
 
-    # True if this a podling project
-    # TODO: We should have this on Committee too, or instead
-    is_podling: bool = sqlmodel.Field(default=False)
     is_retired: bool = sqlmodel.Field(default=False)
-
     super_project_name: str | None = sqlmodel.Field(default=None, 
foreign_key="project.name")
     # NOTE: Neither "Project" | None nor "Project | None" works
     super_project: Optional["Project"] = sqlmodel.Relationship()
@@ -207,7 +203,10 @@ class Project(sqlmodel.SQLModel, table=True):
     @property
     def display_name(self) -> str:
         """Get the display name for the Project."""
-        return self.full_name or self.name
+        base = self.full_name or self.name
+        if self.committee and self.committee.is_podling:
+            return f"{base} (Incubating)"
+        return base
 
     @property
     def short_display_name(self) -> str:
diff --git a/atr/routes/__init__.py b/atr/routes/__init__.py
index d9ba242..9276eac 100644
--- a/atr/routes/__init__.py
+++ b/atr/routes/__init__.py
@@ -213,7 +213,7 @@ class CommitterSession:
         phase: models.ReleasePhase | db.NotSet | None = db.NOT_SET,
         latest_revision_number: str | db.NotSet | None = db.NOT_SET,
         data: db.Session | None = None,
-        with_committee: bool = False,
+        with_committee: bool = True,
         with_project: bool = True,
         with_tasks: bool = False,
         with_revisions: bool = False,
diff --git a/atr/routes/projects.py b/atr/routes/projects.py
index debdf19..237c3cd 100644
--- a/atr/routes/projects.py
+++ b/atr/routes/projects.py
@@ -425,7 +425,7 @@ async def _project_add(form: AddFormProtocol, asf_id: str) 
-> response.Response:
 
         # Construct the new full name
         # We ensure that parenthesised suffixes like "(Incubating)" are 
preserved
-        base_name = base_project.full_name or base_project.name
+        base_name = base_project.display_name
         match = re.match(r"^(.*?) *(\(.*\))?$", base_name)
         if match:
             main_part = match.group(1).strip()
@@ -448,7 +448,6 @@ async def _project_add(form: AddFormProtocol, asf_id: str) 
-> response.Response:
         project = models.Project(
             name=new_project_label,
             full_name=new_project_full_name,
-            is_podling=base_project.is_podling,
             is_retired=base_project.is_retired,
             super_project_name=base_project.name,
             description=base_project.description,
diff --git a/atr/routes/resolve.py b/atr/routes/resolve.py
index 7a7e72b..54f1125 100644
--- a/atr/routes/resolve.py
+++ b/atr/routes/resolve.py
@@ -163,19 +163,6 @@ def task_mid_get(latest_vote_task: models.Task) -> str | 
None:
     return task_mid
 
 
-def _format_artifact_name(project_name: str, version: str, is_podling: bool = 
False) -> str:
-    """Format an artifact name according to Apache naming conventions.
-
-    For regular projects: apache-${project}-${version}
-    For podlings: apache-${project}-incubating-${version}
-    """
-    # TODO: Format this better based on committee and project
-    # Must depend on whether project is a subproject or not
-    if is_podling:
-        return f"apache-{project_name}-incubating-{version}"
-    return f"apache-{project_name}-{version}"
-
-
 async def _send_resolution(
     session: routes.CommitterSession,
     release: models.Release,
diff --git a/atr/templates/index-committer.html 
b/atr/templates/index-committer.html
index 6d8c6b2..6e7dff1 100644
--- a/atr/templates/index-committer.html
+++ b/atr/templates/index-committer.html
@@ -67,7 +67,7 @@
       <div class="mb-5" id="project-{{ project_id }}">
         <h2 class="border-bottom border-secondary pb-2 mb-3">
           {{ display_name_cleaned }}
-          {% if project.is_podling or project.name.startswith("incubator-") %}
+          {% if project.committee.is_podling or 
project.name.startswith("incubator-") %}
             <span class="text-muted fw-normal fs-5">(Incubating)</span>
             <img src="{{ url_for('static', 
filename='svg/apache_incubator.svg') }}"
                  alt=""
diff --git a/atr/templates/project-select.html 
b/atr/templates/project-select.html
index e401c38..63abb38 100644
--- a/atr/templates/project-select.html
+++ b/atr/templates/project-select.html
@@ -20,7 +20,7 @@
                 <h3 class="card-title fs-5 mb-2">{{ project.display_name 
}}</h3>
                 <h4 class="card-subtitle mb-2 text-muted fs-6">{{ project.name 
}}</h4>
                 <p>
-                  {% if project.is_podling or 
project.name.startswith("incubator-") %}
+                  {% if project.committee.is_podling or 
project.name.startswith("incubator-") %}
                     <img 
src="https://incubator.apache.org/images/SVG/apache_incubator.svg";
                          alt="" />
                   {% else %}
diff --git a/migrations/versions/0006_2025.05.30_9672a901.py 
b/migrations/versions/0006_2025.05.30_9672a901.py
new file mode 100644
index 0000000..39fccee
--- /dev/null
+++ b/migrations/versions/0006_2025.05.30_9672a901.py
@@ -0,0 +1,27 @@
+"""Remove Project.is_podling
+
+Revision ID: 0006_2025.05.30_9672a901
+Revises: 0005_2025.05.29_49f92935
+Create Date: 2025-05-30 15:22:08.113248+00:00
+"""
+
+from collections.abc import Sequence
+
+import sqlalchemy as sa
+from alembic import op
+
+# Revision identifiers, used by Alembic
+revision: str = "0006_2025.05.30_9672a901"
+down_revision: str | None = "0005_2025.05.29_49f92935"
+branch_labels: str | Sequence[str] | None = None
+depends_on: str | Sequence[str] | None = None
+
+
+def upgrade() -> None:
+    with op.batch_alter_table("project", schema=None) as batch_op:
+        batch_op.drop_column("is_podling")
+
+
+def downgrade() -> None:
+    with op.batch_alter_table("project", schema=None) as batch_op:
+        batch_op.add_column(sa.Column("is_podling", sa.BOOLEAN(), 
nullable=False))


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

Reply via email to