pagedb 0.1.0-beta.6

Encrypted, portable, embedded page store with B+ tree and segment-file surfaces.
Documentation
# Reusable test workflow.
#
# Called by:
#   - ci.yml      (PR checks + manual)
#   - release.yml (pre-publish gate)
#
# Not triggered directly — `workflow_call` only.
#
# Jobs:
#   lint        — rustfmt + clippy (both workspace packages) + doc + doctests
#                 + checklist-scaffold gate
#   test        — `cargo nextest run` on Linux / macOS / Windows. Crash tests
#                 are serialised by `.config/nextest.toml`, which `cargo test`
#                 ignores — that is why this workflow always uses nextest.
#   invariants  — the same suite with PAGEDB_INVARIANT_CHECKS set, so the
#                 opt-in commit-time structural checks are actually exercised.
#   cross       — `cargo check` across every Tier-1/Tier-2 target the VFS
#                 backend matrix covers, so a platform-specific backend never
#                 regresses unnoticed. Format-bit-identity across these targets
#                 is a release gate, so a target dropped here is a silent hole.
#   wasm        — `cargo check` for wasm32-unknown-unknown (OPFS) and
#                 wasm32-wasip1 (WASI VFS).
#   features    — build with every feature flag combination that ships.
#   bench       — compiles every bench target in both packages without running
#                 any measurement, so a bench that stops building fails CI
#                 without burning a long benchmark slot.

name: Test

on:
  workflow_call:

permissions:
  contents: read

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1

