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
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
# ======================================================
# Continuous Integration: making sure the codebase works
# ======================================================
#
# This workflow tests modifications to 'domain', ensuring that 'domain' can
# be used by others successfully. It verifies certain aspects of the codebase,
# such as the formatting and feature flag combinations, and runs the full test
# suite. It runs on Ubuntu and Windows.
#
# NOTE: We don't test on MacOS right now because the CI runner is expensive
# and at this moment (2026-07-09) 'domain' does not have MacOS-specific code.
# It can be re-enabled if necessary.
#
# --- Rust versions
#
# The data cached in `target/` is specific to the Rust compiler version that
# generated it, so we need to be careful to use the same compiler version when
# we re-use cached `target/`s.
#
# Where possible, we use the compiler version that comes pre-installed on the
# CI runners. We assume this is fairly close to the latest Rust stable, and
# that it does not change often.
#
# NOTE: We use `actions-rust-lang/setup-rust-toolchain` to help set up
# Rust, but it does not support using the pre-installed CI version. We
# would like to use it in these cases because it also configures GitHub to
# provide annotations for Rust tool output; we copy their code and do that
# configuration manually (see `rust-annotations.json`).
# See <https://github.com/actions-rust-lang/setup-rust-toolchain/issues/66>.
#
# --- Caches
#
# `$os-$arch-system-$run_id` caches the `target/` generated by `cargo test`
# with all features enabled, using the Rust version pre-installed on the CI
# runner. `os` is in `{Linux, Windows}`, `arch` is `X64`.
#
# `$os-$arch-nightly-clippy-$run_id` caches the `target/` generated by `cargo
# clippy` with all features enabled, using a nightly Rust version. The Rust
# version (in `rustup toolchain` format) is saved to `target/rust-version`.
# `os` is `Linux`, `arch` is `X64`.
#
# NOTE: We assume the Rust version installed on the CI runner does not change
# often. In theory, we could write down the Rust version in the cache and
# explicitly install that version when restoring from the cache; but this
# probably adds unnecessary overhead.
#
# NOTE: GitHub treats cache entries as immutable (although it _does_ allow
# explicitly removing and re-adding them). We include the run ID so the cache
# key is unique every time; when restoring from the cache, we don't specify
# the run ID, and GitHub will automatically find the most recent cache entry
# using a prefix of the full key.
name: CI
# When the workflow runs
# ----------------------
on:
# Execute when a pull request is (re-) opened or its head changes (e.g. new
# commits are added or the commit history is rewritten) ... but only if
# build-related files change.
pull_request:
paths:
- '**.rs'
- 'Cargo.toml'
- 'Cargo.lock'
- '.github/workflows/ci.yml'
# If a pull request is merged, at least one commit is added to the target
# branch. If the target is another pull request, it will be caught by the
# above event. We miss PRs that merge to a non-PR branch, except for the
# 'main' branch.
# Execute when a commit is pushed to 'main' (including merged PRs) or to a
# release tag ... but only if build-related files change.
push:
branches:
- 'main'
- 'releases/**'
paths:
- '**.rs'
- 'Cargo.toml'
- 'Cargo.lock'
- '.github/workflows/ci.yml'
# Rebuild 'main' every week. This will account for changes to dependencies
# and to Rust, either of which can trigger new failures. Rust releases are
# every 6 weeks, on a Thursday; this event runs every Friday.
schedule:
- cron: '0 10 * * FRI'
defaults:
run:
shell: bash
# Jobs
# ----------------------------------------------------------------------------
jobs:
# Check Formatting
# ----------------
#
# NOTE: This job is run even if no '.rs' files have changed. Inserting such
# a check would require using a separate workflow file or using third-party
# actions. Most commits do change '.rs' files, and 'cargo-fmt' is pretty
# fast, so optimizing this is not necessary.
check-fmt:
name: Check formatting
runs-on: ubuntu-latest
steps:
# Load the repository.
- name: Checkout repository
uses: actions/checkout@v7
# Don't set up a Rust toolchain; use whatever is on the runner already.
# It will be close enough to the latest Rust stable.
# Enable a problem matcher to get inline annotations.
- name: Enable annotations
run: echo "::add-matcher::.github/workflows/rust-annotations.json"
# Don't restore from the cache; it's not needed here.
# Do the actual formatting check.
- name: Check formatting
run: cargo fmt --all -- --check
# Check
# -----
#
# Rust does not provide any way to check that all possible feature flag
# combinations will succeed, so we need to try them manually here. We assume
# this choice is not influenced by the OS or Rust version. For each feature
# flag combination, we check that everything compiles and that documentation
# succeeds.
check:
name: Check
strategy:
matrix:
features:
# We always enable `domain::new`.
# TODO: The following currently break:
# - "--no-default-features -F unstable-new"
# - "--no-default-features -F unstable-new,alloc"
- "-F unstable-new" # + default features
- "--all-features"
runs-on: ubuntu-latest
env:
RUSTDOCFLAGS: "-D warnings"
steps:
# Load the repository.
- name: Checkout repository
uses: actions/checkout@v7
# Restore a cache of dependencies and 'target'.
- name: Restore a dependency cache
id: cache-restore
uses: actions/cache/restore@v6
with:
path: |
~/.cargo
target/
# Cache by OS and Rust version. Assume the "system" version (i.e.
# whatever is on the CI runner) doesn't change that often. We _could_
# figure out what the CI runner has and key by that, but that would
# probably add unnecessary overhead.
key: ${{ runner.os }}-${{ runner.arch }}-system-
# Don't set up a Rust toolchain; use whatever is on the runner already.
# It will be close enough to the latest Rust stable.
# Enable a problem matcher to get inline annotations.
- name: Enable annotations
run: echo "::add-matcher::.github/workflows/rust-annotations.json"
- name: Check
run: cargo check ${{ matrix.features }}
# `cargo doc` should benefit from the existing work done by `check`, so
# run it immediately afterwards. This eliminates the need for a separate
# `cargo doc` job.
- name: Doc
run: cargo doc --no-deps ${{ matrix.features }}
# Check Examples
# --------------
#
# Make sure examples compile, with the specific feature flags `Cargo.toml`
# indicates they need.
check-examples:
name: Check examples
runs-on: ubuntu-latest
steps:
# Load the repository.
- name: Checkout repository
uses: actions/checkout@v7
# Restore a cache of dependencies and 'target'.
- name: Restore a dependency cache
id: cache-restore
uses: actions/cache/restore@v6
with:
path: |
~/.cargo
target/
# Cache by OS and Rust version. Assume the "system" version (which
# corresponds to whatever is on the CI runner) doesn't change that
# often. We _could_ figure out what the CI runner has and key by that,
# but that would probably add unnecessary overhead.
key: ${{ runner.os }}-${{ runner.arch }}-system-
# Don't set up a Rust toolchain; use whatever is on the runner already.
# It will be close enough to the latest Rust stable.
# Enable a problem matcher to get inline annotations.
- name: Enable annotations
run: echo "::add-matcher::.github/workflows/rust-annotations.json"
# Check the required feature flags for every example.
- name: Check required features of examples
run: |
# Scrape crate metadata and construct the right 'check' commands.
# Cargo doesn't have an option to select the right features for us.
# See: https://github.com/rust-lang/cargo/issues/4663
cargo metadata --no-deps --format-version 1 \
| jq -r '.packages[].targets[]|select(.kind|any(.=="example"))|{name,features:(.["required-features"]+[]|join(","))}|"\(.name) \(.features)"' \
| while read -r name features; do
cargo check --example=$name --no-default-features --features=$features
done
# Check Minimal Versions
# ----------------------
#
# Ensure that 'domain' compiles with the oldest compatible versions of all
# packages, even those 'domain' depends upon indirectly. This is compiled
# with the declared MSRV to make sure Clippy's minimal versions lint didn't
# miss anything.
check-minimal-versions:
name: Check minimal versions
runs-on: ubuntu-latest
env:
RUSTFLAGS: "-D warnings"
steps:
# Load the repository.
- name: Checkout repository
uses: actions/checkout@v7
# Determine the MSRV.
- name: Determine MSRV
id: determine-msrv
run: |
msrv=`cargo metadata --no-deps --format-version 1 | jq -r '.packages[]|select(.name=="domain")|.rust_version'`
echo "msrv=$msrv" >> "$GITHUB_OUTPUT"
# TODO: Caching?
# Set up the Rust toolchain.
- name: Set up Rust (at MSRV)
id: setup-rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: nightly, ${{ steps.determine-msrv.outputs.msrv }}
cache: false
matcher: false
# Enable a problem matcher to get inline annotations.
- name: Enable annotations
run: echo "::add-matcher::.github/workflows/rust-annotations.json"
# Lock all dependencies to their minimal versions.
- name: Lock dependencies to minimal versions
run: cargo +nightly update -Z minimal-versions
# Check that 'domain' compiles.
- name: Check
run: cargo check --all-targets --all-features --locked
# Clippy
# ------
#
# We run Clippy separately, and only on nightly Rust because it offers a
# superset of the lints.
#
# 'cargo clippy' and 'cargo build' can share some state for fast execution,
# but it's faster to execute them in parallel than to establish an ordering
# between them.
clippy:
name: Clippy
runs-on: ubuntu-latest
env:
RUSTFLAGS: "-D warnings"
steps:
# Load the repository.
- name: Checkout repository
uses: actions/checkout@v7
# Restore a cached `target/`.
- name: Restore from cache
if: github.event_name != 'schedule'
id: cache-restore
uses: actions/cache/restore@v6
with:
path: |
~/.cargo
target/
# Cache by OS. Use a recently cached version of Rust nightly.
key: ${{ runner.os }}-${{ runner.arch }}-nightly-clippy-
# Determine the Rust version provided by the cache.
#
# If we didn't restore from cache, use the latest nightly.
- name: Determine the cached Rust version
id: determine-rust-version
run: |
if [[ -f target/rust-version ]]; then
echo "rust-version=$(cat target/rust-version)" >> "$GITHUB_OUTPUT"
else
echo "rust-version=nightly" >> "$GITHUB_OUTPUT"
fi
# Set up the Rust toolchain.
- name: Set up Rust nightly
id: setup-rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: ${{ steps.determine-rust-version.outputs.rust-version }}
components: clippy
cache: false
matcher: false
# Enable a problem matcher to get inline annotations.
- name: Enable annotations
run: echo "::add-matcher::.github/workflows/rust-annotations.json"
# Do the actually Clippy run.
- name: Check Clippy
run: cargo clippy --all-targets --all-features
# Note the Rust version in the cache.
- name: Note Rust version for the cache
if: github.event_name == 'schedule'
run: |
rustup toolchain list | grep nightly- | tee target/rust-version
# Save to the cache every week.
- name: Save to the cache
uses: actions/cache/save@v6
if: github.event_name == 'schedule'
with:
path: |
~/.cargo
target/
key: ${{ runner.os }}-${{ runner.arch }}-nightly-clippy-${{ github.run_id }}
# Test
# ----
#
# Ensure that 'domain' compiles and its test suite passes.
test:
name: Test
strategy:
matrix:
os:
runs-on: ${{ matrix.os }}
env:
RUSTFLAGS: "-D warnings"
DOMAIN_FEATURES: "--all-features"
steps:
# Load the repository.
- name: Checkout repository
uses: actions/checkout@v7
# Restore a cached `target/`.
- name: Restore from cache
id: cache-restore
uses: actions/cache/restore@v6
with:
path: |
~/.cargo
target/
key: ${{ runner.os }}-${{ runner.arch }}-system-
# Don't set up a Rust toolchain; use whatever is on the runner already.
# It will be close enough to the latest Rust stable.
# Enable a problem matcher to get inline annotations.
- name: Enable annotations
run: echo "::add-matcher::.github/workflows/rust-annotations.json"
# Prepare the environment on Windows
- name: Prepare Windows environment
if: matrix.os == 'windows-latest'
shell: bash
run: |
# Filter out `openssl` because it's a pain to compile on Windows.
# Cargo doesn't support enabling all but one feature, so determine the
# complete feature list and filter it out manually.
features=`cargo metadata --no-deps --format-version 1 | jq -r '.packages[]|select(.name == "domain")|.features|keys|map(select(.!="openssl"))|join(",")'`
# Overwrite the 'DOMAIN_FEATURES' environment variable.
echo "DOMAIN_FEATURES=--features=$features" >> "$GITHUB_ENV"
# See <https://github.com/actions/runner-images/issues/12432>
echo "CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER=rust-lld" >> "$GITHUB_ENV"
# Build and run the test suite.
- name: Test
run: cargo test --all-targets $DOMAIN_FEATURES
# Test docs.
#
# Passing `--all-targets` to `cargo test` (as in the previous step)
# prevents doc tests from running.
# See <https://github.com/rust-lang/cargo/issues/6669>.
- name: Test docs
run: cargo test --doc $DOMAIN_FEATURES
# Save to the cache.
- name: Save to the cache
if: github.event_name == 'schedule'
uses: actions/cache/save@v6
with:
path: |
~/.cargo
target/
key: ${{ runner.os }}-${{ runner.arch }}-system-${{ github.run_id }}
# TODO: Use 'cargo-semver-checks' on releases.