This is an automated email from the ASF dual-hosted git repository.

arm pushed a commit to branch sbom_tests
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git

commit a5c32405fe4d96fe224a11f40a281bdbac87e8e2
Author: Alastair McFarlane <[email protected]>
AuthorDate: Wed Dec 10 16:19:41 2025 +0000

    Add missing sections for local dev compose, and re-add syft to Docker 
container
---
 atr/tasks/sbom.py           | 52 ++++++++++++++++++++++++++++++++++-----------
 tests/Dockerfile.e2e        |  2 +-
 tests/e2e/sbom/__init__.py  | 16 ++++++++++++++
 tests/e2e/sbom/conftest.py  | 47 ++++++++++++++++++++++++++++++++++++++++
 tests/e2e/sbom/test_post.py | 40 ++++++++++++++++++++++++++++++++++
 5 files changed, 144 insertions(+), 13 deletions(-)

diff --git a/atr/tasks/sbom.py b/atr/tasks/sbom.py
index 6451f10..a32a9be 100644
--- a/atr/tasks/sbom.py
+++ b/atr/tasks/sbom.py
@@ -221,17 +221,17 @@ async def _generate_cyclonedx_core(artifact_path: str, 
output_path: str) -> dict
     async with util.async_temporary_directory(prefix="cyclonedx_sbom_") as 
temp_dir:
         log.info(f"Created temporary directory: {temp_dir}")
 
-        # Find and validate the root directory
-        try:
-            root_dir = await asyncio.to_thread(targz.root_directory, 
artifact_path)
-        except targz.RootDirectoryError as e:
-            raise SBOMGenerationError(f"Archive root directory issue: {e}", 
{"artifact_path": artifact_path}) from e
-        except Exception as e:
-            raise SBOMGenerationError(
-                f"Failed to determine archive root directory: {e}", 
{"artifact_path": artifact_path}
-            ) from e
-
-        extract_dir = os.path.join(temp_dir, root_dir)
+        # # Find and validate the root directory
+        # try:
+        #     root_dir = await asyncio.to_thread(targz.root_directory, 
artifact_path)
+        # except targz.RootDirectoryError as e:
+        #     raise SBOMGenerationError(f"Archive root directory issue: {e}", 
{"artifact_path": artifact_path}) from e
+        # except Exception as e:
+        #     raise SBOMGenerationError(
+        #         f"Failed to determine archive root directory: {e}", 
{"artifact_path": artifact_path}
+        #     ) from e
+        #
+        # extract_dir = os.path.join(temp_dir, root_dir)
 
         # Extract the archive to the temporary directory
         # TODO: Ideally we'd have task dependencies or archive caching
@@ -243,7 +243,18 @@ async def _generate_cyclonedx_core(artifact_path: str, 
output_path: str) -> dict
             max_size=_CONFIG.MAX_EXTRACT_SIZE,
             chunk_size=_CONFIG.EXTRACT_CHUNK_SIZE,
         )
-        log.info(f"Extracted {extracted_size} bytes into {extract_dir}")
+        log.info(f"Extracted {extracted_size} bytes")
+
+        # Find the root directory
+        if (extract_dir := _extracted_dir(str(temp_dir))) is None:
+            log.error("No root directory found in archive")
+            return {
+                "valid": False,
+                "message": "No root directory found in archive",
+                "errors": [],
+            }
+
+        log.info(f"Using root directory: {extract_dir}")
 
         # Run syft to generate the CycloneDX SBOM
         syft_command = ["syft", extract_dir, "-o", "cyclonedx-json"]
@@ -302,3 +313,20 @@ async def _generate_cyclonedx_core(artifact_path: str, 
output_path: str) -> dict
         except FileNotFoundError:
             log.error("syft command not found. Is it installed and in PATH?")
             raise SBOMGenerationError("syft command not found")
+
+def _extracted_dir(temp_dir: str) -> str | None:
+    # Loop through all the dirs in temp_dir
+    extract_dir = None
+    log.info(f"Checking directories in {temp_dir}: {os.listdir(temp_dir)}")
+    for dir_name in os.listdir(temp_dir):
+        if dir_name.startswith("."):
+            continue
+        dir_path = os.path.join(temp_dir, dir_name)
+        if os.path.isdir(dir_path):
+            if extract_dir is None:
+                extract_dir = dir_path
+            else:
+                raise ValueError(f"Multiple root directories found: 
{extract_dir}, {dir_path}")
+    if extract_dir is None:
+        extract_dir = temp_dir
+    return extract_dir
diff --git a/tests/Dockerfile.e2e b/tests/Dockerfile.e2e
index a0c0ded..a66133b 100644
--- a/tests/Dockerfile.e2e
+++ b/tests/Dockerfile.e2e
@@ -1,4 +1,4 @@
-FROM mcr.microsoft.com/playwright/python:v1.56.0-noble
+FROM mcr.microsoft.com/playwright/python:v1.57.0-noble
 
 RUN pip3 install --no-cache-dir --break-system-packages pytest 
pytest-playwright
 
diff --git a/tests/e2e/sbom/__init__.py b/tests/e2e/sbom/__init__.py
new file mode 100644
index 0000000..13a8339
--- /dev/null
+++ b/tests/e2e/sbom/__init__.py
@@ -0,0 +1,16 @@
+# 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.
diff --git a/tests/e2e/sbom/conftest.py b/tests/e2e/sbom/conftest.py
new file mode 100644
index 0000000..649b926
--- /dev/null
+++ b/tests/e2e/sbom/conftest.py
@@ -0,0 +1,47 @@
+# 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.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+import e2e.helpers as helpers  # type: ignore[reportMissingImports]
+import pytest
+import pathlib
+
+if TYPE_CHECKING:
+    from collections.abc import Generator
+
+    from playwright.sync_api import Page
+
+PROJECT_NAME='test'
+VERSION_NAME='0.1'
+FILE_NAME='pyproject.tar.gz'
+CURRENT_DIR=pathlib.Path(__file__).parent.resolve()
+
[email protected]
+def page_release_with_file(page: Page) -> Generator[Page]:
+    helpers.log_in(page)
+    helpers.visit(page, f"/start/{PROJECT_NAME}")
+    page.get_by_role('textbox').type(VERSION_NAME)
+    page.get_by_role('button', name='Start new release').click()
+    helpers.visit(page, f"/upload/{PROJECT_NAME}/{VERSION_NAME}")
+    
page.locator('input[name="file_data"]').set_input_files(f"{CURRENT_DIR}/../test_files/{FILE_NAME}")
+    page.get_by_role("button", name="Add files").click()
+    helpers.visit(page, f"/compose/{PROJECT_NAME}/{VERSION_NAME}")
+    page.wait_for_selector('#ongoing-tasks-banner',state='hidden')
+    yield page
diff --git a/tests/e2e/sbom/test_post.py b/tests/e2e/sbom/test_post.py
new file mode 100644
index 0000000..4c6165b
--- /dev/null
+++ b/tests/e2e/sbom/test_post.py
@@ -0,0 +1,40 @@
+# 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.
+
+from playwright.sync_api import Page, expect
+
+import e2e.helpers as helpers  # type: ignore[reportMissingImports]
+from e2e.sbom.conftest import PROJECT_NAME,VERSION_NAME,FILE_NAME # type: 
ignore[reportMissingImports]
+
+def test_sbom_generate(page_release_with_file: Page) -> None:
+    # Make sure test file exists
+    file_cell = page_release_with_file.get_by_role('cell', name=FILE_NAME)
+    expect(file_cell).to_be_visible()
+
+    # Generate an SBOM for the file
+    helpers.visit(page_release_with_file, 
f"/draft/tools/{PROJECT_NAME}/{VERSION_NAME}/{FILE_NAME}")
+    generate_button = page_release_with_file.get_by_role('button', name='SBOM')
+    generate_button.click()
+
+    # Check the generated SBOM exists now
+    helpers.visit(page_release_with_file, 
f"/compose/{PROJECT_NAME}/{VERSION_NAME}")
+    
page_release_with_file.wait_for_selector('#ongoing-tasks-banner',state='hidden')
+    page_release_with_file.reload()
+
+    sbom_cell = page_release_with_file.get_by_role('cell', 
name=f"{FILE_NAME}.cdx.json")
+    expect(sbom_cell).to_be_visible()
+


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

Reply via email to