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 22c1d06  Move draft.add_files to upload.release
22c1d06 is described below

commit 22c1d06e63f3f31d8195c9c27825ee0b81b5bcbb
Author: Sean B. Palmer <s...@miscoranda.com>
AuthorDate: Fri Apr 25 16:55:19 2025 +0100

    Move draft.add_files to upload.release
---
 atr/routes/draft.py               | 133 ++------------------------------
 atr/routes/modules.py             |   1 +
 atr/routes/upload.py              | 154 ++++++++++++++++++++++++++++++++++++++
 atr/templates/draft-content.html  |   2 +-
 atr/templates/draft-evaluate.html |   2 +-
 atr/templates/drafts.html         |   2 +-
 6 files changed, 163 insertions(+), 131 deletions(-)

diff --git a/atr/routes/draft.py b/atr/routes/draft.py
index 24bbda0..c269433 100644
--- a/atr/routes/draft.py
+++ b/atr/routes/draft.py
@@ -42,15 +42,15 @@ import atr.mail as mail
 import atr.revision as revision
 import atr.routes as routes
 import atr.routes.candidate as candidate
+import atr.routes.upload as upload
 import atr.tasks.sbom as sbom
 import atr.tasks.vote as tasks_vote
 import atr.user as user
 import atr.util as util
 
 if TYPE_CHECKING:
-    from collections.abc import Callable, Sequence
+    from collections.abc import Callable
 
-    import werkzeug.datastructures as datastructures
     import werkzeug.wrappers.response as response
 
 # _CONFIG: Final = config.get()
@@ -102,83 +102,6 @@ class StartReleaseForm(util.QuartFormTyped):
     submit = wtforms.SubmitField("Start new release")
 
 
