holt 0.3.3

An adaptive-radix-tree metadata storage engine for path-shaped keys, with per-blob concurrency and crash-safe persistence.
Documentation
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

permissions:
  contents: read

env:
  CARGO_TERM_COLOR: always
  CARGO_INCREMENTAL: "0"

jobs:
  # --------------------------------------------------------------
  # Build + test the library + examples + integration tests on
  # the two Tier-1 Unix targets holt supports today.
  # --------------------------------------------------------------
  test:
    name: test (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest]
    steps:
      - uses: actions/checkout@v6

      - uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt, clippy

      - name: Cache cargo registry + build
        uses: Swatinem/rust-cache@v2

      - name: cargo build
        run: cargo build --workspace --all-targets --all-features --locked

      - name: cargo test (lib + integration)
        run: cargo test --workspace --all-features --lib --tests --examples --locked

      - name: cargo test (doctests)
        run: cargo test --workspace --all-features --doc --locked

      - name: cargo run --example basic_kv
        run: cargo run --example basic_kv --locked

      - name: cargo run --example filesystem_meta
        run: cargo run --example filesystem_meta --locked

      - name: cargo run --example session_store
        run: cargo run --example session_store --locked

      - name: cargo run --example s3_metadata
        run: cargo run --example s3_metadata --locked

  # --------------------------------------------------------------
  # Lint pass — formatting + clippy with deny(warnings) on top of
  # the crate's pedantic lints.
  # --------------------------------------------------------------
  lint:
    name: lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt, clippy

      - uses: Swatinem/rust-cache@v2

      - name: cargo fmt --check
        run: cargo fmt --all --check

      # Clippy on tests + examples too, not just the library. The
      # crate opts into `clippy::pedantic` and has a vetted set of
      # `#![allow]`s in `src/lib.rs` for the unhelpful ones; any
      # new warning fails the build.
      - name: cargo clippy
        run: cargo clippy --workspace --all-targets --all-features --locked -- -D warnings

  # --------------------------------------------------------------
  # Fuzz smoke — short libFuzzer run over the storage-engine model
  # target. This is not a replacement for long fuzz campaigns; it is
  # a CI guard that keeps the fuzz target building and exercises the
  # atomic / WAL / range oracle for a bounded number of generated
  # inputs.
  # --------------------------------------------------------------
  fuzz:
    name: fuzz
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - uses: dtolnay/rust-toolchain@stable

      - uses: dtolnay/rust-toolchain@nightly

      - uses: Swatinem/rust-cache@v2

      - name: install cargo-fuzz
        run: cargo +stable install cargo-fuzz --locked

      - name: cargo fuzz run atomic_model
        run: cargo +nightly fuzz run atomic_model -- -runs=512

  # --------------------------------------------------------------
  # Coverage — source-based LLVM coverage over lib + integration
  # tests + examples on Ubuntu. The line threshold is deliberately
  # close to the current measured baseline so coverage can only move
  # backward intentionally.
  # --------------------------------------------------------------
  coverage:
    name: coverage
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - uses: dtolnay/rust-toolchain@stable
        with:
          components: llvm-tools-preview

      - uses: Swatinem/rust-cache@v2

      - name: install cargo-llvm-cov
        run: cargo install cargo-llvm-cov --locked

      - name: cargo llvm-cov
        run: |
          set -o pipefail
          cargo llvm-cov clean --workspace
          cargo llvm-cov \
            --workspace --all-features --lib --tests --examples --locked \
            2>&1 | tee coverage-full.txt
          awk 'capture { print } /^Filename[[:space:]]/ { capture=1; print }' \
            coverage-full.txt > coverage.txt
          python3 - <<'PY'
          from pathlib import Path

          threshold = 88.0
          total = next(
              line for line in Path("coverage.txt").read_text().splitlines()
              if line.startswith("TOTAL")
          )
          line_coverage = float(total.split()[9].removesuffix("%"))
          if line_coverage < threshold:
              raise SystemExit(
                  f"line coverage {line_coverage:.2f}% is below {threshold:.2f}%"
              )
          PY
          cargo llvm-cov report --lcov --output-path lcov.info
          cargo llvm-cov report --html --output-dir coverage
          {
            echo "## coverage"
            echo ""
            echo '```'
            cat coverage.txt
            echo '```'
          } >> "$GITHUB_STEP_SUMMARY"

      - name: Upload coverage artifacts
        uses: actions/upload-artifact@v4
        with:
          name: coverage
          path: |
            coverage.txt
            lcov.info
            coverage/html

  # --------------------------------------------------------------
  # Criterion microbenches — run on both Tier-1 Unix targets so
  # we can read the perf delta between the default
  # `pread`/`pwrite` backend (macOS + Linux without feature) and
  # the `--features io-uring` Linux build. Numbers go to the job
  # summary as a Markdown table; no failure thresholds — variance
  # on hosted runners is too high for hard SLOs, but eyeballing
  # the deltas tells us if a commit massively regressed
  # something. The CI window is intentionally small; full
  # comparator runs stay in the standalone bench package.
  # --------------------------------------------------------------
  bench:
    name: bench (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest]
    steps:
      - uses: actions/checkout@v6

      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2

      # Run the lightweight Holt-only regression benchmark from the
      # independent benchmark package. The public comparator harness
      # still lives under benches/, but CI keeps RocksDB / SQLite /
      # sled out of the regression gate.
      - name: cargo bench (default backend)
        run: |
          set -o pipefail
          cargo bench --manifest-path benches/Cargo.toml \
            --bench regression --no-default-features --locked -- \
            --warm-up-time 1 --measurement-time 1 \
            --sample-size 10 --nresamples 1000 --noplot \
            2>&1 | tee bench-default.txt

      - name: cargo bench (io-uring backend, Linux only)
        if: runner.os == 'Linux'
        run: |
          set -o pipefail
          cargo bench --manifest-path benches/Cargo.toml \
            --bench regression --no-default-features --features io-uring --locked -- \
            --warm-up-time 1 --measurement-time 1 \
            --sample-size 10 --nresamples 1000 --noplot \
            2>&1 | tee bench-uring.txt

      - name: Post results to job summary
        if: always()
        run: |
          {
            echo "## holt bench results — ${{ matrix.os }}"
            echo ""
            echo "### Default backend (\`pread\`/\`pwrite\`)"
            echo '```'
            grep -E "time:|thrpt:" bench-default.txt || echo "(no measurements captured)"
            echo '```'
            if [ -f bench-uring.txt ]; then
              echo ""
              echo "### \`--features io-uring\` backend"
              echo '```'
              grep -E "time:|thrpt:" bench-uring.txt || echo "(no measurements captured)"
              echo '```'
            fi
          } >> "$GITHUB_STEP_SUMMARY"

      - name: Upload raw bench logs
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: bench-${{ matrix.os }}
          path: bench-*.txt
          if-no-files-found: warn

  # --------------------------------------------------------------
  # `cargo doc` — fails on broken intra-doc links / missing docs
  # so the rendered docs.rs page stays clean.
  # --------------------------------------------------------------
  docs:
    name: docs
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2

      - name: cargo doc (no deps)
        env:
          RUSTDOCFLAGS: "-D warnings"
        run: cargo doc --workspace --no-deps --locked

  # --------------------------------------------------------------
  # MSRV — pin to the rust-version field in Cargo.toml. Keeps us
  # honest about the floor we promise downstream users. We build
  # the library only (no tests/benches/examples) because
  # dev-dependencies routinely require newer Rust than the
  # library surface itself does.
  # --------------------------------------------------------------
  msrv:
    name: msrv (rust 1.82)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@1.82
      - uses: Swatinem/rust-cache@v2

      - name: cargo build (library only)
        run: cargo build --lib --locked

  # --------------------------------------------------------------
  # Supply-chain hygiene: `cargo deny check` fails the build on
  # disallowed licenses, banned crates, RustSec advisories, and
  # duplicate / unmaintained dependencies. Policy lives in
  # `deny.toml` at the repo root.
  # --------------------------------------------------------------
  deny:
    name: deny
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: EmbarkStudios/cargo-deny-action@v2
        with:
          command: check
          arguments: --all-features --locked