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 236c3b1 Ensure that KEYS files are automatically generated when keys
change
236c3b1 is described below
commit 236c3b1cd6535ae23890d4712552e9c1a348f4b4
Author: Sean B. Palmer <[email protected]>
AuthorDate: Tue Jun 17 18:39:25 2025 +0100
Ensure that KEYS files are automatically generated when keys change
---
atr/routes/keys.py | 61 +++++++++++++++++++++---------------------
atr/templates/keys-review.html | 3 +++
2 files changed, 33 insertions(+), 31 deletions(-)
diff --git a/atr/routes/keys.py b/atr/routes/keys.py
index da9a16a..dd76343 100644
--- a/atr/routes/keys.py
+++ b/atr/routes/keys.py
@@ -66,7 +66,7 @@ class DeleteKeyForm(util.QuartFormTyped):
class UpdateCommitteeKeysForm(util.QuartFormTyped):
- submit = wtforms.SubmitField("Update KEYS file")
+ submit = wtforms.SubmitField("Regenerate KEYS file")
class UploadKeyFormBase(util.QuartFormTyped):
@@ -169,6 +169,8 @@ async def add(session: routes.CommitterSession) -> str:
for key_info in added_keys:
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)
if not added_keys:
await quart.flash("No keys were added.", "error")
# Clear form data on success by creating a new empty form instance
@@ -210,6 +212,8 @@ async def delete(session: routes.CommitterSession) ->
response.Response:
if key:
# Delete the GPG key
await data.delete(key)
+ for committee in key.committees:
+ await _autogenerate_keys_file(committee.name)
return await session.redirect(keys, success="GPG key deleted
successfully")
# If not a GPG key, try to get an SSH key
@@ -255,6 +259,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)
message = f"Uploaded {success_count} keys,"
if error_count > 0:
message += f" failed to upload {error_count} keys for {',
'.join(submitted_committees)}"
@@ -408,32 +413,12 @@ 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}")
- project_names_updated: list[str] = []
- write_errors: list[str] = []
- base_downloads_dir = util.get_downloads_dir()
+ error_msg = await _autogenerate_keys_file(committee_name)
- async with db.session() as data:
- full_keys_file_content = await _keys_formatter(committee_name, data)
- committee_keys_dir = base_downloads_dir / committee_name
- committee_keys_path = committee_keys_dir / "KEYS"
- await _write_keys_file(
- committee_keys_dir=committee_keys_dir,
- full_keys_file_content=full_keys_file_content,
- committee_keys_path=committee_keys_path,
- committee_name=committee_name,
- project_names_updated=project_names_updated,
- write_errors=write_errors,
- )
- if write_errors:
- error_summary = "; ".join(write_errors)
- await quart.flash(
- f"Completed KEYS update for {committee_name}, but encountered
errors: {error_summary}", "error"
- )
- elif project_names_updated:
- projects_str = ", ".join(project_names_updated)
- await quart.flash(f"KEYS files updated successfully for projects:
{projects_str}", "success")
+ if error_msg:
+ await quart.flash(error_msg, "error")
else:
- await quart.flash(f"No KEYS files were updated for committee
{committee_name}.", "warning")
+ await quart.flash(f"Successfully updated KEYS file for committee
{committee_name}.", "success")
return await session.redirect(keys)
@@ -530,6 +515,22 @@ async def upload(session: routes.CommitterSession) -> str:
return await render()
+async def _autogenerate_keys_file(committee_name: str) -> str | None:
+ base_downloads_dir = util.get_downloads_dir()
+
+ async with db.session() as data:
+ full_keys_file_content = await _keys_formatter(committee_name, data)
+ 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,
+ full_keys_file_content=full_keys_file_content,
+ committee_keys_path=committee_keys_path,
+ committee_name=committee_name,
+ )
+ return error_msg
+
+
async def _format_keys_file(
committee_name_for_header: str,
key_count_for_header: int,
@@ -645,18 +646,16 @@ async def _write_keys_file(
full_keys_file_content: str,
committee_keys_path: pathlib.Path,
committee_name: str,
- project_names_updated: list[str],
- write_errors: list[str],
-) -> None:
+) -> str | None:
try:
await asyncio.to_thread(committee_keys_dir.mkdir, parents=True,
exist_ok=True)
await asyncio.to_thread(committee_keys_path.write_text,
full_keys_file_content, encoding="utf-8")
- project_names_updated.append(committee_name)
except OSError as e:
error_msg = f"Failed to write KEYS file for committee
{committee_name}: {e}"
logging.exception(error_msg)
- write_errors.append(error_msg)
+ return error_msg
except Exception as e:
error_msg = f"An unexpected error occurred writing KEYS for committee
{committee_name}: {e}"
logging.exception(error_msg)
- write_errors.append(error_msg)
+ return error_msg
+ return None
diff --git a/atr/templates/keys-review.html b/atr/templates/keys-review.html
index 646115a..825b2cc 100644
--- a/atr/templates/keys-review.html
+++ b/atr/templates/keys-review.html
@@ -191,6 +191,9 @@
</tbody>
</table>
</div>
+ <p class="text-muted">
+ The <code>KEYS</code> file is automatically generated when you add
or remove a key, but you can also use the form below to manually regenerate it.
+ </p>
<form method="post"
action="{{ as_url(routes.keys.update_committee_keys,
committee_name=committee.name) }}"
class="mb-4 d-inline-block">
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]