-class SvnImportForm(util.QuartFormTyped):
-    """Form for importing files from SVN into a draft."""
-
-    svn_url = wtforms.URLField(
-        "SVN URL",
-        validators=[
-            wtforms.validators.InputRequired("SVN URL is required."),
-            wtforms.validators.URL(require_tld=False),
-        ],
-        description="The URL to the public SVN directory",
-    )
-    revision = wtforms.StringField(
-        "Revision",
-        default="HEAD",
-        validators=[],
-        description="Specify an SVN revision number or leave as HEAD for the 
latest",
-    )
-    target_subdirectory = wtforms.StringField(
-        "Target subdirectory",
-        validators=[],
-        description="Subdirectory to place imported files, defaulting to the 
root (optional)",
-    )
-    submit = wtforms.SubmitField("Queue SVN import task")
-
-
-# TODO: Rename to upload.release?
-@routes.committer("/upload/<project_name>/<version_name>", methods=["GET", 
"POST"])
-async def add_files(session: routes.CommitterSession, project_name: str, 
version_name: str) -> response.Response | str:
-    """Show a page to allow the user to add files to a candidate draft."""
-    await session.check_access(project_name)
-
-    class AddFilesForm(util.QuartFormTyped):
-        """Form for adding files to a release candidate."""
-
-        file_name = wtforms.StringField("File name (optional)")
-        file_data = wtforms.MultipleFileField(
-            "Files", validators=[wtforms.validators.InputRequired("At least 
one file is required")]
-        )
-        submit = wtforms.SubmitField("Add files")
-
-        def validate_file_name(self, field: wtforms.Field) -> bool:
-            if field.data and len(self.file_data.data) > 1:
-                raise wtforms.validators.ValidationError("File name can only 
be used when uploading a single file")
-            return True
-
-    form = await AddFilesForm.create_form()
-    if await form.validate_on_submit():
-        try:
-            file_name = None
-            if isinstance(form.file_name.data, str) and form.file_name.data:
-                file_name = pathlib.Path(form.file_name.data)
-            file_data = form.file_data.data
-
-            number_of_files = await _upload_files(project_name, version_name, 
session.uid, file_name, file_data)
-            return await session.redirect(
-                compose,
-                success=f"{number_of_files} file{'' if number_of_files == 1 
else 's'} added successfully",
-                project_name=project_name,
-                version_name=version_name,
-            )
-        except Exception as e:
-            logging.exception("Error adding file:")
-            await quart.flash(f"Error adding file: {e!s}", "error")
-
-    svn_form = await SvnImportForm.create_form()
-    release = await session.release(project_name, version_name)
-
-    return await quart.render_template(
-        "draft-add-files.html",
-        asf_id=session.uid,
-        server_domain=session.host,
-        release=release,
-        form=form,
-        svn_form=svn_form,
-    )
-
-
 # TODO: Rename to compose.release?
 @routes.committer("/compose/<project_name>/<version_name>")
 async def compose(session: routes.CommitterSession, project_name: str, 
version_name: str) -> response.Response | str:
@@ -909,7 +832,7 @@ async def start(session: routes.CommitterSession, 
project_name: str) -> response
 async def svnload(session: routes.CommitterSession, project_name: str, 
version_name: str) -> response.Response | str:
     """Import files from SVN into a draft."""
     await session.check_access(project_name)
-    form = await SvnImportForm.create_form()
+    form = await upload.SvnImportForm.create_form()
     release = await session.release(project_name, version_name, 
with_project=False)
 
     if not await form.validate_on_submit():
@@ -917,7 +840,7 @@ async def svnload(session: routes.CommitterSession, 
project_name: str, version_n
             for error in errors:
                 await quart.flash(f"{error}", "error")
         return await session.redirect(
-            add_files,
+            upload.release,
             project_name=project_name,
             version_name=version_name,
         )
@@ -945,7 +868,7 @@ async def svnload(session: routes.CommitterSession, 
project_name: str, version_n
     except Exception:
         logging.exception("Error queueing SVN import task:")
         return await session.redirect(
-            add_files,
+            upload.release,
             error="Error queueing SVN import task",
             project_name=project_name,
             version_name=version_name,
@@ -1388,49 +1311,3 @@ async def _revisions_process(
         "modified": sorted(list(modified_files)),
     }
     return revision_data, current_revision_files
-
-
-async def _save_file(file: datastructures.FileStorage, target_path: 
pathlib.Path) -> None:
-    async with aiofiles.open(target_path, "wb") as f:
-        while chunk := await asyncio.to_thread(file.stream.read, 8192):
-            await f.write(chunk)
-
-
-async def _upload_files(
-    project_name: str,
-    version_name: str,
-    asf_uid: str,
-    file_name: pathlib.Path | None,
-    files: Sequence[datastructures.FileStorage],
-) -> int:
-    """Process and save the uploaded files into a new draft revision."""
-    async with revision.create_and_manage(project_name, version_name, asf_uid) 
as (
-        new_revision_dir,
-        _new_revision_name,
-    ):
-
-        def get_target_path(file: datastructures.FileStorage) -> pathlib.Path:
-            # Determine the target path within the new revision directory
-            relative_file_path: pathlib.Path
-            if not file_name:
-                if not file.filename:
-                    raise routes.FlashError("No filename provided")
-                # Use the original name
-                relative_file_path = pathlib.Path(file.filename)
-            else:
-                # Use the provided name, relative to its anchor
-                # In other words, ignore the leading "/"
-                relative_file_path = file_name.relative_to(file_name.anchor)
-
-            # Construct path inside the new revision directory
-            target_path = new_revision_dir / relative_file_path
-            return target_path
-
-        # Save each uploaded file to the new revision directory
-        for file in files:
-            target_path = get_target_path(file)
-            # Ensure parent directories exist within the new revision
-            target_path.parent.mkdir(parents=True, exist_ok=True)
-            await _save_file(file, target_path)
-
-    return len(files)
diff --git a/atr/routes/modules.py b/atr/routes/modules.py
index fd8d6ab..b2ed77c 100644
--- a/atr/routes/modules.py
+++ b/atr/routes/modules.py
@@ -25,6 +25,7 @@ import atr.routes.preview as preview
 import atr.routes.projects as projects
 import atr.routes.release as release
 import atr.routes.root as root
+import atr.routes.upload as upload
 
 
 # Export data for a custom linter script
diff --git a/atr/routes/upload.py b/atr/routes/upload.py
new file mode 100644
index 0000000..123e12d
--- /dev/null
+++ b/atr/routes/upload.py
@@ -0,0 +1,154 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import asyncio
+import logging
+import pathlib
+from collections.abc import Sequence
+
+import aiofiles
+import quart
+import werkzeug.datastructures as datastructures
+import werkzeug.wrappers.response as response
+import wtforms
+
+import atr.revision as revision
+import atr.routes as routes
+import atr.routes.draft as draft
+import atr.util as util
+
+
+class SvnImportForm(util.QuartFormTyped):
+    """Form for importing files from SVN into a draft."""
+
+    svn_url = wtforms.URLField(
+        "SVN URL",
+        validators=[
+            wtforms.validators.InputRequired("SVN URL is required."),
+            wtforms.validators.URL(require_tld=False),
+        ],
+        description="The URL to the public SVN directory",
+    )
+    revision = wtforms.StringField(
+        "Revision",
+        default="HEAD",
+        validators=[],
+        description="Specify an SVN revision number or leave as HEAD for the 
latest",
+    )
+    target_subdirectory = wtforms.StringField(
+        "Target subdirectory",
+        validators=[],
+        description="Subdirectory to place imported files, defaulting to the 
root (optional)",
+    )
+    submit = wtforms.SubmitField("Queue SVN import task")
+
+
+@routes.committer("/upload/<project_name>/<version_name>", methods=["GET", 
"POST"])
+async def release(session: routes.CommitterSession, project_name: str, 
version_name: str) -> response.Response | str:
+    """Show a page to allow the user to add files to a candidate draft."""
+    await session.check_access(project_name)
+
+    class AddFilesForm(util.QuartFormTyped):
+        """Form for adding files to a release candidate."""
+
+        file_name = wtforms.StringField("File name (optional)")
+        file_data = wtforms.MultipleFileField(
+            "Files", validators=[wtforms.validators.InputRequired("At least 
one file is required")]
+        )
+        submit = wtforms.SubmitField("Add files")
+
+        def validate_file_name(self, field: wtforms.Field) -> bool:
+            if field.data and len(self.file_data.data) > 1:
+                raise wtforms.validators.ValidationError("File name can only 
be used when uploading a single file")
+            return True
+
+    form = await AddFilesForm.create_form()
+    if await form.validate_on_submit():
+        try:
+            file_name = None
+            if isinstance(form.file_name.data, str) and form.file_name.data:
+                file_name = pathlib.Path(form.file_name.data)
+            file_data = form.file_data.data
+
+            number_of_files = await _upload_files(project_name, version_name, 
session.uid, file_name, file_data)
+            return await session.redirect(
+                draft.compose,
+                success=f"{number_of_files} file{'' if number_of_files == 1 
else 's'} added successfully",
+                project_name=project_name,
+                version_name=version_name,
+            )
+        except Exception as e:
+            logging.exception("Error adding file:")
+            await quart.flash(f"Error adding file: {e!s}", "error")
+
+    svn_form = await SvnImportForm.create_form()
+    release = await session.release(project_name, version_name)
+
+    return await quart.render_template(
+        "draft-add-files.html",
+        asf_id=session.uid,
+        server_domain=session.host,
+        release=release,
+        form=form,
+        svn_form=svn_form,
+    )
+
+
+async def _save_file(file: datastructures.FileStorage, target_path: 
pathlib.Path) -> None:
+    async with aiofiles.open(target_path, "wb") as f:
+        while chunk := await asyncio.to_thread(file.stream.read, 8192):
+            await f.write(chunk)
+
+
+async def _upload_files(
+    project_name: str,
+    version_name: str,
+    asf_uid: str,
+    file_name: pathlib.Path | None,
+    files: Sequence[datastructures.FileStorage],
+) -> int:
+    """Process and save the uploaded files into a new draft revision."""
+    async with revision.create_and_manage(project_name, version_name, asf_uid) 
as (
+        new_revision_dir,
+        _new_revision_name,
+    ):
+
+        def get_target_path(file: datastructures.FileStorage) -> pathlib.Path:
+            # Determine the target path within the new revision directory
+            relative_file_path: pathlib.Path
+            if not file_name:
+                if not file.filename:
+                    raise routes.FlashError("No filename provided")
+                # Use the original name
+                relative_file_path = pathlib.Path(file.filename)
+            else:
+                # Use the provided name, relative to its anchor
+                # In other words, ignore the leading "/"
+                relative_file_path = file_name.relative_to(file_name.anchor)
+
+            # Construct path inside the new revision directory
+            target_path = new_revision_dir / relative_file_path
+            return target_path
+
+        # Save each uploaded file to the new revision directory
+        for file in files:
+            target_path = get_target_path(file)
+            # Ensure parent directories exist within the new revision
+            target_path.parent.mkdir(parents=True, exist_ok=True)
+            await _save_file(file, target_path)
+
+    return len(files)
diff --git a/atr/templates/draft-content.html b/atr/templates/draft-content.html
index 09845af..bdfaffb 100644
--- a/atr/templates/draft-content.html
+++ b/atr/templates/draft-content.html
@@ -86,7 +86,7 @@
       <h5 class="mb-0">Draft actions</h5>
     </div>
     <div class="card-body d-flex flex-wrap gap-2">
-      <a href="{{ as_url(routes.draft.add_files, 
project_name=release.project.name, version_name=release.version) }}"
+      <a href="{{ as_url(routes.upload.release, 
project_name=release.project.name, version_name=release.version) }}"
          title="Upload files to this draft"
          class="btn btn-primary"><i class="fas fa-upload me-1"></i> Upload 
files</a>
       <a href="{{ as_url(routes.draft.revisions, 
project_name=release.project.name, version_name=release.version) }}"
diff --git a/atr/templates/draft-evaluate.html 
b/atr/templates/draft-evaluate.html
index 931438d..5a452a6 100644
--- a/atr/templates/draft-evaluate.html
+++ b/atr/templates/draft-evaluate.html
@@ -195,7 +195,7 @@
     </div>
     <div class="card-body">
       <p>
-        <a href="{{ as_url(routes.draft.add_files, 
project_name=release.project.name, version_name=release.version) }}">Upload a 
file in the browser</a>, or use the command below to add or modify files in 
this release using rsync:
+        <a href="{{ as_url(routes.upload.release, 
project_name=release.project.name, version_name=release.version) }}">Upload a 
file in the browser</a>, or use the command below to add or modify files in 
this release using rsync:
       </p>
     </div>
     <div class="card-footer bg-light border-1 pt-4 pb-4 position-relative">
diff --git a/atr/templates/drafts.html b/atr/templates/drafts.html
index 79a382d..88475cc 100644
--- a/atr/templates/drafts.html
+++ b/atr/templates/drafts.html
@@ -48,7 +48,7 @@
                  title="Project for {{ release.project.display_name }} {{ 
release.version }}"
                  class="btn btn-sm btn-outline-secondary">Project</a>
               <br />
-              <a href="{{ as_url(routes.draft.add_files, 
project_name=release.project.name, version_name=release.version) }}"
+              <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) }}"


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

Reply via email to