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

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

commit abfad6a1306286a2c032586980129ac9c6d2dee1
Author: Sean B. Palmer <[email protected]>
AuthorDate: Mon Jan 5 15:25:57 2026 +0000

    Add debugging for a CI problem
---
 atr/admin/__init__.py | 19 +++++++++++++++-
 atr/get/checks.py     | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++
 playwright/test.py    | 35 ++++++++++++++++++++++++++--
 3 files changed, 114 insertions(+), 3 deletions(-)

diff --git a/atr/admin/__init__.py b/atr/admin/__init__.py
index 0ce76e0..1d9f28d 100644
--- a/atr/admin/__init__.py
+++ b/atr/admin/__init__.py
@@ -569,7 +569,24 @@ async def logs(session: web.Committer) -> 
web.QuartResponse:
     recent_logs = log.get_recent_logs()
     if recent_logs is None:
         raise base.ASFQuartException("Debug logging not initialised", 
errorcode=404)
-    return web.TextResponse("\n".join(recent_logs))
+    worker_log_tail = ""
+    worker_log_path = pathlib.Path(conf.STATE_DIR) / "atr-worker.log"
+    if await aiofiles.os.path.exists(worker_log_path):
+        try:
+            size = await aiofiles.os.path.getsize(worker_log_path)
+            offset = max(0, size - 65536)
+            async with aiofiles.open(worker_log_path, "rb") as f:
+                await f.seek(offset)
+                data = await f.read()
+            text = data.decode("utf-8", errors="replace")
+            worker_log_tail = "\n".join(text.splitlines()[-400:])
+        except Exception as exc:
+            worker_log_tail = f"{type(exc).__name__}: {exc}"
+
+    output = "\n".join(recent_logs)
+    if worker_log_tail:
+        output = f"{output}\n\n=== WORKER LOG TAIL ===\n{worker_log_tail}"
+    return web.TextResponse(output)
 
 
 @admin.get("/ongoing-tasks/<project_name>/<version_name>/<revision>")
diff --git a/atr/get/checks.py b/atr/get/checks.py
index 1781b2f..1d6c60f 100644
--- a/atr/get/checks.py
+++ b/atr/get/checks.py
@@ -24,6 +24,7 @@ import htpy
 import quart
 
 import atr.blueprints.get as get
+import atr.config as config
 import atr.db as db
 import atr.db.interaction as interaction
 import atr.form as form
@@ -165,6 +166,67 @@ async def selected_revision(
     checks_summary_elem = shared._render_checks_summary(info, project_name, 
version_name)
     checks_summary_html = str(checks_summary_elem) if checks_summary_elem else 
""
 
+    debug_and_allow_tests = (config.get_mode() == config.Mode.Debug) and 
config.get().ALLOW_TESTS
+    debug_path = quart.request.args.get("debug_path")
+    debug_only = quart.request.args.get("debug_only") == "1"
+    debug = None
+    if debug_and_allow_tests and debug_path and info is not None:
+        debug_path_obj = pathlib.Path(debug_path)
+        successes = info.successes.get(debug_path_obj, [])
+        warnings = info.warnings.get(debug_path_obj, [])
+        errors = info.errors.get(debug_path_obj, [])
+        ignored_errors = [cr for cr in info.ignored_errors if 
cr.primary_rel_path == debug_path]
+        ignored_warnings = [cr for cr in info.ignored_warnings if 
cr.primary_rel_path == debug_path]
+
+        def summarise(results: list[sql.CheckResult]) -> dict[str, object]:
+            return {
+                "count": len(results),
+                "checkers": sorted({cr.checker for cr in results}),
+                "messages": [cr.message for cr in results[:5]],
+            }
+
+        async with db.session() as data:
+            tasks_for_path = (
+                await data.task(
+                    project_name=project_name,
+                    version_name=version_name,
+                    revision_number=revision_number,
+                    primary_rel_path=debug_path,
+                )
+                .order_by(sql.Task.id)
+                .all()
+            )
+
+        debug_tasks = [
+            {
+                "id": t.id,
+                "type": t.task_type.value,
+                "status": t.status.value,
+                "error": t.error,
+                "started": t.started.isoformat() if t.started else None,
+                "completed": t.completed.isoformat() if t.completed else None,
+            }
+            for t in tasks_for_path
+        ]
+
+        debug = {
+            "path": debug_path,
+            "success": summarise(successes),
+            "warning": summarise(warnings),
+            "error": summarise(errors),
+            "ignored_error": summarise(ignored_errors),
+            "ignored_warning": summarise(ignored_warnings),
+            "tasks": debug_tasks,
+        }
+
+    if debug_only and debug is not None:
+        return quart.jsonify(
+            {
+                "ongoing": ongoing_count,
+                "debug": debug,
+            }
+        )
+
     delete_file_forms: dict[str, str] = {}
     if release.phase == sql.ReleasePhase.RELEASE_CANDIDATE_DRAFT:
         for path in paths:
@@ -202,6 +264,7 @@ async def selected_revision(
             "ongoing": ongoing_count,
             "checks_summary_html": checks_summary_html,
             "files_table_html": files_table_html,
+            "debug": debug,
         }
     )
 
