From 670321efd12931a9fbdaf8868d0ba381d64223ee Mon Sep 17 00:00:00 2001 From: Stephen Waits Date: Mon, 13 Apr 2026 10:24:08 -0600 Subject: [PATCH] ci: GitHub Actions workflow + drop test-fuzz-long MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Eight parallel jobs on push/PR: - test × 3 (default / no-default / all-features) via nextest + doc - clippy × 3 feature permutations, -D warnings - fmt - doc (all-features, -D warnings on rustdoc) - deny (cargo-deny action) - miri (nightly, lib + vectors under default + no-default) - fuzz (nightly, 60 s smoke on every target) - mutants × 2 feature configs (same equivalence exclusions as local) Swatinem/rust-cache on every cargo job; taiki-e/install-action for nextest / cargo-fuzz / cargo-mutants. Uploads mutants.out on failure. Drops test-fuzz-long from the justfile — 60 s per target in CI every push plus the local proptest 1M-case suite already covers this. --- .github/workflows/ci.yml | 161 +++++++++++++++++++++++++++++++++++++++ justfile | 10 --- 2 files changed, 161 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d78baac --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,161 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + +jobs: + test: + name: Test (${{ matrix.features }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + features: + - default + - no-default-features + - all-features + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - uses: taiki-e/install-action@v2 + with: + tool: nextest + - name: cargo nextest run + run: | + case "${{ matrix.features }}" in + default) cargo nextest run ;; + no-default-features) cargo nextest run --no-default-features ;; + all-features) cargo nextest run --all-features ;; + esac + - name: cargo test --doc + run: cargo test --doc + + clippy: + name: Clippy (${{ matrix.features }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + features: + - default + - no-default-features + - all-features + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: clippy + - uses: Swatinem/rust-cache@v2 + - name: cargo clippy + run: | + case "${{ matrix.features }}" in + default) cargo clippy --all-targets -- -D warnings ;; + no-default-features) cargo clippy --all-targets --no-default-features -- -D warnings ;; + all-features) cargo clippy --all-targets --all-features -- -D warnings ;; + esac + + fmt: + name: Format + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + - run: cargo fmt -- --check + + doc: + name: Doc + runs-on: ubuntu-latest + env: + RUSTDOCFLAGS: -D warnings + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - run: cargo doc --no-deps --all-features + + deny: + name: Cargo Deny + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: EmbarkStudios/cargo-deny-action@v2 + + miri: + name: Miri + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + components: miri + - uses: Swatinem/rust-cache@v2 + - uses: taiki-e/install-action@v2 + with: + tool: nextest + - run: cargo +nightly miri nextest run --lib --test vectors + - run: cargo +nightly miri nextest run --lib --test vectors --no-default-features + + fuzz: + name: Fuzz (60s per target) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + - uses: Swatinem/rust-cache@v2 + with: + workspaces: | + . + fuzz + - uses: taiki-e/install-action@v2 + with: + tool: cargo-fuzz + - name: Build fuzz targets + working-directory: fuzz + run: cargo +nightly fuzz build + - name: Smoke-run every target + working-directory: fuzz + run: | + for target in parse_command parse_radio_config cobs_decoder_feed cobs_roundtrip command_roundtrip cobs_decoder_multi_frame cobs_chunk_invariant; do + echo "── $target ──" + cargo +nightly fuzz run "$target" -- -max_total_time=60 + done + + mutants: + name: Mutants + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + features: + - default + - no-default-features + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - uses: taiki-e/install-action@v2 + with: + tool: cargo-mutants, nextest + - name: cargo mutants (${{ matrix.features }}) + run: | + extra="" + [ "${{ matrix.features }}" = "no-default-features" ] && extra="--no-default-features" + cargo mutants --no-shuffle --test-tool=nextest $extra \ + -E 'replace > with >= in Command::from_bytes' \ + -E 'replace > with >= in CobsDecoder::feed' \ + -E 'replace < with <= in cobs_encode_response' + - name: Upload mutants.out on failure + if: failure() + uses: actions/upload-artifact@v4 + with: + name: mutants-${{ matrix.features }} + path: mutants.out diff --git a/justfile b/justfile index 2c55f04..792b2b0 100644 --- a/justfile +++ b/justfile @@ -48,16 +48,6 @@ test-fuzz: cd fuzz && cargo +nightly fuzz run cobs_decoder_multi_frame -- -max_total_time=60 cd fuzz && cargo +nightly fuzz run cobs_chunk_invariant -- -max_total_time=60 -# long fuzz run: 10 minutes per target. Use for CI cron / pre-release. -test-fuzz-long: - cd fuzz && cargo +nightly fuzz run parse_command -- -max_total_time=600 - cd fuzz && cargo +nightly fuzz run parse_radio_config -- -max_total_time=600 - cd fuzz && cargo +nightly fuzz run cobs_decoder_feed -- -max_total_time=600 - cd fuzz && cargo +nightly fuzz run cobs_roundtrip -- -max_total_time=600 - cd fuzz && cargo +nightly fuzz run command_roundtrip -- -max_total_time=600 - cd fuzz && cargo +nightly fuzz run cobs_decoder_multi_frame -- -max_total_time=600 - cd fuzz && cargo +nightly fuzz run cobs_chunk_invariant -- -max_total_time=600 - # run tests under miri (catches UB, requires nightly + miri component) # The proptest suite is skipped — its file-persistence layer calls getcwd # which miri does not support. Vectors and unit tests run clean.