jobs:
  # ─────────────────────────────────────────────────────────────────────────
  lint:
    name: Lint, Format & Docs
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

      - name: Install Rust
        uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable
        with:
          components: rustfmt, clippy

      # The isolated comparison package links RocksDB (librocksdb-sys), whose
      # build runs bindgen and needs libclang.
      - name: Install LLVM/Clang
        run: sudo apt-get update && sudo apt-get install -y clang libclang-dev

      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          prefix-key: lint

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

      - name: Keep external engines out of the PageDB package
        run: |
          for features in "" "--all-features"; do
            if cargo tree -p pagedb $features --edges normal,build,dev --prefix none |
              rg -q '^(librocksdb-sys|libsqlite3-sys|redb|rocksdb|rusqlite) v'; then
              echo "::error::External comparison engine leaked into the pagedb dependency graph (${features:-default features})."
              exit 1
            fi
          done

      - name: Clippy (all targets, all features)
        run: cargo clippy -p pagedb --all-targets --all-features -- -D warnings

      # No `--all-features` — the comparison package has no feature flags.
      - name: Clippy (engine comparison)
        run: cargo clippy -p pagedb-engine-comparison --all-targets -- -D warnings

      # Warnings are denied because the failures that matter here are silent:
      # a link to a crate-private item renders as plain text rather than
      # erroring, so the published docs lose it and nothing reports why.
      - name: Build docs
        run: cargo doc -p pagedb --no-deps --all-features
        env:
          RUSTDOCFLAGS: -D warnings

      - name: Run doctests
        run: cargo test -p pagedb --doc --all-features

      # Code is organised by what it does, never by the order it was built,
      # so no build-order scaffolding may leak into source / Cargo.toml /
      # README: no checklist letters, no "step N" / "phase N" / "milestone
      # MN" markers, in prose or in identifiers. A reader must not be able to
      # reconstruct the build sequence from the code. Greps for the explicit
      # forbidden patterns; fails the job if any are present.
      - name: Checklist-scaffold leak gate
        run: |
          set -euo pipefail
          if rg --color=never -n \
                -e '\b(step_[a-z]_|phase_[a-z]_|m[0-9]+_)\w*' \
                -e '(?i)\b(added in step|step [A-Z]\b|phase [A-Z]\b|milestone M[0-9])' \
                src/ Cargo.toml README.md; then
            echo "::error::Build-order scaffolding leaked into source. Name things by what they do, not by the order they landed."
            exit 1
          fi

      - name: Shellcheck release scripts
        run: shellcheck scripts/ci/*.sh

      - name: Release artifact verification
        run: bash scripts/ci/test_release_verifiers.sh

  # ─────────────────────────────────────────────────────────────────────────
  test:
    name: Test (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

      - name: Install Rust
        uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable

      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          prefix-key: test-${{ matrix.os }}

      - name: Install cargo-nextest
        uses: taiki-e/install-action@41049aa56687c35e0afa74eed4f09cec4f9afabf # v2.85.2
        with:
          tool: nextest

      # Always nextest — crash-recovery tests rely on the serial group in
      # .config/nextest.toml that `cargo test` ignores.
      - name: Run tests
        run: cargo nextest run -p pagedb --all-features --no-fail-fast

      - name: Upload nextest report
        if: always()
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: nextest-${{ matrix.os }}
          path: target/nextest/default/junit.xml
          if-no-files-found: ignore

  # ─────────────────────────────────────────────────────────────────────────
  # The commit-time freed-page / reachability invariant is opt-in: with
  # PAGEDB_INVARIANT_CHECKS unset, `WriteTxn::commit` probes the variable once
  # and does nothing else, so the `test` job above never runs a single line of
  # it. Without this lane a regression in the check — or a healthy commit that
  # it starts rejecting — is invisible until someone enables it by hand.
  #
  # Linux-only and single-config on purpose: the check is pure page-graph
  # traversal with no platform-specific or feature-gated code, so a second
  # runner would buy nothing. It is deliberately expensive (it walks the data,
  # catalog, and commit-history trees on every commit), which is why it stays
  # opt-in rather than becoming the default for the whole matrix.
  invariants:
    name: Invariant checks
    runs-on: ubuntu-latest
    env:
      # Only presence is checked, never the value.
      PAGEDB_INVARIANT_CHECKS: "1"
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

      - name: Install Rust
        uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable

      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          prefix-key: invariants

      - name: Install cargo-nextest
        uses: taiki-e/install-action@41049aa56687c35e0afa74eed4f09cec4f9afabf # v2.85.2
        with:
          tool: nextest

      - name: Run tests with commit invariants enabled
        run: cargo nextest run -p pagedb --all-features --no-fail-fast

      - name: Upload nextest report
        if: always()
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: nextest-invariants
          path: target/nextest/default/junit.xml
          if-no-files-found: ignore

  # ─────────────────────────────────────────────────────────────────────────
  # Per-platform VFS backends must each at least type-check on every target
  # they exist for: one backend per platform family, selected by cfg, so a
  # target absent here compiles nowhere. Compile-only — running on
  # these targets would require emulators / Apple runners we don't gate
  # every PR on.
  cross:
    name: Cross check (${{ matrix.label }})
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-pc-windows-gnu
            label: windows-gnu (IocpVfs)
          - target: aarch64-apple-darwin
            label: macos-aarch64 (GcdVfs)
          - target: x86_64-apple-darwin
            label: macos-x86_64 (GcdVfs)
          - target: aarch64-apple-ios
            label: ios-aarch64 (GcdVfs)
          - target: aarch64-linux-android
            label: android-aarch64 (IouringVfs)
          - target: armv7-linux-androideabi
            label: android-armv7 (AndroidVfs / thread-pool)
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

      - name: Install Rust + target
        uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable
        with:
          targets: ${{ matrix.target }}

      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          prefix-key: cross-${{ matrix.target }}

      - name: cargo check --lib
        run: cargo check -p pagedb --target ${{ matrix.target }} --lib

  # ─────────────────────────────────────────────────────────────────────────
  wasm:
    name: WASM / WASI check (${{ matrix.label }})
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: wasm32-unknown-unknown
            features: --features opfs
            label: wasm32 (OpfsVfs)
          - target: wasm32-wasip1
            features: ""
            label: wasi (WasiVfs)
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

      - name: Install Rust + target
        uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable
        with:
          targets: ${{ matrix.target }}

      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          prefix-key: wasm-${{ matrix.target }}

      - name: cargo check --lib
        run: cargo check -p pagedb --target ${{ matrix.target }} --lib ${{ matrix.features }}

  # ─────────────────────────────────────────────────────────────────────────
  features:
    name: Feature matrix (${{ matrix.flags }})
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        flags:
          - ""                           # default features
          - "--no-default-features"
          - "--features compression"
          - "--all-features"
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

      - name: Install Rust
        uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable

      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          prefix-key: features

      - name: cargo check
        run: cargo check -p pagedb --lib --bins --tests --benches ${{ matrix.flags }}

  # ─────────────────────────────────────────────────────────────────────────
  bench:
    name: Benchmark (dry run)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

      - name: Install Rust
        uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable

      # The isolated comparison package links RocksDB (librocksdb-sys), whose
      # build runs bindgen and needs libclang.
      - name: Install LLVM/Clang
        run: sudo apt-get update && sudo apt-get install -y clang libclang-dev

      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          prefix-key: bench

      # Compile-only smoke; running fluxbench in CI is a separate, manually-
      # dispatched workflow so we don't pay the runtime on every PR. This
      # job catches "the bench no longer compiles" regressions. Package-wide,
      # no `--bench` filters: naming targets individually is what let two
      # benches go uncompiled, and a package-wide build can't silently miss
      # a newly added target.
      - name: Build benches
        run: |
          cargo bench --no-run -p pagedb
          cargo bench --no-run -p pagedb-engine-comparison

  # ─────────────────────────────────────────────────────────────────────────
  audit:
    name: Dependency audit
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - name: Install Rust
        uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable
      - name: Install cargo-deny
        uses: taiki-e/install-action@41049aa56687c35e0afa74eed4f09cec4f9afabf # v2.85.2
        with:
          tool: cargo-deny
      - name: Run cargo-deny
        run: cargo deny --exclude-dev check