-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
ci: Extract test names for flaky test issues #20298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
3dd6c07
get test name for failed tests
nicohrubec 616cea7
another test run
nicohrubec 0aeee92
one issue for each failing test
nicohrubec 59b43c4
empty commit for testing dedup logic
nicohrubec 02a15db
revert test changes
nicohrubec ceeb914
Merge branch 'develop' into nh/get-test-name
nicohrubec 79ca3b4
extract script
nicohrubec dc939a7
test
nicohrubec 9c86b21
Revert "test"
nicohrubec File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| /** | ||
| * CI Failure Reporter script. | ||
| * | ||
| * Creates GitHub issues for tests that fail on the develop branch. | ||
| * For each failed job in the workflow run, it fetches check run annotations | ||
| * to identify individual failing tests, then creates one issue per failing | ||
| * test using the FLAKY_CI_FAILURE_TEMPLATE.md template. Existing open issues | ||
| * with matching titles are skipped to avoid duplicates. | ||
| * | ||
| * Intended to be called from a GitHub Actions workflow via actions/github-script: | ||
| * | ||
| * const { default: run } = await import( | ||
| * `${process.env.GITHUB_WORKSPACE}/scripts/report-ci-failures.mjs` | ||
| * ); | ||
| * await run({ github, context, core }); | ||
| */ | ||
|
|
||
| import { readFileSync } from 'node:fs'; | ||
|
|
||
| export default async function run({ github, context, core }) { | ||
| const { owner, repo } = context.repo; | ||
|
|
||
| // Fetch actual job details from the API to get descriptive names | ||
| const jobs = await github.paginate(github.rest.actions.listJobsForWorkflowRun, { | ||
| owner, | ||
| repo, | ||
| run_id: context.runId, | ||
| per_page: 100, | ||
| }); | ||
|
|
||
| const failedJobs = jobs.filter(job => job.conclusion === 'failure' && !job.name.includes('(optional)')); | ||
|
|
||
| if (failedJobs.length === 0) { | ||
| core.info('No failed jobs found'); | ||
| return; | ||
| } | ||
|
|
||
| // Read and parse template | ||
| const template = readFileSync('.github/FLAKY_CI_FAILURE_TEMPLATE.md', 'utf8'); | ||
| const [, frontmatter, bodyTemplate] = template.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/); | ||
|
|
||
| // Get existing open issues with Tests label | ||
| const existing = await github.paginate(github.rest.issues.listForRepo, { | ||
| owner, | ||
| repo, | ||
| state: 'open', | ||
| labels: 'Tests', | ||
| per_page: 100, | ||
| }); | ||
|
|
||
| for (const job of failedJobs) { | ||
| const jobName = job.name; | ||
| const jobUrl = job.html_url; | ||
|
|
||
| // Fetch annotations from the check run to extract failed test names | ||
| let testNames = []; | ||
| try { | ||
| const annotations = await github.paginate(github.rest.checks.listAnnotations, { | ||
| owner, | ||
| repo, | ||
| check_run_id: job.id, | ||
| per_page: 100, | ||
| }); | ||
|
|
||
| const testAnnotations = annotations.filter(a => a.annotation_level === 'failure' && a.path !== '.github'); | ||
| testNames = [...new Set(testAnnotations.map(a => a.title || a.path))]; | ||
| } catch (e) { | ||
| core.info(`Could not fetch annotations for ${jobName}: ${e.message}`); | ||
| } | ||
|
|
||
| // If no test names found, fall back to one issue per job | ||
| if (testNames.length === 0) { | ||
| testNames = ['Unknown test']; | ||
| } | ||
|
|
||
| // Create one issue per failing test for proper deduplication | ||
| for (const testName of testNames) { | ||
| const vars = { | ||
| JOB_NAME: jobName, | ||
| RUN_LINK: jobUrl, | ||
| TEST_NAME: testName, | ||
| }; | ||
|
|
||
| let title = frontmatter.match(/title:\s*'(.*)'/)[1]; | ||
| let issueBody = bodyTemplate; | ||
| for (const [key, value] of Object.entries(vars)) { | ||
| const pattern = new RegExp(`\\{\\{\\s*env\\.${key}\\s*\\}\\}`, 'g'); | ||
| title = title.replace(pattern, value); | ||
| issueBody = issueBody.replace(pattern, value); | ||
|
nicohrubec marked this conversation as resolved.
|
||
| } | ||
|
|
||
| const existingIssue = existing.find(i => i.title === title); | ||
|
|
||
| if (existingIssue) { | ||
| core.info(`Issue already exists for "${testName}" in ${jobName}: #${existingIssue.number}`); | ||
| continue; | ||
| } | ||
|
|
||
| const newIssue = await github.rest.issues.create({ | ||
| owner, | ||
| repo, | ||
| title, | ||
| body: issueBody.trim(), | ||
| labels: ['Tests'], | ||
| }); | ||
| core.info(`Created issue #${newIssue.data.number} for "${testName}" in ${jobName}`); | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This means issue titles could get quite long but I guess it's somewhat necessary when we need an automated way to set the title to something more specific than the job name. Easy to do manually, hard for a machine :D anyway, we can follow up on this. not a blocker for now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking we could ask an LLM but that would break deduplication, i.e. we would need to put in a deterministic ruleset else this won't work properly. I don't wanna over engineer this, but we could definitely think about putting in some simple heuristics if we can think of anything that makes sense! I'll merge as is for now and maybe we can revisit later