This is an automated email from the ASF dual-hosted git repository. arm pushed a commit to branch sbom_generation_fix in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git
commit 6823fe3405e8bfb676a3fcbd8490b12cdc8f46cb Author: Alastair McFarlane <[email protected]> AuthorDate: Thu Dec 11 17:58:08 2025 +0000 Wait for SBOM task to complete before continuing with revision --- atr/db/interaction.py | 24 +++++++++++++++++++++++- atr/post/draft.py | 25 ++++++++++++------------- atr/storage/writers/sbom.py | 13 ------------- 3 files changed, 35 insertions(+), 27 deletions(-) diff --git a/atr/db/interaction.py b/atr/db/interaction.py index d1b210f..3225e8a 100644 --- a/atr/db/interaction.py +++ b/atr/db/interaction.py @@ -14,7 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. - +import asyncio import contextlib import datetime import enum @@ -455,6 +455,28 @@ async def user_projects(asf_uid: str, caller_data: db.Session | None = None) -> return [(p.name, p.display_name) for p in projects] +async def wait_for_task( + task: sql.Task, + caller_data: db.Session | None = None, + desired_status: sql.TaskStatus = sql.TaskStatus.COMPLETED, + timeout_s: int = 10, +) -> bool: + # We must wait until the sbom_task is complete before we can queue checks + # Maximum wait time is 60 * 100ms = 6000ms + log.info(f"Waiting for task {task.id} to complete") + async with db.ensure_session(caller_data) as data: + t = await data.task(id=task.id).get() + if t is None: + return False + for _attempt in range(timeout_s * 10): + await data.refresh(t) + if t.status == desired_status: + return True + # Wait 100ms before checking again + await asyncio.sleep(0.1) + return False + + async def _trusted_project(repository: str, workflow_ref: str, phase: TrustedProjectPhase) -> sql.Project: # Debugging log.info(f"GitHub OIDC JWT payload: {repository} {workflow_ref}") diff --git a/atr/post/draft.py b/atr/post/draft.py index 770ef39..88eaa3e 100644 --- a/atr/post/draft.py +++ b/atr/post/draft.py @@ -35,6 +35,7 @@ import atr.shared as shared import atr.storage as storage import atr.util as util import atr.web as web +from atr.db.interaction import wait_for_task class VotePreviewForm(form.Form): @@ -193,19 +194,17 @@ async def sbomgen(session: web.Committer, project_name: str, version_name: str, if await aiofiles.os.path.exists(sbom_path_in_new_revision): raise base.ASFQuartException("SBOM file already exists", errorcode=400) - if creating.new is None: - raise web.FlashError("Internal error: New revision not found") - - # Calculate the paths in the revision now that create_and_manage moved it - new_revision_dir = util.get_unfinished_dir() / project_name / version_name / creating.new.number - path_in_new_revision = new_revision_dir / rel_path - sbom_path_in_new_revision = new_revision_dir / rel_path.parent / sbom_path_rel - - # Create and queue the task, using paths within the new revision - sbom_task = await wacp.sbom.generate_cyclonedx( - project_name, version_name, creating.new.number, path_in_new_revision, sbom_path_in_new_revision - ) - await wacp.sbom.generate_cyclonedx_wait(sbom_task) + # This shouldn't happen as we need a revision to kick the task off from + if creating.old is None: + raise web.FlashError("Internal error: Revision not found") + + # Create and queue the task, using paths within the new revision + sbom_task = await wacp.sbom.generate_cyclonedx( + project_name, version_name, creating.old.number, path_in_new_revision, sbom_path_in_new_revision + ) + success = await wait_for_task(sbom_task) + if not success: + raise web.FlashError("Internal error: SBOM generation timed out") except Exception as e: log.exception("Error generating SBOM:") diff --git a/atr/storage/writers/sbom.py b/atr/storage/writers/sbom.py index 12d35d2..a18e5e9 100644 --- a/atr/storage/writers/sbom.py +++ b/atr/storage/writers/sbom.py @@ -18,7 +18,6 @@ # Removing this will cause circular imports from __future__ import annotations -import asyncio import datetime from typing import TYPE_CHECKING @@ -131,18 +130,6 @@ class CommitteeParticipant(FoundationCommitter): await self.__data.refresh(sbom_task) return sbom_task - # TODO: This is not a writer - # Move this to the readers - async def generate_cyclonedx_wait(self, sbom_task: sql.Task) -> None: - # We must wait until the sbom_task is complete before we can queue checks - # Maximum wait time is 60 * 100ms = 6000ms - for _attempt in range(60): - await self.__data.refresh(sbom_task) - if sbom_task.status != sql.TaskStatus.QUEUED: - break - # Wait 100ms before checking again - await asyncio.sleep(0.1) - async def osv_scan_cyclonedx( self, project_name: str, --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
