-
Notifications
You must be signed in to change notification settings - Fork 1.3k
147 lines (127 loc) · 6.18 KB
/
Copy pathci-status.yml
File metadata and controls
147 lines (127 loc) · 6.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
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: 180
permissions:
checks: read
pull-requests: read
steps:
- name: Wait for CI checks to complete
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.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', 'timed_out', 'cancelled', 'action_required', 'startup_failure', 'stale']);
const replayCheckPrefix = 'Integration Tests (';
const recordCheckPrefix = 'record-providers (';
function pathTriggersReplay(path) {
if (path.startsWith('src/ogx_ui/')) return false;
return path.startsWith('src/ogx/') ||
path.startsWith('tests/') ||
path === 'uv.lock' ||
path === 'pyproject.toml' ||
path === '.github/workflows/integration-tests.yml' ||
path === '.github/actions/setup-ollama/action.yml' ||
path === '.github/actions/setup-test-environment/action.yml' ||
path === '.github/actions/run-and-record-tests/action.yml' ||
path === 'scripts/integration-tests.sh' ||
path === 'scripts/generate_ci_matrix.py';
}
let replayRequired = context.eventName === 'merge_group';
if (context.payload.pull_request) {
const changedFiles = await github.paginate(github.rest.pulls.listFiles, {
owner,
repo,
pull_number: context.payload.pull_request.number,
per_page: 100,
});
replayRequired = changedFiles.some(file => pathTriggersReplay(file.filename));
core.info(`Replay required: ${replayRequired} (${changedFiles.length} changed file(s) checked)`);
}
while (true) {
const checkRuns = await github.paginate(github.rest.checks.listForRef, {
owner,
repo,
ref: sha,
per_page: 100,
});
// Filter to only GitHub Actions checks, excluding ourselves and bots
const relevant = checkRuns.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));
const replayChecks = relevant.filter(cr => cr.name.startsWith(replayCheckPrefix));
const recordChecks = relevant.filter(cr => cr.name.startsWith(recordCheckPrefix));
core.info(`Checks: ${completed.length} completed, ${pending.length} pending out of ${relevant.length} total`);
core.info(`Replay checks: ${replayChecks.length}; record checks: ${recordChecks.length}`);
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;
}
if (replayRequired && replayChecks.length === 0) {
if (recordChecks.length > 0) {
core.warning('Record checks are present, but no replay matrix checks were found for this SHA.');
}
core.info('Replay checks are required for this change. Waiting 30s for the replay workflow to appear...');
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.warning(`${cr.name} concluded with: ${cr.conclusion} — waiting for re-run`);
}
core.info(`${failed.length} check(s) failed. Waiting 30s for re-runs before giving up...`);
core.info('Re-run the failed job(s) and ci-status will pick up the result automatically.');
await new Promise(r => setTimeout(r, 30000));
continue;
}
const succeeded = completed.filter(cr => successConclusions.has(cr.conclusion));
core.info(`All ${succeeded.length} CI checks passed.`);
return;
}