Skip to content

decode: validate string fields with smoothutf8 (default-on fast-utf8 feature) #740

decode: validate string fields with smoothutf8 (default-on fast-utf8 feature)

decode: validate string fields with smoothutf8 (default-on fast-utf8 feature) #740

Workflow file for this run

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"
jobs:
# ── Fast feedback: lint + test on stable ─────────────────────────────────
lint-and-test:
runs-on: ubuntu-latest
timeout-minutes: 10
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
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
# `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
# ── 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
# ── 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
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 SizeCache `unsafe` reads no uninitialized 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`.
# Miri tracks per-byte init state, so this job mechanically verifies the
# soundness invariant (slots < len are initialized) on every reserve / set /
# consume / clear / spill path the size_cache tests exercise. Scoped to that
# module to keep the (interpreted, slow) run fast — ~15-30s 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 soundness)
run: cargo +${{ env.MIRI_TOOLCHAIN }} miri test -p buffa size_cache
# ── 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
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
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/duration.proto \
google/protobuf/empty.proto \
google/protobuf/field_mask.proto \
google/protobuf/struct.proto \
google/protobuf/timestamp.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
cache-to: 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