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 92f62f0  Place incubator KEYS in the correct location in downloads
92f62f0 is described below

commit 92f62f0fbc745315f83d6c365f7dbfddd1134914
Author: Sean B. Palmer <[email protected]>
AuthorDate: Wed Jun 18 14:20:17 2025 +0100

    Place incubator KEYS in the correct location in downloads
---
 atr/blueprints/admin/admin.py |  7 ++++++-
 atr/construct.py              | 12 +++++++++---
 atr/db/interaction.py         |  2 ++
 atr/routes/draft.py           |  1 +
 atr/routes/keys.py            | 23 +++++++++++++++++------
 atr/tasks/__init__.py         |  2 ++
 atr/tasks/vote.py             |  1 +
 7 files changed, 38 insertions(+), 10 deletions(-)

diff --git a/atr/blueprints/admin/admin.py b/atr/blueprints/admin/admin.py
index 62f2c82..7225fcd 100644
--- a/atr/blueprints/admin/admin.py
+++ b/atr/blueprints/admin/admin.py
@@ -754,11 +754,12 @@ def _project_status(
 async def _regenerate_keys_all() -> tuple[int, list[str]]:
     okay = 0
     failures = []
+    downloads_dir = util.get_downloads_dir()
     async with db.session() as data:
         committees = await data.committee().all()
         for committee in committees:
             try:
-                error_msg = await keys.autogenerate_keys_file(committee.name, 
caller_data=data)
+                error_msg = await keys.autogenerate_keys_file(committee.name, 
committee.is_podling, caller_data=data)
             except Exception as e:
                 failures.append(f"Caller error regenerating KEYS file for 
committee {committee.name}: {e!s}")
                 continue
@@ -766,6 +767,10 @@ async def _regenerate_keys_all() -> tuple[int, list[str]]:
                 failures.append(error_msg)
             else:
                 okay += 1
+            if committee.is_podling:
+                if await aiofiles.os.path.isdir(downloads_dir / 
committee.name):
+                    # Accidental top level directory, so remove it
+                    await aiofiles.os.rmdir(downloads_dir / committee.name)
     return okay, failures
 
 
diff --git a/atr/construct.py b/atr/construct.py
index 1184ea8..17a7313 100644
--- a/atr/construct.py
+++ b/atr/construct.py
@@ -39,6 +39,7 @@ class StartVoteOptions:
     asfuid: str
     fullname: str
     committee_name: str
+    is_podling: bool
     project_name: str
     version_name: str
     vote_duration: int
@@ -111,9 +112,14 @@ async def start_vote_body(body: str, options: 
StartVoteOptions) -> str:
     project_short_display_name = release.project.short_display_name if 
release.project else options.project_name
 
     keys_file = None
-    keys_file_path = util.get_finished_dir() / options.committee_name / "KEYS"
-    if await aiofiles.os.path.isfile(keys_file_path):
-        keys_file = f"https://{host}/downloads/{options.committee_name}/KEYS";
+    if options.is_podling:
+        keys_file_path = util.get_downloads_dir() / "incubator" / 
options.committee_name / "KEYS"
+        if await aiofiles.os.path.isfile(keys_file_path):
+            keys_file = 
f"https://{host}/downloads/incubator/{options.committee_name}/KEYS";
+    else:
+        keys_file_path = util.get_downloads_dir() / options.committee_name / 
"KEYS"
+        if await aiofiles.os.path.isfile(keys_file_path):
+            keys_file = 
f"https://{host}/downloads/{options.committee_name}/KEYS";
 
     checklist_content = ""
     async with db.session() as data:
diff --git a/atr/db/interaction.py b/atr/db/interaction.py
index e4a4e1a..32ae0ee 100644
--- a/atr/db/interaction.py
+++ b/atr/db/interaction.py
@@ -356,6 +356,8 @@ def _key_latest_self_signature(key: dict) -> 
datetime.datetime | None:
 
 async def _key_user_add_validate_key_properties(public_key: str) -> list[dict]:
     """Validate GPG key string, import it, and return its properties and 
fingerprint."""
+    # import atr.gpgpatch as gpgpatch
+    # gnupg = gpgpatch.patch_gnupg()
     import gnupg
 
     def _sig_with_timestamp(self, args):
diff --git a/atr/routes/draft.py b/atr/routes/draft.py
index dab5af5..1e92f25 100644
--- a/atr/routes/draft.py
+++ b/atr/routes/draft.py
@@ -502,6 +502,7 @@ async def vote_preview(
             asfuid=asfuid,
             fullname=session.fullname,
             committee_name=release.committee.display_name,
+            is_podling=release.committee.is_podling,
             project_name=project_name,
             version_name=version_name,
             vote_duration=vote_duration,
diff --git a/atr/routes/keys.py b/atr/routes/keys.py
index d097f65..adf03ca 100644
--- a/atr/routes/keys.py
+++ b/atr/routes/keys.py
@@ -130,6 +130,7 @@ async def add(session: routes.CommitterSession) -> str:
         project_list = session.committees + session.projects
         user_committees = await data.committee(name_in=project_list).all()
 
+    committee_is_podling = {c.name: c.is_podling for c in user_committees}
     committee_choices = [(c.name, c.display_name or c.name) for c in 
user_committees]
 
     class AddGpgKeyForm(util.QuartFormTyped):
@@ -171,7 +172,8 @@ async def add(session: routes.CommitterSession) -> str:
                 if key_info:
                     await quart.flash(f"GPG key {key_info.get('fingerprint', 
'')} added successfully.", "success")
                     for committee_name in selected_committees_data:
-                        await autogenerate_keys_file(committee_name)
+                        is_podling = committee_is_podling[committee_name]
+                        await autogenerate_keys_file(committee_name, 
is_podling)
             if not added_keys:
                 await quart.flash("No keys were added.", "error")
             # Clear form data on success by creating a new empty form instance
@@ -194,7 +196,9 @@ async def add(session: routes.CommitterSession) -> str:
     )
 
 
-async def autogenerate_keys_file(committee_name: str, caller_data: db.Session 
| None = None) -> str | None:
+async def autogenerate_keys_file(
+    committee_name: str, is_podling: bool, caller_data: db.Session | None = 
None
+) -> str | None:
     base_downloads_dir = util.get_downloads_dir()
 
     if caller_data is None:
@@ -204,7 +208,10 @@ async def autogenerate_keys_file(committee_name: str, 
caller_data: db.Session |
 
     async with manager as data:
         full_keys_file_content = await _keys_formatter(committee_name, data)
-        committee_keys_dir = base_downloads_dir / committee_name
+        if is_podling:
+            committee_keys_dir = base_downloads_dir / "incubator" / 
committee_name
+        else:
+            committee_keys_dir = base_downloads_dir / committee_name
         committee_keys_path = committee_keys_dir / "KEYS"
         error_msg = await _write_keys_file(
             committee_keys_dir=committee_keys_dir,
@@ -235,7 +242,7 @@ async def delete(session: routes.CommitterSession) -> 
response.Response:
                 # Delete the GPG key
                 await data.delete(key)
                 for committee in key.committees:
-                    await autogenerate_keys_file(committee.name, 
caller_data=data)
+                    await autogenerate_keys_file(committee.name, 
committee.is_podling, caller_data=data)
                 return await session.redirect(keys, success="GPG key deleted 
successfully")
 
             # If not a GPG key, try to get an SSH key
@@ -281,7 +288,7 @@ async def import_selected_revision(
         )
     except interaction.InteractionError as e:
         return await session.redirect(compose.selected, error=str(e))
-    await autogenerate_keys_file(release.committee.name)
+    await autogenerate_keys_file(release.committee.name, 
release.committee.is_podling)
     message = f"Uploaded {success_count} keys,"
     if error_count > 0:
         message += f" failed to upload {error_count} keys for {', 
'.join(submitted_committees)}"
@@ -435,7 +442,11 @@ async def update_committee_keys(session: 
routes.CommitterSession, committee_name
     if committee_name not in (session.committees + session.projects):
         quart.abort(403, description=f"You are not authorised to update the 
KEYS file for {committee_name}")
 
-    error_msg = await autogenerate_keys_file(committee_name)
+    async with db.session() as data:
+        committee = await data.committee(name=committee_name).demand(
+            base.ASFQuartException(f"Committee {committee_name} not found", 
errorcode=404)
+        )
+        error_msg = await autogenerate_keys_file(committee_name, 
committee.is_podling, caller_data=data)
 
     if error_msg:
         await quart.flash(error_msg, "error")
diff --git a/atr/tasks/__init__.py b/atr/tasks/__init__.py
index cb2f41e..0fde156 100644
--- a/atr/tasks/__init__.py
+++ b/atr/tasks/__init__.py
@@ -88,6 +88,8 @@ async def draft_checks(
 
 
 def ensure_session(caller_data: db.Session | None) -> db.Session | 
contextlib.nullcontext[db.Session]:
+    # TODO: Move to interaction.py
+    # This pattern is also used in routes/keys.py
     if caller_data is None:
         return db.session()
     return contextlib.nullcontext(caller_data)
diff --git a/atr/tasks/vote.py b/atr/tasks/vote.py
index 218f38b..d651003 100644
--- a/atr/tasks/vote.py
+++ b/atr/tasks/vote.py
@@ -116,6 +116,7 @@ async def _initiate_core_logic(args: Initiate) -> dict[str, 
Any]:
             asfuid=args.initiator_id,
             fullname=args.initiator_fullname,
             committee_name=release.committee.display_name,
+            is_podling=release.committee.is_podling,
             project_name=release.project.name,
             version_name=release.version,
             vote_duration=args.vote_duration,


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

Reply via email to