-
Notifications
You must be signed in to change notification settings - Fork 93
511 lines (442 loc) · 23.2 KB
/
Copy pathci.yml
File metadata and controls
511 lines (442 loc) · 23.2 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
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
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
name: CI
on:
push:
branches: [main]
pull_request:
# Run required checks on merge queue groups so queued PRs can land.
merge_group:
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
# protoc v27+ is required for editions 2023 support (utf8_validation.proto).
# Ubuntu's apt protobuf-compiler is v21.12 — too old. Keep this in sync
# with the conformance tools image (see conformance/Dockerfile.tools).
PROTOC_VERSION: "33.5"
# SHA256 of protoc-${PROTOC_VERSION}-linux-x86_64.zip from the upstream
# protobuf release. Bump together with PROTOC_VERSION.
PROTOC_SHA256: "24e58fb231d50306ee28491f33a170301e99540f7e29ca461e0e80fd1239f8d1"
jobs:
# ── Fast feedback: lint + test on stable ─────────────────────────────────
lint-and-test:
runs-on: ubuntu-latest
# 15 rather than 10: documenting the workspace (buffa-test's generated
# modules are large) adds a few minutes on top of a cold-cache build.
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
# Pinned (not @stable) so rustfmt/clippy match rust-toolchain.toml and
# local runs agree with CI. Bump together with that file.
#
# NOTE: the action only runs `rustup default`, which rust-toolchain.toml
# overrides — so the file is what actually selects the toolchain for
# cargo. The pin here must NAME THE SAME VERSION as the file so the
# components installed by this step belong to the toolchain cargo uses.
- uses: dtolnay/rust-toolchain@1.95
with:
components: clippy, rustfmt
- name: Install protoc
run: |
curl -fsSL "https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-x86_64.zip" -o /tmp/protoc.zip
echo "${PROTOC_SHA256} /tmp/protoc.zip" | sha256sum -c -
sudo unzip -o /tmp/protoc.zip -d /usr/local
protoc --version
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 (sha-pinned)
- name: Rustfmt
run: cargo fmt --all --check
- name: Clippy
run: cargo clippy --workspace --all-targets -- -D warnings
- name: Tests
run: cargo test --workspace
# Optional features must keep compiling — #88 was a feature-flag-only
# break that escaped the default-features test pass above. cargo check
# is sufficient to catch compile breaks without doubling the test time.
- name: Check all features
run: cargo check --workspace --all-features
# Rustdoc warnings are errors, in generated docs too: buffa-test's
# build include!s codegen output (views, lazy views, json, reflect), so
# documenting the workspace lints emitted doc comments as well as
# hand-written ones. Covers workspace members only — excluded crates
# (benchmarks/, most examples/) are not documented. (#295)
- name: Rustdoc (deny warnings)
run: cargo doc --workspace --all-features --no-deps
env:
RUSTDOCFLAGS: -D warnings
# `gate_impls_on_crate_features` codegen output: generate the buffa-test
# fixture protos with the gate on, then `cargo check` the result with
# each `{json, views, text}` subset. Catches name-resolution bugs the
# string-match tests cannot — a `cfg`-gated item referenced from an
# ungated context survives `syn::parse_file` but fails this. Marked
# `--ignored` so the default workspace test stays fast. (#113)
- name: Feature-gating compile matrix
run: cargo test -p buffa-codegen --test feature_gating_compile -- --ignored
# Shared-pool mode emits each package's descriptor_pool() as a relative
# `super::…::__buffa_fds` path, so the hop count has to match the module
# nesting exactly. Only a real compiled tree proves it resolves; the
# string-level tests check the arithmetic against itself. Also `#[ignore]`d
# because it spawns cargo. (#311)
- name: Shared descriptor pool compile
run: cargo test -p buffa-codegen --test shared_pool_compile -- --ignored
# `examples/bsr-quickstart` is outside the workspace, and its `src/gen/`
# is checked-in output from the published BSR plugin, not in-tree
# codegen. Its runtime deps are pinned to the plugin's release (see its
# `Cargo.toml`), so this fails if the pins diverge or `src/gen/` is
# hand-edited. `--locked` keeps new upstream releases of its transitive
# deps from changing the result. (#427)
- name: Check bsr-quickstart example
run: cargo check --locked --manifest-path examples/bsr-quickstart/Cargo.toml
# ── Markdown lint ────────────────────────────────────────────────────────
# Same pinned markdownlint-cli version as `task lint-md`. Node is
# preinstalled on ubuntu-latest so no setup-node step is needed.
lint-markdown:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- run: npx --yes markdownlint-cli@0.47.0 '**/*.md' --ignore target --ignore .claude
# ── Workflow lint ────────────────────────────────────────────────────────
# A malformed workflow does not fail a normal CI job — GitHub records a
# startup failure on the run instead, which is easy to miss and only surfaces
# when the workflow was meant to do something. A duplicate `env:` key reached
# main in publish-crates.yml exactly that way: PyYAML's default loader accepts
# duplicate keys and keeps the last one, so it validated locally while the
# publish workflow silently could not start. actionlint parses the way Actions
# does and rejects it.
lint-workflows:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Install actionlint
env:
ACTIONLINT_VERSION: 1.7.7
# SHA256 of actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz from
# the upstream release's checksums.txt. Bump together with the
# version.
ACTIONLINT_SHA256: 023070a287cd8cccd71515fedc843f1985bf96c436b7effaecce67290e7e0757
run: |
curl -fsSL -o /tmp/actionlint.tar.gz \
"https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz"
echo "${ACTIONLINT_SHA256} /tmp/actionlint.tar.gz" | sha256sum -c -
mkdir -p "${RUNNER_TEMP}/bin"
tar -xzf /tmp/actionlint.tar.gz -C "${RUNNER_TEMP}/bin" actionlint
echo "${RUNNER_TEMP}/bin" >> "${GITHUB_PATH}"
"${RUNNER_TEMP}/bin/actionlint" --version
- name: Lint workflows
run: actionlint -color
# ── Publish list ─────────────────────────────────────────────────────────
# Adding a publishable crate to the workspace without adding it to
# .github/publish-order.txt means the release silently skips it. That is how
# buffa-remote-derive was announced in the 0.9.0 changelog and never reached
# crates.io. The publish workflow checks this too, but only on a v* tag —
# running it here fails the PR that introduces the crate instead.
check-publish-list:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
# cargo metadata only reads manifests, so the pinned dev toolchain is not
# needed; stable avoids downloading it just for this check.
- name: Check publish coverage
env:
RUSTUP_TOOLCHAIN: stable
run: ./scripts/check-publish-coverage.sh
# ── Changelog check ──────────────────────────────────────────────────────
# CHANGELOG.md is generated from .changes/ via changie. Fail if it has been
# edited directly or is stale relative to the per-version source files.
check-changelog:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- name: Install changie
env:
CHANGIE_VERSION: 1.24.2
# SHA256 of changie_${CHANGIE_VERSION}_linux_amd64.tar.gz from the
# upstream release's checksums.txt. Bump together with the version.
CHANGIE_SHA256: 31535a9d8dc548d6d8f315762bfd5b1fba34e707b7600748c8bb8a609649007d
run: |
curl -fsSL -o /tmp/changie.tar.gz \
"https://github.com/miniscruff/changie/releases/download/v${CHANGIE_VERSION}/changie_${CHANGIE_VERSION}_linux_amd64.tar.gz"
echo "${CHANGIE_SHA256} /tmp/changie.tar.gz" | sha256sum -c -
mkdir -p "${RUNNER_TEMP}/bin"
tar -xzf /tmp/changie.tar.gz -C "${RUNNER_TEMP}/bin" changie
echo "${RUNNER_TEMP}/bin" >> "${GITHUB_PATH}"
"${RUNNER_TEMP}/bin/changie" --version
- name: Verify CHANGELOG.md is in sync
run: |
changie merge
# The message below is single-quoted on purpose: its backticks are
# literal markdown for the reader, not command substitution.
# shellcheck disable=SC2016
git diff --exit-code -- CHANGELOG.md || {
echo '::error file=CHANGELOG.md::CHANGELOG.md is out of sync. Do not edit it directly — add a fragment via `task changelog-new` (see CONTRIBUTING.md), or run `task changelog-merge` if you edited a .changes/ file.'
exit 1
}
# ── MSRV check ───────────────────────────────────────────────────────────
msrv-check:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
# The checked-in Cargo.lock is written by the pinned dev toolchain and
# may resolve transitive deps (e.g. getrandom, clap_lex) to versions
# whose manifests cargo 1.75 cannot parse (edition2024). Regenerate with
# stable cargo's MSRV-aware resolver so transitive deps satisfy
# rust-version = 1.75; cargo writes a v3 lockfile because the workspace
# rust-version is below 1.78.
- name: Regenerate MSRV-compatible lockfile
env:
CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS: fallback
run: rm -f Cargo.lock && cargo generate-lockfile
- uses: dtolnay/rust-toolchain@master
with:
toolchain: '1.75'
- name: Install protoc
run: |
curl -fsSL "https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-x86_64.zip" -o /tmp/protoc.zip
echo "${PROTOC_SHA256} /tmp/protoc.zip" | sha256sum -c -
sudo unzip -o /tmp/protoc.zip -d /usr/local
protoc --version
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 (sha-pinned)
# rust-toolchain.toml pins the dev toolchain and overrides `rustup
# default`, so RUSTUP_TOOLCHAIN is set explicitly to outrank the file.
- name: Check (MSRV 1.75)
env:
RUSTUP_TOOLCHAIN: '1.75'
run: cargo check --workspace --all-targets --locked
# ── Miri: proves the `set_len`-publishing `unsafe` reads no uninit memory ──
# SizeCache stores its inline slots as `MaybeUninit<u32>` to avoid zeroing the
# whole array on every encode; `consume_next` reads them via `assume_init`.
# `copy_into_spare` (types.rs) fills a Vec's spare capacity chunk by chunk and
# publishes the length afterwards, including on the unwind paths its tests
# drive. Miri tracks per-byte init state, so this job mechanically verifies
# both invariants on every path those two test modules exercise. Scoped to
# them to keep the (interpreted, slow) run fast — under a minute steady state,
# so it runs on every PR as a blocking gate rather than on a nightly schedule.
#
# The nightly is PINNED (not floating): a required check must not be broken by
# an unrelated bad nightly. Bump MIRI_TOOLCHAIN occasionally; the sysroot
# cache key follows it. `cargo +<toolchain>` overrides rust-toolchain.toml.
miri:
runs-on: ubuntu-latest
timeout-minutes: 15
env:
MIRI_TOOLCHAIN: nightly-2026-02-27
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.MIRI_TOOLCHAIN }}
components: miri
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 (sha-pinned)
# The Miri std sysroot (~100 MB) is the only slow part of a cold run
# (~1-3 min to build); rust-cache does not cover it. Cache it keyed on the
# pinned toolchain so steady-state runs skip the build.
- name: Cache Miri sysroot
uses: actions/cache@v4
with:
path: ~/.cache/miri
key: miri-sysroot-${{ runner.os }}-${{ env.MIRI_TOOLCHAIN }}
- name: Miri setup (build/restore sysroot)
run: cargo +${{ env.MIRI_TOOLCHAIN }} miri setup
- name: Miri (size_cache and copy_into_spare soundness)
run: cargo +${{ env.MIRI_TOOLCHAIN }} miri test -p buffa -- size_cache copy_into_spare
# OwnedView: the view borrows from the Bytes it is stored beside, with
# forged 'static lifetimes hidden behind a MaybeDangling wrapper. Miri
# turns a wrong field order (a dangling borrow while the view's
# destructor runs), a second drop of the view on the into_bytes unwind
# path, or a missing MaybeDangling (a protector on the forged borrows
# when an OwnedView is passed by value) into a hard error.
# `owned_view_lifetime_parametric_contract` adds the Debug, Clone,
# PartialEq, reborrow and to_owned_message paths on a hand-written view.
- name: Miri (OwnedView soundness)
run: >-
cargo +${{ env.MIRI_TOOLCHAIN }} miri test -p buffa --
owned_view_drop owned_view_into_bytes owned_view_lifetime_parametric_contract
# ── no_std and 32-bit compilation checks ─────────────────────────────────
check-nostd:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
# Must name the same version as rust-toolchain.toml (which selects the
# toolchain cargo uses) so the cross-targets installed here belong to
# that toolchain. With @stable, the targets land on a toolchain the
# file then bypasses, and the bare-metal check fails with E0463.
- uses: dtolnay/rust-toolchain@1.95
with:
targets: i686-unknown-linux-gnu, thumbv7em-none-eabihf
- name: Install protoc
run: |
curl -fsSL "https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-x86_64.zip" -o /tmp/protoc.zip
echo "${PROTOC_SHA256} /tmp/protoc.zip" | sha256sum -c -
sudo unzip -o /tmp/protoc.zip -d /usr/local
protoc --version
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 (sha-pinned)
- name: Check buffa no_std (host)
run: cargo check -p buffa --no-default-features
- name: Check buffa no_std (bare-metal ARM)
run: cargo check -p buffa --no-default-features --target thumbv7em-none-eabihf
- name: Check buffa-types no_std
run: cargo check -p buffa-types --no-default-features
- name: Check buffa-types no_std + chrono
run: cargo check -p buffa-types --no-default-features --features chrono
- name: Check buffa-descriptor no_std
run: cargo check -p buffa-descriptor --no-default-features
- name: Check workspace 32-bit
run: cargo check --workspace --target i686-unknown-linux-gnu
# ── Verify checked-in generated code is up to date ───────────────────────
check-generated-code:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Install protoc
run: |
curl -fsSL "https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-x86_64.zip" -o /tmp/protoc.zip
echo "${PROTOC_SHA256} /tmp/protoc.zip" | sha256sum -c -
sudo unzip -o /tmp/protoc.zip -d /usr/local
protoc --version
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 (sha-pinned)
- name: Regenerate bootstrap descriptor types
# Proto sources are vendored in buffa-descriptor/protos/ (pinned),
# so this is independent of the installed protoc's bundled includes.
run: scripts/gen-bootstrap-types.sh
- name: Generate descriptor set (WKTs)
run: |
# WKT protos are vendored in buffa-types/protos (NOT read from
# /usr/local/include) so the checked-in code is pinned to a
# specific proto source, not whatever protoc version ships.
protoc --descriptor_set_out=/tmp/wkt.pb --include_imports \
--include_source_info \
-I buffa-types/protos \
google/protobuf/any.proto \
google/protobuf/api.proto \
google/protobuf/duration.proto \
google/protobuf/empty.proto \
google/protobuf/field_mask.proto \
google/protobuf/source_context.proto \
google/protobuf/struct.proto \
google/protobuf/timestamp.proto \
google/protobuf/type.proto \
google/protobuf/wrappers.proto
- name: Regenerate WKT types
run: cargo run -p buffa-codegen --bin gen_wkt_types -- /tmp/wkt.pb buffa-types/src/generated
- name: Check for differences
run: |
if ! git diff --exit-code buffa-descriptor/src/generated/ buffa-types/src/generated/; then
echo "::error::Checked-in generated code is stale. Run 'task gen-wkt-types' and/or regenerate bootstrap types, then commit."
exit 1
fi
# ── Conformance test suite ───────────────────────────────────────────────
# Builds conformance binaries on the runner (cached via rust-cache) and
# extracts the conformance_test_runner + proto files from the tools image.
conformance:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 (sha-pinned)
with:
workspaces: conformance
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 (sha-pinned)
- name: Build tools image (cached)
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2 (sha-pinned)
with:
context: .
file: conformance/Dockerfile.tools
load: true
tags: buffa/tools:local
cache-from: type=gha,scope=tools-image
# Only main pushes save the cache. The cache service already
# isolates branches (a PR-written entry is not restorable from
# main), so this is quota hygiene plus defense in depth rather
# than the poisoning boundary itself: PR and merge-queue saves
# count against the repo's shared 10GB cache and can LRU-evict
# the main-scoped entries every PR restores from — merge_group
# writes land on an ephemeral queue ref nothing can ever restore.
# Cost: a PR that edits Dockerfile.tools rebuilds cold on every
# push instead of only the first; such PRs are rare.
cache-to: ${{ github.event_name == 'push' && 'type=gha,scope=tools-image,mode=max' || '' }}
- name: Extract tools from image
run: |
CID=$(docker create buffa/tools:local /dev/null)
docker cp "$CID:/conformance_test_runner" /usr/local/bin/conformance_test_runner
docker cp "$CID:/protoc" /usr/local/bin/protoc
docker cp "$CID:/protos/." conformance/protos/
docker rm "$CID"
chmod +x /usr/local/bin/conformance_test_runner /usr/local/bin/protoc
# Both builds use the same target dir so build-script artifacts
# (buffa-codegen, buffa-build, syn, etc.) are compiled only once.
# The std binary is copied out before the no_std build overwrites it.
- name: Determine host target
id: host
run: echo "triple=$(rustc -vV | grep '^host:' | cut -d' ' -f2)" >> "$GITHUB_OUTPUT"
- name: Build conformance binary (std)
run: >-
cargo build --release --manifest-path conformance/Cargo.toml
--target ${{ steps.host.outputs.triple }}
- name: Save std binary
run: cp conformance/target/${{ steps.host.outputs.triple }}/release/conformance /tmp/buffa-conformance-std
- name: Build conformance binary (no_std)
run: >-
cargo build --release --manifest-path conformance/Cargo.toml
--no-default-features --target ${{ steps.host.outputs.triple }}
- name: Run conformance tests (std)
run: >-
conformance_test_runner
--failure_list conformance/known_failures.txt
--maximum_edition 2024
/tmp/buffa-conformance-std
- name: Run conformance tests (no_std)
run: >-
conformance_test_runner
--failure_list conformance/known_failures_nostd.txt
--maximum_edition 2024
conformance/target/${{ steps.host.outputs.triple }}/release/conformance
- name: Run conformance tests (via-view)
env:
BUFFA_VIA_VIEW: "1"
run: >-
conformance_test_runner
--failure_list conformance/known_failures_view.txt
--maximum_edition 2024
/tmp/buffa-conformance-std
- name: Run conformance tests (via-lazy)
env:
BUFFA_VIA_LAZY: "1"
run: >-
conformance_test_runner
--failure_list conformance/known_failures_lazy.txt
--maximum_edition 2024
/tmp/buffa-conformance-std
- name: Run conformance tests (view-json)
env:
BUFFA_VIEW_JSON: "1"
run: >-
conformance_test_runner
--failure_list conformance/known_failures_view_json.txt
--maximum_edition 2024
/tmp/buffa-conformance-std
- name: Run conformance tests (via-reflect)
env:
BUFFA_VIA_REFLECT: "1"
run: >-
conformance_test_runner
--failure_list conformance/known_failures_reflect.txt
--maximum_edition 2024
/tmp/buffa-conformance-std
- name: Run conformance tests (via-vtable)
env:
BUFFA_VIA_VTABLE: "1"
run: >-
conformance_test_runner
--failure_list conformance/known_failures_view_vtable.txt
--maximum_edition 2024
/tmp/buffa-conformance-std