From d8d05be14e20b6304456f534bb2bb5141ef3d4a0 Mon Sep 17 00:00:00 2001 From: Alexsander Hamir Date: Tue, 27 Jan 2026 13:01:07 -0800 Subject: [PATCH] Fix CI quality gate to wait for all jobs including CircleCI - Now fails if any jobs are still pending (not just warns) - Checks both GitHub Checks API (Actions) and Status API (CircleCI) - Prevents premature passing when CI is still running --- .github/workflows/prevent-self-merge.yml | 38 +++++++++++++++++------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/.github/workflows/prevent-self-merge.yml b/.github/workflows/prevent-self-merge.yml index 17b6f21a4be..fc97fafac6b 100644 --- a/.github/workflows/prevent-self-merge.yml +++ b/.github/workflows/prevent-self-merge.yml @@ -13,35 +13,52 @@ jobs: uses: actions/github-script@v7 with: script: | + // Get GitHub Checks (newer API - GitHub Actions, some CI tools) const { data: checks } = await github.rest.checks.listForRef({ owner: context.repo.owner, repo: context.repo.repo, ref: context.payload.pull_request.head.sha, }); - // Define required CI jobs that must pass - const requiredJobs = [ - 'test', // from test-litellm.yml - 'lint', // from test-linting.yml (if exists) - ]; + // Get GitHub Statuses (older API - CircleCI, Travis, etc.) + const { data: statuses } = await github.rest.repos.getCombinedStatusForRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: context.payload.pull_request.head.sha, + }); console.log('=== CI Status Check ==='); console.log(`PR: #${context.payload.pull_request.number} - ${context.payload.pull_request.title}`); console.log(`Total checks: ${checks.check_runs.length}`); + console.log(`Total statuses (CircleCI, etc): ${statuses.statuses.length}`); let failedJobs = []; let pendingJobs = []; let passedJobs = []; + // Check GitHub Actions checks checks.check_runs.forEach(check => { - console.log(`- ${check.name}: ${check.status} / ${check.conclusion}`); + console.log(`[Check] ${check.name}: ${check.status} / ${check.conclusion}`); if (check.status === 'completed' && check.conclusion === 'failure') { - failedJobs.push(check.name); + failedJobs.push(`[Check] ${check.name}`); } else if (check.status !== 'completed') { - pendingJobs.push(check.name); + pendingJobs.push(`[Check] ${check.name}`); } else if (check.status === 'completed' && check.conclusion === 'success') { - passedJobs.push(check.name); + passedJobs.push(`[Check] ${check.name}`); + } + }); + + // Check CircleCI and other status checks + statuses.statuses.forEach(status => { + console.log(`[Status] ${status.context}: ${status.state}`); + + if (status.state === 'failure' || status.state === 'error') { + failedJobs.push(`[Status] ${status.context}`); + } else if (status.state === 'pending') { + pendingJobs.push(`[Status] ${status.context}`); + } else if (status.state === 'success') { + passedJobs.push(`[Status] ${status.context}`); } }); @@ -56,7 +73,8 @@ jobs: } if (pendingJobs.length > 0) { - console.log(`⏳ Warning: ${pendingJobs.length} job(s) still running. Wait for completion.`); + core.setFailed(`⏳ Cannot evaluate: ${pendingJobs.length} job(s) still running. Wait for all CI jobs to complete before merging.\n\nPending jobs:\n${pendingJobs.join('\n')}`); + return; } // Calculate pass/fail rates