-
Notifications
You must be signed in to change notification settings - Fork 0
405 lines (389 loc) · 16.5 KB
/
Copy pathci.yml
File metadata and controls
405 lines (389 loc) · 16.5 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
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
# ClickDOOM CI.
#
# ClickHouse comes from docker-compose.yml through `make up`, so the image
# digest and the server configuration are pinned in that one file. Bumping the
# pin needs a `ci:` pull request with nightly deep-diff evidence.
#
# Every third-party action is pinned to a full commit SHA. `zizmor` fails the
# lint job if a pin or a token scope regresses.
name: ci
on:
pull_request:
push:
branches: [main]
# Superseded pull request runs are cancelled. Pushes to main are left to finish.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions: {}
env:
CLICKHOUSE_USER: default
# Local-only. This database holds an emulator's RAM and nothing secret.
# docker-compose.yml carries the same value.
CLICKHOUSE_PASSWORD: clickdoom
CLICKHOUSE_DATABASE: clickdoom
jobs:
lint:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- name: Non-Rust lint tools
# shellcheck, clang-format and actionlint have no cargo distribution.
# `make lint` installs typos and zizmor through cargo itself.
run: |
sudo apt-get update -q
sudo apt-get install -y -q shellcheck clang-format
curl -fsSL https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash \
| bash -s -- 1.7.12
sudo install -m 0755 actionlint /usr/local/bin/actionlint
- name: Every static check
# The same target a contributor runs, so CI and local cannot drift.
run: make lint
build-rom:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Build ROM (dockerized rv32 toolchain)
run: make -C rom
- name: Verify the ELF is byte-reproducible
# Through rom/Makefile, so the second build runs in the same pinned
# toolchain image under the same flags as the first. It rebuilds the
# ROM and then links once more, so the step costs two ROM builds.
run: make -C rom check-elf-reproducible
- name: Verify reproducible-build hash pin
# Recomputed here rather than calling `make -C rom check-pinned-hash`,
# so the pin is checked by something other than the tool that built it.
run: |
built=$(sha256sum rom/build/doom-rv32im.bin | cut -d' ' -f1)
pinned=$(cat rom/PINNED_HASH)
if [ "$built" != "$pinned" ]; then
echo "::error::ROM hash mismatch: built=$built pinned=$pinned."
echo "::error::Either the build went nondeterministic (P0) or the ROM changed without rom/PINNED_HASH being updated in the same PR."
exit 1
fi
- name: Upload ROM artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: rom
path: |
rom/build/doom-rv32im.bin
rom/build/doom-rv32im.elf
rom/build/manifest.json
build-refemu:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- uses: taiki-e/install-action@3f74d7c16a4242f1c95561e98edc25d36adb4375 # v2.87.12
with:
tool: cargo-nextest@0.9.140
- name: Build the reference emulator
run: cargo build --locked --release -p refemu
- name: Archive the reference emulator's ROM suites
# In release, the build they need. The `emulator` group runs them.
run: |
mkdir -p target/nextest
cargo nextest archive --locked --release -p refemu --features rom-tests \
--archive-file target/nextest/rom-suites.tar.zst
- name: Upload refemu artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: refemu
path: target/release/refemu
- name: Upload the ROM suites archive
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: rom-suites
path: target/nextest/rom-suites.tar.zst
build-native-tests:
# The native crate's test binaries with the live suites, compiled once
# into a nextest archive. The simulation groups run from it alone, so
# they start as soon as it exists.
runs-on: ubuntu-latest
permissions:
contents: read
env:
# The test binaries carry no debug info. A failing test still reports
# its panic message with the file and line; a backtrace names
# functions only. Set for the job so rust-cache keys on it.
CARGO_PROFILE_TEST_DEBUG: "0"
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- uses: taiki-e/install-action@3f74d7c16a4242f1c95561e98edc25d36adb4375 # v2.87.12
with:
tool: cargo-nextest@0.9.140
- name: Archive the suites
run: |
mkdir -p target/nextest
cargo nextest archive --locked -p clickdoom-native --features clickhouse-tests \
--archive-file target/nextest/native.tar.zst
- name: Upload the archive
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: native-tests
path: target/nextest/native.tar.zst
build-tests:
# Every other crate's test binaries with the live suites, compiled once
# into a nextest archive. Built as build-native-tests is.
runs-on: ubuntu-latest
permissions:
contents: read
env:
CARGO_PROFILE_TEST_DEBUG: "0"
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- uses: taiki-e/install-action@3f74d7c16a4242f1c95561e98edc25d36adb4375 # v2.87.12
with:
tool: cargo-nextest@0.9.140
- name: Archive the suites
run: |
mkdir -p target/nextest
cargo nextest archive --locked --workspace --exclude clickdoom-native \
--features clickhouse-tests --archive-file target/nextest/workspace.tar.zst
- name: Upload the archive
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: workspace-tests
path: target/nextest/workspace.tar.zst
test:
# Every suite, in the groups scripts/test-group.sh names, one runner per
# group with its own ClickHouse, run from the archives the build jobs
# made. The simulation groups are test-sim's; this job runs the rest.
# Throwaway databases only; never `clickdoom`. `make test` runs the same
# suites in one pass locally.
needs: [build-native-tests, build-tests, build-rom, build-refemu]
# The repository variables CLICKDOOM_TEST_RUNNER (a runs-on label) and
# CLICKDOOM_TEST_THREADS (tests per simulation group at once) pick the
# machine the groups run on; unset, a standard runner at four.
runs-on: ${{ vars.CLICKDOOM_TEST_RUNNER || 'ubuntu-latest' }}
permissions:
contents: read
# A backstop against a job that hangs. The slowest test job in the six
# main runs 36126756550 to 36132815983 took 7.1 minutes from its start
# to its end, and this is about twice that. It also sits above
# nextest's cutoff in .config/nextest.toml, 12 minutes, so a test that
# hangs is killed by nextest and reported by name before the job is
# cancelled.
timeout-minutes: 15
env:
TEST_THREADS: ${{ vars.CLICKDOOM_TEST_THREADS || '4' }}
strategy:
fail-fast: false
matrix:
group: [emulator, driver-native]
name: test (${{ matrix.group }})
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: taiki-e/install-action@3f74d7c16a4242f1c95561e98edc25d36adb4375 # v2.87.12
with:
tool: cargo-nextest@0.9.140
- name: Start the pinned ClickHouse
run: make up
# Each group downloads the archives scripts/test-group.sh runs it
# from, and a group that lacks one fails on the missing file.
- name: Download the other crates' suites
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: workspace-tests
path: target/nextest
- name: Download the ROM suites
if: matrix.group == 'emulator'
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: rom-suites
path: target/nextest
- name: Download ROM artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: rom
path: rom/build
- name: Download refemu artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: refemu
path: target/release
- name: The group's suites
env:
CLICKHOUSE_HOST: localhost
CLICKHOUSE_HTTP_PORT: "8123"
CLICKHOUSE_PASSWORD: clickdoom
GROUP: ${{ matrix.group }}
NEXTEST_ARCHIVE_DIR: target/nextest
run: |
chmod +x target/release/refemu
scripts/test-group.sh "$GROUP"
test-sim:
# The simulation groups, as `test` runs its groups, from the native
# crate's archive alone. They need no ROM and no reference emulator.
needs: [build-native-tests]
runs-on: ${{ vars.CLICKDOOM_TEST_RUNNER || 'ubuntu-latest' }}
permissions:
contents: read
# The same backstop as `test`.
timeout-minutes: 15
env:
TEST_THREADS: ${{ vars.CLICKDOOM_TEST_THREADS || '4' }}
strategy:
fail-fast: false
matrix:
group: [native-sim-a, native-sim-b, native-sim-c, native-sim-d]
name: test (${{ matrix.group }})
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: taiki-e/install-action@3f74d7c16a4242f1c95561e98edc25d36adb4375 # v2.87.12
with:
tool: cargo-nextest@0.9.140
- name: Start the pinned ClickHouse
run: make up
- name: Download the native crate's suites
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: native-tests
path: target/nextest
- name: The group's suites
env:
CLICKHOUSE_HOST: localhost
CLICKHOUSE_HTTP_PORT: "8123"
CLICKHOUSE_PASSWORD: clickdoom
GROUP: ${{ matrix.group }}
NEXTEST_ARCHIVE_DIR: target/nextest
run: scripts/test-group.sh "$GROUP"
test-groups:
# Every test in the archives is selected by exactly one group of the
# test matrix, and every group selects at least one test. Lists the
# tests and runs none, so it needs no ClickHouse.
needs: [build-native-tests, build-tests, build-refemu]
runs-on: ubuntu-latest
permissions:
contents: read
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: taiki-e/install-action@3f74d7c16a4242f1c95561e98edc25d36adb4375 # v2.87.12
with:
tool: cargo-nextest@0.9.140
- name: Download the archives
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: "*-{tests,suites}"
merge-multiple: true
path: target/nextest
- name: Every test in exactly one group
env:
NEXTEST_ARCHIVE_DIR: target/nextest
run: scripts/test-group.sh --check
native-smoke:
# The driver's own path to a frame: load the schema and the committed
# probe fixture, render the frame the fixture's metadata names, compare
# against the hash the probe recorded beside that frame's row.
#
# The renderer's pixels are pinned by `native/tests/render_live.rs` in
# the native-sim-d group. This job checks the load and render path around
# them and the fixture's own metadata. No ROM is built: the target's
# prerequisites are `up` and `build-clickdoom`.
runs-on: ubuntu-latest
permissions:
contents: read
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- name: Render the first gameplay frame and check its hash
run: make native-smoke
differential-smoke:
# N instructions of the DOOM ROM on refemu against sqlcpu, comparing the
# full checkpoint trace at every CHECKPOINT_INTERVAL rather than a sample.
#
# A GREEN RUN HERE MEANS "registers and control flow agreed over 24
# comparisons". IT DOES NOT MEAN "the engines agree". 100,000 instructions
# at CHECKPOINT_INTERVAL=4,096 spacing reaches no RAM_HASH_INTERVAL
# (1,048,576) boundary, so `ramhash` and `fbhash` are never compared. That
# leaves uncaught a corrupt RAM word which keeps every register
# bit-identical. Only nightly's deep-diff exercises memory.
#
# N=100,000 because clickdoom emulation diff clamps every sqlcpu batch to
# <= CHECKPOINT_INTERVAL so it cannot skip a checkpoint row. Measured at
# 990 instructions/sec, 101 seconds. Reaching the first memory comparison
# would take about 13.9 minutes on every pull request. timeout-minutes is a
# safety net for runner variance, not a substitute for that measurement.
needs: [build-rom, build-refemu]
runs-on: ubuntu-latest
permissions:
contents: read
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# The cache restores target/ whole, so it goes before anything is
# downloaded into it.
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- name: Start the pinned ClickHouse
run: make up
- name: Download ROM artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: rom
path: rom/build
- name: Download refemu artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: refemu
path: target/release
- name: Build clickdoom
run: cargo build --locked --release -p clickdoom-driver
- name: Smoke diff (100,000 instructions, full checkpoint trace)
run: |
chmod +x target/release/refemu
./target/release/clickdoom emulation diff 100000 --host localhost --port 8123 \
--bin rom/build/doom-rv32im.bin --manifest rom/build/manifest.json \
--refemu-bin target/release/refemu
ci-passed:
# Succeeds only when every other job in this workflow succeeded. A job that
# failed, was skipped or was cancelled fails it. Each test matrix reports
# one result, which is a failure if any of its groups failed. A job added
# to this workflow goes in `needs`, or a branch rule that requires this
# check does not wait for it.
needs: [lint, build-rom, build-refemu, build-native-tests, build-tests, test, test-sim, test-groups, native-smoke, differential-smoke]
if: always()
runs-on: ubuntu-latest
permissions: {}
timeout-minutes: 5
steps:
- name: Every needed job succeeded
env:
NEEDS: ${{ toJSON(needs) }}
run: |
echo "$NEEDS" | jq -r 'to_entries[] | "\(.key): \(.value.result)"'
failed=$(echo "$NEEDS" | jq -r '[to_entries[] | select(.value.result != "success") | .key] | join(" ")')
if [ -n "$failed" ]; then
echo "::error::Not successful: $failed"
exit 1
fi