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 881f4de  Remove the drafts page and update the browser tests
881f4de is described below

commit 881f4de4808e178cd328952267cd63da5a1c81bd
Author: Sean B. Palmer <s...@miscoranda.com>
AuthorDate: Fri Apr 25 17:19:50 2025 +0100

    Remove the drafts page and update the browser tests
---
 atr/routes/compose.py             |   2 +-
 atr/routes/download.py            |   4 +-
 atr/routes/draft.py               |  38 +++------------
 atr/routes/release.py             |   8 +--
 atr/templates/draft-evaluate.html |   2 +-
 atr/templates/drafts.html         | 100 --------------------------------------
 playwright/test.py                |  25 +++++-----
 7 files changed, 28 insertions(+), 151 deletions(-)

diff --git a/atr/routes/compose.py b/atr/routes/compose.py
index 33c4774..393fc9b 100644
--- a/atr/routes/compose.py
+++ b/atr/routes/compose.py
@@ -89,7 +89,7 @@ async def release(session: routes.CommitterSession, 
project_name: str, version_n
     delete_file_form = await draft.DeleteFileForm.create_form()
 
     return await quart.render_template(
-        "draft-content.html",
+        "compose-release.html",
         project_name=project_name,
         version_name=version_name,
         release=release,
diff --git a/atr/routes/download.py b/atr/routes/download.py
index 5e53793..cfb3c90 100644
--- a/atr/routes/download.py
+++ b/atr/routes/download.py
@@ -25,7 +25,7 @@ import quart
 import werkzeug.wrappers.response as response
 
 import atr.routes as routes
-import atr.routes.draft as draft
+import atr.routes.root as root
 import atr.util as util
 
 
@@ -46,7 +46,7 @@ async def phase(
         # Even using the following type declaration, mypy does not know the 
type
         # The same pattern is used in release.py, so this is a bug in mypy
         # TODO: Report the bug upstream to mypy
-        return await session.redirect(draft.drafts, error="File not found")
+        return await session.redirect(root.index, error="File not found")
 
     # Send the file with original filename
     return await quart.send_file(
diff --git a/atr/routes/draft.py b/atr/routes/draft.py
index 5dff789..b0ea93f 100644
--- a/atr/routes/draft.py
+++ b/atr/routes/draft.py
@@ -43,6 +43,7 @@ import atr.revision as revision
 import atr.routes as routes
 import atr.routes.candidate as candidate
 import atr.routes.compose as compose
+import atr.routes.root as root
 import atr.routes.upload as upload
 import atr.tasks.sbom as sbom
 import atr.tasks.vote as tasks_vote
@@ -165,17 +166,17 @@ async def delete(session: routes.CommitterSession) -> 
response.Response:
         for _field, errors in form.errors.items():
             for error in errors:
                 await quart.flash(f"{error}", "error")
-        return await session.redirect(drafts)
+        return await session.redirect(root.index)
 
     candidate_draft_name = form.candidate_draft_name.data
     if not candidate_draft_name:
-        return await session.redirect(drafts, error="Missing required 
parameters")
+        return await session.redirect(root.index, error="Missing required 
parameters")
 
     # Extract project name and version
     try:
         project_name, version = candidate_draft_name.rsplit("-", 1)
     except ValueError:
-        return await session.redirect(drafts, error="Invalid candidate draft 
name format")
+        return await session.redirect(root.index, error="Invalid candidate 
draft name format")
     await session.check_access(project_name)
 
     # Delete the metadata from the database
@@ -185,7 +186,7 @@ async def delete(session: routes.CommitterSession) -> 
response.Response:
                 await _delete_candidate_draft(data, candidate_draft_name)
             except Exception as e:
                 logging.exception("Error deleting candidate draft:")
-                return await session.redirect(drafts, error=f"Error deleting 
candidate draft: {e!s}")
+                return await session.redirect(root.index, error=f"Error 
deleting candidate draft: {e!s}")
 
     # Delete the files on disk, including all revisions
     # We can't use util.release_directory_base here because we don't have the 
release object
@@ -197,7 +198,7 @@ async def delete(session: routes.CommitterSession) -> 
response.Response:
         # Yet it works in preview.py
         await aioshutil.rmtree(draft_dir)  # type: ignore[call-arg]
 
-    return await session.redirect(drafts, success="Candidate draft deleted 
successfully")
+    return await session.redirect(root.index, success="Candidate draft deleted 
successfully")
 
 
 @routes.committer("/draft/delete-file/<project_name>/<version_name>", 
methods=["POST"])
@@ -258,29 +259,6 @@ async def delete_file(session: routes.CommitterSession, 
project_name: str, versi
     )
 
 
-@routes.committer("/drafts")
-async def drafts(session: routes.CommitterSession) -> str:
-    """Allow the user to view current candidate drafts."""
-    # Do them outside of the template rendering call to ensure order
-    # The user_candidate_drafts call can use cached results from user_projects
-    # TODO: admin users should be able to view and manipulate all candidates 
if needed
-    user_projects = await session.user_projects
-    user_candidate_drafts = await session.user_candidate_drafts
-
-    # Create the delete form
-    delete_form = await DeleteForm.create_form()
-
-    return await quart.render_template(
-        "drafts.html",
-        asf_id=session.uid,
-        projects=user_projects,
-        server_domain=session.host,
-        number_of_release_files=util.number_of_release_files,
-        candidate_drafts=user_candidate_drafts,
-        delete_form=delete_form,
-    )
-
-
 @routes.committer("/draft/evaluate/<project_name>/<version_name>")
 async def evaluate(session: routes.CommitterSession, project_name: str, 
version_name: str) -> response.Response | str:
     """Evaluate all the files in the rsync upload directory for a release."""
@@ -905,7 +883,7 @@ async def vote_preview(session: routes.CommitterSession) -> 
quart.wrappers.respo
 
     form = await VotePreviewForm.create_form(data=await quart.request.form)
     if not await form.validate_on_submit():
-        return await session.redirect(drafts, error="Invalid form data")
+        return await session.redirect(root.index, error="Invalid form data")
 
     body = await mail.generate_preview(
         util.unwrap(form.body.data), util.unwrap(form.asfuid.data), 
util.unwrap(form.vote_duration.data)
@@ -1023,7 +1001,7 @@ Thanks,
             if email_to != sender:
                 error = await _promote(data, release.name, project_name, 
version, revision)
                 if error:
-                    return await session.redirect(drafts, error=error)
+                    return await session.redirect(root.index, error=error)
 
                 # This is now handled by the _promote call, above
                 # # Update the release phase to the voting phase only if not 
sending a test message to the user
diff --git a/atr/routes/release.py b/atr/routes/release.py
index af7f9ee..617a2e6 100644
--- a/atr/routes/release.py
+++ b/atr/routes/release.py
@@ -28,7 +28,7 @@ import werkzeug.wrappers.response as response
 import atr.db as db
 import atr.db.models as models
 import atr.routes as routes
-import atr.routes.draft as draft
+import atr.routes.root as root
 import atr.util as util
 
 if asfquart.APP is ...:
@@ -42,11 +42,11 @@ async def bulk_status(session: routes.CommitterSession, 
task_id: int) -> str | r
         # Query for the task with the given ID
         task = await data.task(id=task_id).get()
         if not task:
-            return await session.redirect(draft.drafts, error=f"Task with ID 
{task_id} not found.")
+            return await session.redirect(root.index, error=f"Task with ID 
{task_id} not found.")
 
         # Verify this is a bulk download task
         if task.task_type != "package_bulk_download":
-            return await session.redirect(draft.drafts, error=f"Task with ID 
{task_id} is not a bulk download task.")
+            return await session.redirect(root.index, error=f"Task with ID 
{task_id} is not a bulk download task.")
 
         # If result is a list or tuple with a single item, extract it
         if isinstance(task.result, list | tuple) and (len(task.result) == 1):
@@ -65,7 +65,7 @@ async def bulk_status(session: routes.CommitterSession, 
task_id: int) -> str | r
                 if (session.uid not in release.committee.committee_members) 
and (
                     session.uid not in release.committee.committers
                 ):
-                    return await session.redirect(draft.drafts, error="You 
don't have permission to view this task.")
+                    return await session.redirect(root.index, error="You don't 
have permission to view this task.")
 
     return await quart.render_template("release-bulk.html", task=task, 
release=release, TaskStatus=models.TaskStatus)
 
diff --git a/atr/templates/draft-evaluate.html 
b/atr/templates/draft-evaluate.html
index 5a452a6..ec17f01 100644
--- a/atr/templates/draft-evaluate.html
+++ b/atr/templates/draft-evaluate.html
@@ -183,7 +183,7 @@
         </details>
       {% else %}
         <div class="alert alert-info">
-          This release does not have any files yet. You can add files using 
rsync from the <a href="{{ as_url(routes.draft.drafts) }}">review drafts</a> 
page.
+          This release does not have any files yet. You can add files using 
rsync from the review drafts page.
         </div>
       {% endif %}
     </div>
diff --git a/atr/templates/drafts.html b/atr/templates/drafts.html
deleted file mode 100644
index 88475cc..0000000
--- a/atr/templates/drafts.html
+++ /dev/null
@@ -1,100 +0,0 @@
-{% extends "layouts/base.html" %}
-
-{% block title %}
-  Candidate draft directory ~ ATR
-{% endblock title %}
-
-{% block description %}
-  Review and modify candidate drafts.
-{% endblock description %}
-
-{% import 'macros/dialog.html' as dialog %}
-
-{% block content %}
-  <h1>Candidate drafts</h1>
-  <p>
-    A <strong>candidate draft</strong> is an editable set of files which can 
be <em>frozen and promoted into a candidate release</em> for voting on by the 
PMC.
-  </p>
-  <ul>
-    <li>You can only create a new candidate draft if you are a member of the 
PMC</li>
-    <li>Projects can work on multiple candidate drafts for different versions 
simultaneously</li>
-    <li>A candidate draft is only editable until submitted for voting</li>
-  </ul>
-
-  <div class="row row-cols-1 g-4 mb-5">
-    {% for release in candidate_drafts %}
-      {% set release_id = release.name %}
-      <div class="col" id="{{ release.name }}">
-        <div class="card h-100">
-          <div class="card-body position-relative">
-            <div class="position-absolute top-0 end-0 m-2">
-              <span class="badge bg-success">Draft</span>
-            </div>
-            <h5 class="card-title">{{ release.project.display_name }} {{ 
release.version }}</h5>
-            {% if release.project.committee %}
-              <h6 class="card-subtitle mb-2 text-muted">{{ 
release.project.committee.display_name }}</h6>
-            {% endif %}
-            <div class="d-flex gap-2 mb-2">
-              <a href="{{ as_url(routes.draft.evaluate, 
project_name=release.project.name, version_name=release.version) }}"
-                 title="Checks for {{ release.project.display_name }} {{ 
release.version }}"
-                 class="btn btn-sm btn-outline-secondary">Checks</a>
-              <a href="{{ as_url(routes.draft.view, 
project_name=release.project.name, version_name=release.version) }}"
-                 title="Files for {{ release.project.display_name }} {{ 
release.version }}"
-                 class="btn btn-sm btn-outline-secondary">Files</a>
-              <a href="{{ as_url(routes.draft.revisions, 
project_name=release.project.name, version_name=release.version) }}"
-                 title="Revisions for {{ release.project.display_name }} {{ 
release.version }}"
-                 class="btn btn-sm btn-outline-secondary">Revisions</a>
-              <a href="{{ as_url(routes.projects.view, 
name=release.project.name) }}"
-                 title="Project for {{ release.project.display_name }} {{ 
release.version }}"
-                 class="btn btn-sm btn-outline-secondary">Project</a>
-              <br />
-              <a href="{{ as_url(routes.upload.release, 
project_name=release.project.name, version_name=release.version) }}"
-                 title="Add files to {{ release.project.display_name }} {{ 
release.version }}"
-                 class="btn btn-sm btn-outline-primary">Add files</a>
-              <a href="{{ as_url(routes.draft.vote_start, 
project_name=release.project.name, version=release.version, 
revision=release.revision) }}"
-                 title="Start vote for {{ release.project.display_name }} {{ 
release.version }}"
-                 class="btn btn-sm btn-outline-success">Start vote</a>
-              <button class="btn btn-sm btn-outline-danger"
-                      title="Delete {{ release.project.display_name }} {{ 
release.version }}"
-                      data-bs-toggle="modal"
-                      data-bs-target="#delete-{{ release_id }}">Delete</button>
-            </div>
-            <p class="card-text mt-3">
-              {% if number_of_release_files(release) > 0 %}
-                This candidate draft has {{ number_of_release_files(release) }}
-                {% if number_of_release_files(release) == 1 %}
-                  file.
-                {% else %}
-                  files.
-                {% endif %}
-              {% else %}
-                This candidate draft doesn't have any files yet.
-              {% endif %}
-              Use the command below to add or modify files in this draft:
-            </p>
-          </div>
-          <div class="card-footer bg-light border-1 pt-4 pb-4 
position-relative">
-            <button class="btn btn-sm btn-outline-secondary atr-copy-btn fs-6 
position-absolute top-0 end-0 m-2"
-                    data-clipboard-target="#cmd-{{ release.name|slugify }}">
-              <i class="fas fa-clipboard"></i> Copy
-            </button>
-            <pre class="small mb-0" id="cmd-{{ release.name|slugify }}">rsync 
-av -e 'ssh -p 2222' ${YOUR_FILES}/ {{ asf_id }}@{{ server_domain }}:/{{ 
release.project.name }}/{{ release.version }}/</pre>
-          </div>
-        </div>
-      </div>
-      {{ dialog.delete_modal_with_confirm(release_id, "Delete candidate 
draft", "candidate draft", as_url(routes.draft.delete) , delete_form, 
"candidate_draft_name") }}
-    {% endfor %}
-    {% if candidate_drafts|length == 0 %}
-      <div class="col-12">
-        <div class="alert alert-info">There are currently no candidate 
drafts.</div>
-      </div>
-    {% endif %}
-  </div>
-{% endblock content %}
-
-{% block javascripts %}
-  {{ super() }}
-  <script>
-      init();
-  </script>
-{% endblock javascripts %}
diff --git a/playwright/test.py b/playwright/test.py
index 4b0cc88..8f982e6 100644
--- a/playwright/test.py
+++ b/playwright/test.py
@@ -120,9 +120,11 @@ def lifecycle_01_add_draft(page: sync_api.Page, 
credentials: Credentials, versio
 
 def lifecycle_02_check_draft_added(page: sync_api.Page, credentials: 
Credentials, version_name: str) -> None:
     logging.info(f"Checking for draft 'tooling-test-example {version_name}'")
-    go_to_path(page, "/drafts")
-    draft_card_locator = 
page.locator(f"#tooling-test-example-{esc_id(version_name)}")
-    sync_api.expect(draft_card_locator).to_be_visible()
+    go_to_path(page, f"/compose/tooling-test-example/{version_name}")
+    h1_strong_locator = page.locator("h1 strong:has-text('Tooling Test 
Example')")
+    sync_api.expect(h1_strong_locator).to_be_visible()
+    h1_em_locator = page.locator(f"h1 em:has-text('{esc_id(version_name)}')")
+    sync_api.expect(h1_em_locator).to_be_visible()
     logging.info(f"Draft 'tooling-test-example {version_name}' found 
successfully")
 
 
@@ -147,21 +149,18 @@ def lifecycle_03_add_file(page: sync_api.Page, 
credentials: Credentials, version
     wait_for_path(page, f"/compose/tooling-test-example/{version_name}")
     logging.info("Add file actions completed successfully")
 
-    logging.info("Navigating back to /drafts")
-    go_to_path(page, "/drafts")
-    logging.info("Navigation back to /drafts completed successfully")
+    logging.info("Navigating back to /compose/tooling-test-example")
+    go_to_path(page, f"/compose/tooling-test-example/{version_name}")
+    logging.info("Navigation back to /compose/tooling-test-example completed 
successfully")
 
 
 def lifecycle_04_start_vote(page: sync_api.Page, credentials: Credentials, 
version_name: str) -> None:
-    logging.info(f"Navigating to the drafts page for tooling-test-example 
{version_name}")
-    go_to_path(page, "/drafts")
-    logging.info("Drafts page loaded successfully")
+    logging.info(f"Navigating to the compose/tooling-test-example page for 
tooling-test-example {version_name}")
+    go_to_path(page, f"/compose/tooling-test-example/{version_name}")
+    logging.info("Compose/tooling-test-example page loaded successfully")
 
     logging.info(f"Locating start vote link for tooling-test-example 
{version_name}")
-    draft_card_locator = 
page.locator(f"#tooling-test-example-{esc_id(version_name)}")
-    start_vote_link_locator = draft_card_locator.locator(
-        f'a[title="Start vote for Apache Tooling Test Example {version_name}"]'
-    )
+    start_vote_link_locator = page.locator('a[title="Start a vote on this 
draft"]')
     sync_api.expect(start_vote_link_locator).to_be_visible()
 
     logging.info("Follow the start vote link")


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@tooling.apache.org
For additional commands, e-mail: commits-h...@tooling.apache.org

Reply via email to