Skip to content

Aggregate CI check status #1539

Aggregate CI check status

Aggregate CI check status #1539

Workflow file for this run

name: CI Status
run-name: Aggregate CI check status
on:
pull_request:
branches:
- main
- 'release-[0-9]+.[0-9]+.x'
merge_group:
branches:
- main
- 'release-[0-9]+.[0-9]+.x'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
ci-status:
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
checks: read
steps:
- name: Wait for CI checks to complete
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const sha = context.payload.pull_request?.head.sha ?? context.sha;
const owner = context.repo.owner;
const repo = context.repo.repo;
// Wait for other workflows to get queued
core.info('Waiting 60s for CI workflows to get queued...');
await new Promise(r => setTimeout(r, 60000));
const excludedChecks = new Set([
'ci-status',
'Run Integration Tests with Latest Release (Informational)',
'Check Schema Compatibility with Latest Release (Informational)',
]);
const excludedApps = new Set(['mergify']);
const terminalStatuses = new Set(['completed']);
const successConclusions = new Set(['success', 'skipped', 'neutral']);
const failureConclusions = new Set(['failure', 'cancelled', 'timed_out']);
while (true) {
const { data: checkRuns } = await github.rest.checks.listForRef({
owner,
repo,
ref: sha,
per_page: 100,
});
// Filter to only GitHub Actions checks, excluding ourselves and bots
const relevant = checkRuns.check_runs.filter(cr => {
if (excludedChecks.has(cr.name)) return false;
if (cr.app && excludedApps.has(cr.app.slug)) return false;
// Only include GitHub Actions checks
if (!cr.app || cr.app.slug !== 'github-actions') return false;
return true;
});
if (relevant.length === 0) {
core.info('No other CI checks found yet, waiting...');
await new Promise(r => setTimeout(r, 30000));
continue;
}
const pending = relevant.filter(cr => !terminalStatuses.has(cr.status));
const completed = relevant.filter(cr => terminalStatuses.has(cr.status));
core.info(`Checks: ${completed.length} completed, ${pending.length} pending out of ${relevant.length} total`);
for (const cr of completed) {
core.info(` ✓ ${cr.name}: ${cr.conclusion}`);
}
for (const cr of pending) {
core.info(` ⏳ ${cr.name}: ${cr.status}`);
}
if (pending.length > 0) {
core.info('Waiting 30s for pending checks...');
await new Promise(r => setTimeout(r, 30000));
continue;
}
// All checks completed — evaluate conclusions
const failed = completed.filter(cr => failureConclusions.has(cr.conclusion));
if (failed.length > 0) {
for (const cr of failed) {
core.error(`${cr.name} concluded with: ${cr.conclusion}`);
}
core.setFailed(`${failed.length} CI check(s) failed.`);
return;
}
const succeeded = completed.filter(cr => successConclusions.has(cr.conclusion));
core.info(`All ${succeeded.length} CI checks passed.`);
return;
}