Skip to content

gh-158895: Teach libregrtest to output github job summaries - #158896

Merged
zware merged 45 commits into
python:mainfrom
cmaloney:regrtest-gha-summary
Oct 23, 2026
Merged

zware merged 45 commits into
python:mainfrom
cmaloney:regrtest-gha-summary

Conversation

@cmaloney

@cmaloney cmaloney commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Update libregrtest to output GitHub Job Summary and Error Annotations for each failing job, test when run in Github Actions. Screenshots of the changes below.

  1. Job Summaries: For each job which fails output a markdown file with details. Auto-expanded when there are few, collapsed when there are many. Only failures to reduce noise (there are a lot of jobs run)
  2. Error Annotations: When a test fails emit a marker so Github can link the specific diff line to the job failure.
  3. Introduce a "Reporter" concept to libregrtest which Github is one instance of to keep the integration from leaking around. Buildbot (calling github API), JUnit XML, etc. could be additional reporters in time.

This creates a number of UI adjustments which should make it a little easier to get around. These only happen if run in Github. Goal is to make triage a little bit simpler/faster but keep the full fidelity of the existing libregrtest output.

note: iOS and Android are excluded for now, as are other kinds of jobs like docs and mypy. Can expand support to those but would rather do as separate well scoped PRs.

Job Summaries

If you click on "checks" tab on a PR then the "tests" you get taken to the summary page (ex. https://github.com/python/cpython/actions/runs/38330186853). At the top is the workflow graph, if you scroll past that you see a job summary per failed job. If there are many failures they are "collapsed". Just a few will start expanded. The tracebacks are colored with Python traceback syntax highlighting.

Screenshot 2026-10-12 at 7 46 05 PM

Error Annotations

If you click on "Files Changed" in a PR there is a button to "Open Alerts Panel". This will show all the error annotations on all the files. Individual test failures are mapped back to the source line when possible, and that line in the Diff view will show an indicator that there is an error which can be expanded. Clicking on "show details" will take you to the specific job logs.

On individual test jobs all the annotations from that job are included at the top of the job page.

Unfortunately this does result in redundant printing of the assertion failures as we print both the libregrtest form and a form of the github annotation line is presented. I chose for this to be just after the existing failure message which I think reads reasonably.

Screenshot 2026-10-12 at 8 00 07 PM Screenshot 2026-10-12 at 7 48 29 PM Screenshot 2026-10-12 at 7 46 31 PM Screenshot 2026-10-12 at 7 46 47 PM

edit: Rewrote with screenshots to show features.

Goal is to make it faster to triage individual test failures while
keeping the full existing regrtest fidelity. Fairly minimal integration
to hopefully save some human eyeball time.

Setup so only the test summary process should output the summary,
subprocesses have the env var which will cause output unset.
@bedevere-app bedevere-app Bot added the type-feature A feature request or enhancement label Oct 6, 2026
@hugovk

hugovk commented Oct 6, 2026

Copy link
Copy Markdown
Member

Can you temporarily trigger a test failure so we can see it in action?

@cmaloney

cmaloney commented Oct 6, 2026

Copy link
Copy Markdown
Contributor Author

Yea, planning to pass + fail + iterate on failure to get it pretty then get back to passing and mark as ready for review :)

Comment thread Lib/test/libregrtest/main.py Outdated
@cmaloney cmaloney changed the title gh-158895: Teach librertest to output github job summaries gh-158895: Teach libregrtest to output github job summaries Oct 6, 2026
@cmaloney
cmaloney marked this pull request as ready for review October 15, 2026 06:07
@cmaloney

Copy link
Copy Markdown
Contributor Author

I think this is ready for review now. I updated the PR comment with full details.

My hope is in the longer term the Reporter could be used if we, for instance, want buildbot jobs to annotate the github commit (or if moving places other than github). With this PR there is the text, JUNIT_XML, and GitHub outputs. I'd like Reporter to work for JUNIT_XML but moving it is a separate project.

class Reporter:
"""Report test failures, for example to a CI service. A no-op here."""

def run_started(self, runtests: RunTests) -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def run_started(self, runtests: RunTests) -> None:
def test_started(self, runtests: RunTests) -> None:

Maybe, for parity with the other methods?

@cmaloney cmaloney Oct 15, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "run" concept is different than a "test" or "test suite" hence the different name. Parallels RunTests vs. a TestResult

if not failed:
return
ncases = sum(len(result.failed_test_cases) for result in failed)
blocks = [f"## {decolor(state)}: "

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
blocks = [f"## {decolor(state)}: "
blocks = [f"## 🛑 {decolor(state)}: "

Maybe it makes it a little easier to parse vs just markdown text?
Maybe also lowercase the state? I think it is all caps currently

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we output success as well as failure definitely, this only outputs failures to github summaries. I tried outputting both success and failures but we run 30+ test jobs and that just made a lot of summaries that needed to be browsed past and weren't particularly useful.

filename, line = frames[0].groups()
exception = traceback[frames[-1].end():]
# Prefer the last frame in a test file, the assertion
for frame in frames:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm wrong but will this get executed only when the above comparison is True?

@cmaloney cmaloney Oct 15, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct.In general this tries to find a filename from the traceback but if we fail that's okay (it shouldn't block the test run from finishing). The if frames translates to "if the length of the list of frames is > 1".

The loop then finds the last frame with a name that starts with "test_" and uses that for the annotation. Definitely not a perfect metric but seems to work reasonably for getting back to the test filename.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@zware zware left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm all for it :)

I have not reviewed every line in detail, but I have reviewed enough to know that it does what it says on the tin and I don't see any obvious simplifications.

My only concern is that I think one of the new tests is triggering false-positives in the summary; looking at the summary for this PR (which is passing all tests) includes quite a few entries. Unless those have built up over the lifetime of the PR (which I don't think is how it works?), there may be some cleanup to be done. However, I'd be ok with leaving that to a followup.

This reverts commit 81d4828.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cmaloney

Copy link
Copy Markdown
Contributor Author

I think the mixed was just that I added errors for giving a lightning talk while you were reviewing which were reporting in. Just removed those failures so we'll find out once CI finishes :)

@ZeroIntensity ZeroIntensity left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The demo was convincing. I really really want this :)

test_run_failed_module_live runs a nested regrtest that is expected to
crash; unset GITHUB_STEP_SUMMARY so it doesn't land in the CI summary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cmaloney
cmaloney requested a review from pablogsal as a code owner October 23, 2026 18:18
@zware

zware commented Oct 23, 2026

Copy link
Copy Markdown
Member

I think the mixed was just that I added errors for giving a lightning talk while you were reviewing which were reporting in. Just removed those failures so we'll find out once CI finishes :)

Turns out it wasn't a new test, it was the old one that you found and fixed :)

cmaloney and others added 2 commits October 23, 2026 11:36
The "Verify excluded tests still need exclusion" step expects each
excluded test to fail; unset GITHUB_STEP_SUMMARY so those failures
aren't annotated or added to the job summary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lets a workflow or test turn off the GitHub reporter by setting the
variable to an empty string instead of failing to open ''.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cmaloney
cmaloney requested review from DinoV and Yhg1s as code owners October 23, 2026 18:38
@zware
zware enabled auto-merge (squash) October 23, 2026 19:44
@zware
zware merged commit 7715c92 into python:main Oct 23, 2026
106 of 108 checks passed
@cmaloney
cmaloney deleted the regrtest-gha-summary branch October 23, 2026 20:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type-feature A feature request or enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants