-
Notifications
You must be signed in to change notification settings - Fork 15
266 lines (226 loc) · 9.52 KB
/
Copy pathci.yml
File metadata and controls
266 lines (226 loc) · 9.52 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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
name: ci
# THE CI OF ONE COMMIT, IN STAGES. The design and its measurements are in
# .agents/docs/2026-10-02-pr-ci-acceleration-and-the-toolchain-specification-design.md
# (Part II); the rules it cites are R1-R7 there.
#
# changes what the commit changed decides what runs (R2). A change of
# documentation that nothing reads runs `docs` and nothing else.
# docs the checks that need no binary, on every change.
# build-* this commit's mcpp, built ONCE per host (R1) by build.yml and
# uploaded as `mcpp-built-<host>`.
# the rest the per-area workflows, called as reusable workflows. Each waits
# for its own host's build only, takes the binary with
# .github/actions/use-built-mcpp, and keeps the job names it had as
# a workflow of its own.
# e2e-coverage every e2e test ran somewhere or says why not (R5).
#
# Measured before this shape (2026-10-01, seven commits): 35 to 47 jobs started
# at once against the organisation's 20 slots, 30 to 37 of them built mcpp
# from source, and a commit waited 36 to 50 minutes, 9 to 13 of them in the
# queue.
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
# A superseded pull-request run is cancelled. Every push to main runs to the
# end, in a group of its own commit: its build jobs are the only writers of the
# caches (R3), and a cancelled run saves nothing. A group shared by all pushes
# to main would still lose runs, because GitHub keeps one pending run per group
# and cancels the older pending one when a third arrives.
concurrency:
group: ci-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
# `changes` lists the files of the pull request.
pull-requests: read
jobs:
changes:
name: what the change starts
runs-on: ubuntu-24.04
timeout-minutes: 5
outputs:
code: ${{ steps.classify.outputs.code }}
steps:
- uses: actions/checkout@v4
- name: Classify the changed paths
id: classify
env:
GH_TOKEN: ${{ github.token }}
EVENT: ${{ github.event_name }}
REPO: ${{ github.repository }}
PR: ${{ github.event.pull_request.number }}
BEFORE: ${{ github.event.before }}
SHA: ${{ github.sha }}
run: |
set -euo pipefail
full() { echo "code=true" >> "$GITHUB_OUTPUT"; echo "code=true ($1)"; }
# Anything this step cannot list runs the whole CI: an API error, a
# force-push whose previous commit is gone, an event without a diff.
case "$EVENT" in
pull_request)
gh api --paginate "repos/$REPO/pulls/$PR/files" \
--jq '.[] | .filename, (.previous_filename // empty)' > changed.txt \
|| { full "the files of the pull request could not be listed"; exit 0; } ;;
push)
if [ -z "$BEFORE" ] || [ "$BEFORE" = 0000000000000000000000000000000000000000 ]; then
full "a push with no previous commit"; exit 0
fi
gh api "repos/$REPO/compare/$BEFORE...$SHA" > compare.json \
|| { full "the push could not be compared with its previous commit"; exit 0; }
# The compare API lists at most 300 files; a longer list is
# treated as a change of everything.
if [ "$(jq '.files | length' compare.json)" -ge 300 ]; then
full "300 or more changed files"; exit 0
fi
jq -r '.files[] | .filename, (.previous_filename // empty)' compare.json > changed.txt ;;
*)
full "event $EVENT"; exit 0 ;;
esac
python3 .github/tools/classify_changes.py --github-output "$GITHUB_OUTPUT" < changed.txt
docs:
name: documentation and repository checks
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- name: Check version / xlings pin consistency
run: bash .github/tools/check_version_pins.sh
- name: Check modules/ wiring
run: bash .github/tools/check_modules_wiring.sh
- name: Check src/build/prepare* file lengths
run: bash .github/tools/check_file_lengths.sh
- name: Check no walk-derived path is narrowed directly
run: bash .github/tools/check_narrow_conversions.sh
- name: Where the CI assertions live
run: bash tools/lint-ci-assertions.sh
- name: Steps assert what their names say
env:
GH_TOKEN: ${{ github.token }}
run: |
python3 tests/scripts/test_check_workflow_assertions.py
python3 .github/tools/check_workflow_assertions.py --check-open
- name: The release canary runner runs each command under the named bash
run: python3 tests/scripts/test_release_canaries.py
- name: The protocol table of SPEC-007 names the engine's protocol
run: python3 tests/scripts/test_protocol_table.py
- name: The target matrix names every refusal
run: bash .github/tools/check_matrix_reasons.sh
- name: Check documentation style and bilingual parity
run: bash .github/tools/check_docs_style.sh
- name: Check documentation structure
run: bash .github/tools/check_docs_structure.sh
- name: Documented target tiers agree with the table
run: python3 .github/tools/check_target_tiers.py
- name: Reason tokens agree with the engine, and with the mirror
run: bash .github/tools/check_reason_tokens.sh
- name: The tools of the CI stages pass their fixtures
run: |
python3 tests/scripts/test_classify_changes.py
python3 tests/scripts/test_check_e2e_coverage.py
python3 tests/scripts/test_check_default_toolchain_docs.py
build-linux:
needs: changes
if: needs.changes.outputs.code == 'true'
uses: ./.github/workflows/build.yml
with:
host: linux-x86_64
runs-on: ubuntu-24.04
# What the Linux consumers install beyond the build's own toolchain: the
# e2e shards (musl, llvm, mingw-cross), the toolchain legs (musl, llvm).
prewarm: gcc 16.1.0-musl; llvm 22.1.8; mingw-cross 16.1.0
build-linux-arm:
needs: changes
if: needs.changes.outputs.code == 'true'
uses: ./.github/workflows/build.yml
with:
host: linux-aarch64
runs-on: ubuntu-24.04-arm
build-macos:
needs: changes
if: needs.changes.outputs.code == 'true'
uses: ./.github/workflows/build.yml
with:
host: macos-arm64
runs-on: macos-15
build-windows:
needs: changes
if: needs.changes.outputs.code == 'true'
uses: ./.github/workflows/build.yml
with:
host: windows-x86_64
runs-on: windows-latest
# The MinGW payload every Windows e2e shard installs.
prewarm: mingw 16.1.0
linux:
needs: build-linux
uses: ./.github/workflows/ci-linux.yml
linux-e2e:
needs: build-linux
uses: ./.github/workflows/ci-linux-e2e.yml
macos:
needs: build-macos
uses: ./.github/workflows/ci-macos.yml
with:
# R7: the known-red legs (#669) run where they can change a decision: on
# main, on dispatch, and on a pull request labelled `macos-27`. The label
# is read when the pull request is pushed to; adding it starts no run,
# because a run on every label of every pull request would cost a whole
# CI each time.
known-red: ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'macos-27') }}
macos-e2e:
needs: build-macos
uses: ./.github/workflows/ci-macos-e2e.yml
with:
known-red: ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'macos-27') }}
macos-ios:
needs: build-macos
uses: ./.github/workflows/ci-macos-ios.yml
windows:
needs: build-windows
uses: ./.github/workflows/ci-windows.yml
windows-e2e:
needs: build-windows
uses: ./.github/workflows/ci-windows-e2e.yml
windows-msvc-xlings:
needs: build-windows
uses: ./.github/workflows/ci-windows-msvc-xlings.yml
cross:
needs: [build-linux, build-windows]
uses: ./.github/workflows/cross-build-test.yml
target-matrix:
needs: [build-linux, build-linux-arm, build-macos, build-windows]
uses: ./.github/workflows/ci-target-matrix.yml
# Pull requests and dispatch only, as when it was a workflow of its own.
openkal:
needs: [build-linux, build-macos, build-windows]
if: github.event_name != 'push'
uses: ./.github/workflows/openkal-cross.yml
e2e-coverage:
name: every e2e test runs somewhere
needs: [changes, linux-e2e, windows-e2e, macos-e2e]
if: ${{ always() && needs.changes.outputs.code == 'true' && !cancelled() }}
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Fetch the shards' reports
uses: actions/download-artifact@v4
with:
pattern: e2e-report-*
path: reports
merge-multiple: true
- name: Every test ran, is run by a dedicated job, or is excused
run: python3 .github/tools/check_e2e_coverage.py --reports reports --timings-out timings
# The measured durations, merged per host, in the format of
# tests/e2e/timings/; refreshing those tables is copying these files.
- name: Upload the measured durations
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-timings
path: timings/
if-no-files-found: ignore
retention-days: 14