({ github, context, core })
| 57 | } |
| 58 | |
| 59 | export default async function run({ github, context, core }) { |
| 60 | const { owner, repo } = context.repo; |
| 61 | |
| 62 | // Fetch actual job details from the API to get descriptive names |
| 63 | const jobs = await github.paginate(github.rest.actions.listJobsForWorkflowRun, { |
| 64 | owner, |
| 65 | repo, |
| 66 | run_id: context.runId, |
| 67 | per_page: 100, |
| 68 | }); |
| 69 | |
| 70 | const failedJobs = jobs.filter(job => job.conclusion === 'failure' && !job.name.includes('(optional)')); |
| 71 | |
| 72 | if (failedJobs.length === 0) { |
| 73 | core.info('No failed jobs found'); |
| 74 | return; |
| 75 | } |
| 76 | |
| 77 | // Read and parse template |
| 78 | const template = readFileSync('.github/FLAKY_CI_FAILURE_TEMPLATE.md', 'utf8'); |
| 79 | const [, frontmatter, bodyTemplate] = template.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/); |
| 80 | const titleTemplate = frontmatter.match(/title:\s*'(.*)'/)[1]; |
| 81 | |
| 82 | // Titles we've already created or matched in this run, so the same flaky test failing on |
| 83 | // multiple matrix jobs within a single run doesn't open duplicate issues (the `existing` list |
| 84 | // below is fetched once and won't include issues created earlier in this same run). |
| 85 | const handledTitles = new Set(); |
| 86 | |
| 87 | // Get existing open issues with Tests label |
| 88 | const existing = await github.paginate(github.rest.issues.listForRepo, { |
| 89 | owner, |
| 90 | repo, |
| 91 | state: 'open', |
| 92 | labels: 'Tests', |
| 93 | per_page: 100, |
| 94 | }); |
| 95 | |
| 96 | for (const job of failedJobs) { |
| 97 | const jobName = job.name; |
| 98 | const normalizedJobName = normalizeJobName(jobName); |
| 99 | const jobUrl = job.html_url; |
| 100 | |
| 101 | // Fetch annotations from the check run to extract failed test names |
| 102 | let testNames = []; |
| 103 | try { |
| 104 | const annotations = await github.paginate(github.rest.checks.listAnnotations, { |
| 105 | owner, |
| 106 | repo, |
| 107 | check_run_id: job.id, |
| 108 | per_page: 100, |
| 109 | }); |
| 110 | |
| 111 | const testAnnotations = annotations.filter(a => a.annotation_level === 'failure' && a.path !== '.github'); |
| 112 | testNames = [...new Set(testAnnotations.map(a => a.title || a.path))]; |
| 113 | } catch (e) { |
| 114 | core.info(`Could not fetch annotations for ${jobName}: ${e.message}`); |
| 115 | } |
| 116 |
nothing calls this directly
no test coverage detected