diff --git a/playwright/test.py b/playwright/test.py
index ebe6fc2..924645d 100755
--- a/playwright/test.py
+++ b/playwright/test.py
@@ -619,10 +619,41 @@ def test_checks_02_license_files(page: Page, credentials: 
Credentials) -> None:
     go_to_path(page, compose_path)
 
     logging.info(f"Locating 'Show report' link for {filename_targz}")
-    row_locator = page.locator(f"tr:has(:text('{filename_targz}'))")
+    file_cell_locator = page.get_by_role("cell", name=filename_targz, 
exact=True)
+    row_locator = page.locator("tr", has=file_cell_locator)
     evaluate_link_title = f"Show report for {filename_targz}"
     evaluate_link_locator = 
row_locator.locator(f'a[title="{evaluate_link_title}"]')
-    expect(evaluate_link_locator).to_be_visible()
+    try:
+        expect(evaluate_link_locator).to_be_visible()
+    except AssertionError:
+        logging.error(f"Did not find expected 'Show report' link for 
{filename_targz} on {compose_path}")
+        row_html = row_locator.first.inner_html()
+        logging.error(f"Row HTML for {filename_targz}:\n{row_html}")
+        links = row_locator.locator("a").all()
+        for i, link in enumerate(links[:10]):
+            href = link.get_attribute("href")
+            title = link.get_attribute("title")
+            text = link.inner_text()
+            logging.error(f"Row link {i}: text={text!r} title={title!r} 
href={href!r}")
+
+        revision_link_locator = 
page.locator(f'a[href^="/revisions/{project_name}/{version_name}#"]')
+        if revision_link_locator.is_visible(timeout=1000):
+            revision_href = revision_link_locator.get_attribute("href")
+            if revision_href:
+                revision = revision_href.split("#", 1)[-1]
+                debug_url = (
+                    f"{ATR_BASE_URL}/checks/{project_name}/{version_name}/"
+                    f"{revision}?debug_path={filename_targz}&debug_only=1"
+                )
+                debug_response = page.request.get(debug_url)
+                logging.error(f"Debug checks response status: 
{debug_response.status}")
+                logging.error(f"Debug checks response 
body:\n{debug_response.text()}")
+
+        server_logs_url = f"{ATR_BASE_URL}/admin/logs"
+        server_logs_response = page.request.get(server_logs_url)
+        logging.error(f"Server logs response status: 
{server_logs_response.status}")
+        logging.error(f"Server logs response 
body:\n{server_logs_response.text()}")
+        raise
 
     logging.info(f"Clicking 'Show report' link for {filename_targz}")
     evaluate_link_locator.click()


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

Reply via email to