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]
