From bdfc2cff3fb54aaee42551e478b2016b84e8e448 Mon Sep 17 00:00:00 2001 From: mangeshraut712 Date: Tue, 27 Oct 2026 09:24:02 +0000 Subject: [PATCH] fix(ci): print lychee failure URLs in the job log When LYCHEE_REPORT_DIR forces --format json, the console only showed the short permanent/transient summary. Echo the final failure table (source, URL, status) so broken links are visible without opening the report artifact. Fixes #3204. --- ci/tools/retry_lychee.py | 8 +++++++- ci/tools/tests/test_retry_lychee.py | 6 +++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/ci/tools/retry_lychee.py b/ci/tools/retry_lychee.py index 524933dcbd7..1fd157be4f3 100644 --- a/ci/tools/retry_lychee.py +++ b/ci/tools/retry_lychee.py @@ -135,11 +135,17 @@ def attempt_report(report: Path, attempt: int) -> Path: def finish(report: Path, data: Report, exit_codes: list[int], message: str, *, warning: bool = False) -> None: """Publish the final result without presenting recovered failures as current.""" + details = render_report(data) summary = f"## Documentation links\n\n{message}\n\n| Attempt | Exit code |\n| --- | --- |\n" summary += "".join(f"| {attempt} | {code} |\n" for attempt, code in enumerate(exit_codes, start=1)) - summary += f"\n### Final attempt\n\n{render_report(data)}\n" + summary += f"\n### Final attempt\n\n{details}\n" report.with_suffix(".md").write_text(summary, encoding="utf-8") + # CI console logs previously showed only the short message when LYCHEE_REPORT_DIR + # forced --format json. Echo the final failure table so broken URLs are visible + # without opening the uploaded report artifact (Fixes #3204). print(f"::warning::{message}" if warning else message, flush=True) + if data.failures: + print(details, flush=True) if summary_path := os.environ.get("GITHUB_STEP_SUMMARY"): with Path(summary_path).open("a", encoding="utf-8") as stream: stream.write(summary) diff --git a/ci/tools/tests/test_retry_lychee.py b/ci/tools/tests/test_retry_lychee.py index 8bada184e9f..b668a15c170 100644 --- a/ci/tools/tests/test_retry_lychee.py +++ b/ci/tools/tests/test_retry_lychee.py @@ -133,13 +133,17 @@ def test_http_timeout_map_entries_are_retryable(tmp_path, code): ], ) @pytest.mark.agent_authored(model="gpt-6") -def test_permanent_or_unknown_failures_stop_without_sleep_or_retry(tmp_path, monkeypatch, failure): +def test_permanent_or_unknown_failures_stop_without_sleep_or_retry(tmp_path, monkeypatch, capsys, failure): report = tmp_path / "lychee.json" write_report(report, report_data(errors=[failure])) monkeypatch.setattr(retry_lychee.time, "sleep", lambda _delay: pytest.fail("Unexpected sleep")) assert retry_lychee.retry_lychee(2, report, run_attempt=unexpected_retry) == 2 assert "permanent or unclassified" in report.with_suffix(".md").read_text(encoding="utf-8") + out = capsys.readouterr().out + assert "permanent or unclassified" in out + assert "| Source | URL | Status |" in out + assert retry_lychee.escape_markdown(failure["url"]) in out @pytest.mark.agent_authored(model="gpt-6")