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
| name: Post-trigger check new failing tests | |
| on: | |
| push: | |
| branches: | |
| - post_trigger_check_new_failing_tests | |
| workflow_dispatch: | |
| inputs: | |
| source_workflow_run_id: | |
| description: 'Workflow run ID to re-analyze (where check_new_failures failed/was skipped)' | |
| required: true | |
| type: string | |
| job_name: | |
| description: 'CI suite name as shown in GitHub UI, e.g. "Quantization CI", "Model CI"' | |
| required: true | |
| type: string | |
| only_process_job: | |
| description: 'Skip "Find commits" jobs and only re-run the process job (use when find-commits already ran successfully but the process step failed)' | |
| required: false | |
| type: boolean | |
| default: false | |
| # For `push` runs: set these values directly to test without workflow_dispatch | |
| env: | |
| source_workflow_run_id: "25356138915" | |
| job_name: "Model CI" | |
| only_process_job: "false" | |
| jobs: | |
| prepare: | |
| name: Fetch inputs from source run | |
| runs-on: ubuntu-22.04 | |
| outputs: | |
| source_workflow_run_id: ${{ steps.resolve.outputs.source_workflow_run_id }} | |
| only_process_job: ${{ steps.resolve.outputs.only_process_job }} | |
| docker: ${{ steps.fetch-inputs.outputs.docker }} | |
| job: ${{ steps.fetch-inputs.outputs.job }} | |
| slack_report_channel: ${{ steps.fetch-inputs.outputs.slack_report_channel }} | |
| ci_event: ${{ steps.fetch-inputs.outputs.ci_event }} | |
| report_repo_id: ${{ steps.fetch-inputs.outputs.report_repo_id }} | |
| commit_sha: ${{ steps.fetch-inputs.outputs.commit_sha }} | |
| pr_number: ${{ steps.fetch-inputs.outputs.pr_number }} | |
| max_num_runners: ${{ steps.fetch-inputs.outputs.max_num_runners }} | |
| steps: | |
| - name: Resolve inputs | |
| id: resolve | |
| env: | |
| source_workflow_run_id: ${{ inputs.source_workflow_run_id || env.source_workflow_run_id }} | |
| only_process_job: ${{ inputs.only_process_job || env.only_process_job }} | |
| run: | | |
| echo "source_workflow_run_id=$source_workflow_run_id" >> $GITHUB_OUTPUT | |
| echo "only_process_job=$only_process_job" >> $GITHUB_OUTPUT | |
| - name: Fetch inputs from source run job log | |
| id: fetch-inputs | |
| uses: actions/github-script@v6 | |
| with: | |
| script: | | |
| const sourceRunId = parseInt('${{ inputs.source_workflow_run_id || env.source_workflow_run_id }}'); | |
| const jobName = `${{ inputs.job_name || env.job_name }}`; | |
| const targetJobName = `${jobName} / Check new failures / Setup matrix for finding commits`; | |
| // Find the job (paginate in case there are many jobs in the run) | |
| let targetJobId = null; | |
| let page = 1; | |
| while (true) { | |
| const { data } = await github.rest.actions.listJobsForWorkflowRun({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| run_id: sourceRunId, | |
| per_page: 100, | |
| page: page, | |
| }); | |
| const job = data.jobs.find(j => j.name === targetJobName); | |
| if (job) { | |
| targetJobId = job.id; | |
| break; | |
| } | |
| if (data.jobs.length < 100) break; | |
| page++; | |
| } | |
| if (!targetJobId) { | |
| core.setFailed(`Job "${targetJobName}" not found in run ${sourceRunId}. Check that job_name matches the first segment of the full job name in the GitHub UI.`); | |
| return; | |
| } | |
| // Download the job log (GitHub returns a redirect which Octokit follows) | |
| const logResponse = await github.rest.actions.downloadJobLogsForWorkflowRun({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| job_id: targetJobId, | |
| }); | |
| const logText = logResponse.data; | |
| const parsedInputs = {}; | |
| let inInputs = false; | |
| for (const line of logText.split('\n')) { | |
| // Strip the timestamp prefix: "2026-05-05T06:02:50.3702012Z " | |
| const content = line.replace(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z /, ''); | |
| if (content.includes('##[group]Inputs') || content.includes('##[group] Inputs')) { | |
| inInputs = true; | |
| continue; | |
| } | |
| if (inInputs) { | |
| if (content.includes('##[endgroup]')) break; | |
| // Each input line is " key: value" (2-space indent) | |
| const match = content.match(/^ (\w+): ?(.*)/); | |
| if (match) parsedInputs[match[1]] = match[2].trim(); | |
| } | |
| } | |
| if (Object.keys(parsedInputs).length === 0) { | |
| core.setFailed('Could not parse any inputs from the job log. The ##[group] Inputs section may be missing or in an unexpected format.'); | |
| return; | |
| } | |
| console.log('Extracted inputs:'); | |
| for (const [key, value] of Object.entries(parsedInputs)) { | |
| console.log(` ${key}: ${value}`); | |
| core.setOutput(key, value); | |
| } | |
| // --- Extract END_SHA from env of "Check failed tests" step --- | |
| let endSha = null; | |
| let inTargetStep = false; | |
| let inEnvBlock = false; | |
| let stepDepth = 0; | |
| for (const line of logText.split('\n')) { | |
| const content = line.replace(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z /, ''); | |
| if (content.includes('##[group]') ) { | |
| if (content.includes('Check failed tests')) { | |
| inTargetStep = true; | |
| stepDepth = 1; | |
| continue; | |
| } else if (inTargetStep) { | |
| stepDepth++; | |
| } | |
| } | |
| if (!inTargetStep) continue; | |
| if (content.includes('##[endgroup]')) { | |
| stepDepth--; | |
| if (stepDepth <= 0) break; // exited the step | |
| inEnvBlock = false; | |
| continue; | |
| } | |
| if (content.trim() === 'env:') { | |
| inEnvBlock = true; | |
| continue; | |
| } | |
| if (inEnvBlock) { | |
| const match = content.match(/^\s+END_SHA:\s*(.+)/); | |
| if (match) { | |
| endSha = match[1].trim(); | |
| break; | |
| } | |
| // stop if we've left the env block (next non-indented line) | |
| if (content.trim() && !content.startsWith(' ')) { | |
| inEnvBlock = false; | |
| } | |
| } | |
| } | |
| if (endSha) { | |
| console.log(`END_SHA: ${endSha}`); | |
| core.setOutput('END_SHA', endSha); | |
| } else { | |
| core.warning('Could not find END_SHA in the "Check failed tests" step env block.'); | |
| } | |
| check_new_failures: | |
| name: Check new failures | |
| needs: prepare | |
| uses: ./.github/workflows/check_failed_tests.yml | |
| with: | |
| docker: ${{ needs.prepare.outputs.docker }} | |
| job: ${{ needs.prepare.outputs.job }} | |
| slack_report_channel: ${{ needs.prepare.outputs.slack_report_channel }} | |
| ci_event: ${{ needs.prepare.outputs.ci_event }} | |
| report_repo_id: ${{ needs.prepare.outputs.report_repo_id }} | |
| commit_sha: ${{ needs.prepare.outputs.commit_sha }} | |
| pr_number: ${{ needs.prepare.outputs.pr_number }} | |
| max_num_runners: ${{ fromJSON(needs.prepare.outputs.max_num_runners) }} | |
| source_workflow_run_id: ${{ needs.prepare.outputs.source_workflow_run_id }} | |
| only_process_job: ${{ fromJSON(needs.prepare.outputs.only_process_job) }} | |
| secrets: inherit |