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 a91f1c6  Update and improve the form to add an SSH key
a91f1c6 is described below

commit a91f1c6af09783bbc271a383d06f0f72898f20f3
Author: Sean B. Palmer <[email protected]>
AuthorDate: Tue May 13 18:56:30 2025 +0100

    Update and improve the form to add an SSH key
---
 atr/routes/keys.py              | 27 +++++++++++++++++++++------
 atr/tarzip.py                   |  7 +++++--
 atr/templates/keys-ssh-add.html | 24 +++++++-----------------
 atr/templates/macros/forms.html | 17 +++++++++++++++++
 4 files changed, 50 insertions(+), 25 deletions(-)

diff --git a/atr/routes/keys.py b/atr/routes/keys.py
index 8091929..accd485 100644
--- a/atr/routes/keys.py
+++ b/atr/routes/keys.py
@@ -48,7 +48,15 @@ from atr.routes import compose
 
 
 class AddSSHKeyForm(util.QuartFormTyped):
-    key = wtforms.StringField("SSH key", widget=wtforms.widgets.TextArea())
+    key = wtforms.StringField(
+        "SSH public key",
+        widget=wtforms.widgets.TextArea(),
+        render_kw={"placeholder": "Paste your SSH public key here (in the 
format used in authorized_keys files)"},
+        description=(
+            "Your SSH public key should be in the standard format, starting 
with a key type"
+            ' (like "ssh-rsa" or "ssh-ed25519") followed by the key data.'
+        ),
+    )
     submit = wtforms.SubmitField("Add SSH key")
 
 
@@ -321,11 +329,18 @@ async def ssh_add(session: routes.CommitterSession) -> 
response.Response | str:
     fingerprint = None
     if await form.validate_on_submit():
         key: str = util.unwrap(form.key.data)
-        fingerprint = await asyncio.to_thread(key_ssh_fingerprint, key)
-        async with db.session() as data:
-            async with data.begin():
-                data.add(models.SSHKey(fingerprint=fingerprint, key=key, 
asf_uid=session.uid))
-        return await session.redirect(keys, success=f"SSH key added 
successfully: {fingerprint}")
+        try:
+            fingerprint = await asyncio.to_thread(key_ssh_fingerprint, key)
+        except ValueError as e:
+            if isinstance(form.key.errors, list):
+                form.key.errors.append(str(e))
+            else:
+                form.key.errors = [str(e)]
+        else:
+            async with db.session() as data:
+                async with data.begin():
+                    data.add(models.SSHKey(fingerprint=fingerprint, key=key, 
asf_uid=session.uid))
+            return await session.redirect(keys, success=f"SSH key added 
successfully: {fingerprint}")
 
     return await quart.render_template(
         "keys-ssh-add.html",
diff --git a/atr/tarzip.py b/atr/tarzip.py
index 532c0ff..1a9e01e 100644
--- a/atr/tarzip.py
+++ b/atr/tarzip.py
@@ -23,10 +23,13 @@ from typing import IO, Generic, TypeVar
 from typing import Protocol as TypingProtocol
 
 ArchiveT = TypeVar("ArchiveT", tarfile.TarFile, zipfile.ZipFile)
-MemberT = TypeVar("MemberT", tarfile.TarInfo, zipfile.ZipInfo)
+# If you set this covariant=True, then mypy says an "invariant one is expected"
+# But pyright warns, "should be covariant" if you don't
+# We'll use the covariant version and ignore the mypy error
+MemberT = TypeVar("MemberT", tarfile.TarInfo, zipfile.ZipInfo, covariant=True)
 
 
-class AbstractArchiveMember(TypingProtocol, Generic[MemberT]):
+class AbstractArchiveMember(TypingProtocol, Generic[MemberT]):  # type: 
ignore[misc]
     name: str
     _original_info: MemberT
 
diff --git a/atr/templates/keys-ssh-add.html b/atr/templates/keys-ssh-add.html
index a19e426..eb9d24e 100644
--- a/atr/templates/keys-ssh-add.html
+++ b/atr/templates/keys-ssh-add.html
@@ -22,28 +22,18 @@
     </p>
   </div>
 
-  {% if form.errors %}
-    <h2 class="text-danger">Form errors</h2>
-    <div class="mt-3 mb-3">
-      {% for field, errors in form.errors.items() %}
-        {% for error in errors %}<p class="text-danger mb-1">{{ field }}: {{ 
error }}</p>{% endfor %}
-      {% endfor %}
-    </div>
-  {% endif %}
+  {{ forms.errors_summary(form) }}
 
-  <form method="post" class="atr-canary">
+  <form method="post" class="atr-canary py-4 px-5" novalidate>
     {{ form.hidden_tag() }}
 
     <div class="mb-4">
-      <div class="mb-3">
-        <label for="key" class="form-label">SSH public key:</label>
-      </div>
-      {{ form.key(class="form-control mb-2", rows=4, placeholder="Paste your 
SSH public key here (in the format used in authorized_keys files)", 
aria_describedby="key-help") }}
-      <small id="key-help" class="form-text text-muted">
-        Your SSH public key should be in the standard format, starting with a 
key type (like "ssh-rsa" or "ssh-ed25519") followed by the key data.
-      </small>
+      {{ forms.label(form.key) }}
+      {{ forms.widget(form.key, classes="form-control mb-2", rows=4, 
id=form.key.id) }}
+      {{ forms.errors(form.key, classes="invalid-feedback d-block") }}
+      {{ forms.description(form.key) }}
     </div>
 
-    {{ form.submit(class="btn btn-primary") }}
+    {{ form.submit(class_="btn btn-primary") }}
   </form>
 {% endblock content %}
diff --git a/atr/templates/macros/forms.html b/atr/templates/macros/forms.html
index 8ef5a33..8fecf77 100644
--- a/atr/templates/macros/forms.html
+++ b/atr/templates/macros/forms.html
@@ -64,3 +64,20 @@
 {% macro description(field, classes="form-text text-muted") %}
   {% if field.description %}<div id="{{ field.id }}-help" class="{{ classes 
}}">{{ field.description }}</div>{% endif %}
 {% endmacro %}
+
+{% macro errors_summary(form, alert_classes='alert alert-danger mt-3 mb-3', 
heading_text='Please correct the errors below:') %}
+  {% if form.errors and form.errors|length > 0 %}
+    <div class="{{ alert_classes }}" role="alert">
+      <p class="alert-heading">
+        <strong>{{ heading_text }}</strong>
+      </p>
+      <ul class="mb-0">
+        {% for field_name, field_errors in form.errors.items() %}
+          {% for error in field_errors %}
+            <li>{{ form[field_name].label.text if form[field_name] and 
form[field_name].label else field_name }}: {{ error }}</li>
+          {% endfor %}
+        {% endfor %}
+      </ul>
+    </div>
+  {% endif %}
+{% endmacro %}


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

Reply via email to