From e4383334f08c0439f78acfc95e7f42fe8f78a642 Mon Sep 17 00:00:00 2001 From: Aditya Mukhopadhyay Date: Sat, 26 Sep 2026 18:42:31 +0530 Subject: [PATCH] ci: add MSRV, public-API semver and dependency policy checks (#395) New Policy workflow: - msrv: cargo check --all-features and cargo test --lib on Rust 1.89, with MSRV-aware dependency resolution; fails if Cargo.toml's rust-version drifts from the tested toolchain. - semver-checks: cargo-semver-checks against the latest v* release tag. Hard gate on main; informational (continue-on-error) on PRs to major-version branches such as v3. - cargo-deny: deny.toml allows only permissive licenses, denies duplicate crate versions and wildcard requirements, and requires every crate to come from crates.io. Advisories stay with cargo-audit and OSV-Scanner. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BedF9ZmqVZLkvDwn91fuKy --- .github/workflows/policy.yml | 90 ++++++++++++++++++++++++++++++++++++ CHANGELOG.md | 4 ++ deny.toml | 49 ++++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 .github/workflows/policy.yml create mode 100644 deny.toml diff --git a/.github/workflows/policy.yml b/.github/workflows/policy.yml new file mode 100644 index 000000000..12a35c059 --- /dev/null +++ b/.github/workflows/policy.yml @@ -0,0 +1,90 @@ +name: Policy + +# Release-policy checks (#395): +# - msrv: the crate builds and its unit tests pass on `rust-version` +# - semver-checks: no accidental public-API break against the latest release tag +# - cargo-deny: dependency licenses, duplicate versions and sources (deny.toml) + +on: + push: + branches: [ "main" ] + pull_request: + # Major-version branches (e.g. `v3`) run the same checks, but a semver + # break is allowed there — see the semver-checks job. + branches: [ "main", "v[0-9]*" ] + +env: + CARGO_TERM_COLOR: always + +permissions: + contents: read + +jobs: + msrv: + name: MSRV (Rust 1.89) + runs-on: ubuntu-latest + env: + MSRV: "1.89" + # Resolve dependencies to versions that support our rust-version, so a + # new upstream release cannot break the MSRV build on its own. + CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS: fallback + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Check MSRV matches Cargo.toml + run: | + declared=$(sed -n 's/^rust-version = "\(.*\)"$/\1/p' Cargo.toml) + if [ "$declared" != "$MSRV" ]; then + echo "::error::Cargo.toml rust-version is $declared but this job tests $MSRV; update policy.yml" + exit 1 + fi + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: "1.89" + + - name: Check (all features) + run: cargo check --all-features + + - name: Run unit tests + run: cargo test --lib + + semver-checks: + name: Public API semver check + runs-on: ubuntu-latest + # A breaking change is only allowed on a major-version branch (e.g. `v3`). + # There the job still runs, so the break is visible, but it cannot fail + # the PR. On `main` it is a hard gate. + continue-on-error: ${{ github.event_name == 'pull_request' && github.base_ref != 'main' }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Find latest release tag + id: baseline + run: | + tag=$(git describe --tags --abbrev=0 --match 'v[0-9]*') + echo "Baseline: $tag" + echo "tag=$tag" >> "$GITHUB_OUTPUT" + + - name: Run cargo-semver-checks + uses: obi1kenobi/cargo-semver-checks-action@v2 + with: + baseline-rev: ${{ steps.baseline.outputs.tag }} + + cargo-deny: + name: Dependency policy (cargo-deny) + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run cargo-deny + uses: EmbarkStudios/cargo-deny-action@v2 + with: + # Advisories are covered by cargo-audit.yml and osv-scanner.yml. + command: check bans licenses sources diff --git a/CHANGELOG.md b/CHANGELOG.md index f2b6eb7f2..b397fde44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **A query with `$slot` bind tokens run through `execute()` reported an internal error (#407).** Such a query failed with `[INT-025] internal: unsubstituted :valid-at bind slot reached the executor` when it had a `:valid-at` slot, and with slots in other positions it could run and quietly return nothing. `Minigraf::execute()`, `WriteTransaction::execute()` and the REPL now reject it before execution with the new user error **API-010**, which names the slots and points to `prepare()`. Prepared queries are unchanged. - **Bound-entity queries could drop rows when one `WriteTransaction` wrote the same attribute in several valid-time windows (#323).** Both facts share a `tx_count`, and the selective lookup path de-duplicated on `(entity, attribute, tx_count, asserted)`, so one window's value was silently lost, while the same query via a full scan returned both. That de-duplication has been removed, and bound-entity queries now always match a full scan. +### CI + +- **New `Policy` workflow (#395).** An MSRV job runs `cargo check --all-features` and `cargo test --lib` on Rust 1.89, resolving dependencies to 1.89-compatible versions, and fails if `rust-version` in `Cargo.toml` drifts from the tested toolchain. `cargo-semver-checks` compares the public API with the latest release tag and blocks PRs to `main`; on major-version branches such as `v3` it reports breaks without failing. `cargo-deny` (`deny.toml`) allows only permissive licenses compatible with MIT OR Apache-2.0, rejects duplicate crate versions and wildcard requirements, and requires every crate to come from crates.io. + ### Documentation - **Unsafe WAL recovery advice removed from `docs/ERROR_REFERENCE.md`.** The WAL-001, WAL-002 and STG-011 resolutions said a `.wal` file could be deleted with no data loss, or that a database could be rebuilt from the WAL alone. Both are wrong: transactions committed since the last checkpoint exist only in the WAL, and the WAL holds nothing older than that checkpoint. The resolutions now say to copy both files first, to checkpoint with the version that wrote the WAL, and what is lost if the WAL is deleted. diff --git a/deny.toml b/deny.toml new file mode 100644 index 000000000..22aabc9f6 --- /dev/null +++ b/deny.toml @@ -0,0 +1,49 @@ +# cargo-deny policy for Minigraf dependencies (#395). +# Run locally with: cargo deny check bans licenses sources +# Advisories are covered by cargo-audit.yml and osv-scanner.yml. + +[graph] +# Check the native targets plus the browser WASM target, so the +# wasm-bindgen / web-sys dependency tree is covered too. +targets = [ + "x86_64-unknown-linux-gnu", + "aarch64-apple-darwin", + "x86_64-pc-windows-msvc", + "wasm32-unknown-unknown", +] +all-features = true + +[licenses] +# Permissive licenses compatible with Minigraf's MIT OR Apache-2.0. +allow = [ + "MIT", + "Apache-2.0", + "Apache-2.0 WITH LLVM-exception", + "BSD-2-Clause", + "BSD-3-Clause", + "ISC", + "Zlib", + "Unicode-3.0", + "Unlicense", + "CC0-1.0", +] +confidence-threshold = 0.9 +# Keep the list stable as dependencies come and go. +unused-allowed-license = "allow" + +[bans] +# Two versions of the same crate bloat the binary (PHILOSOPHY.md §4). +# If an upstream crate forces a duplicate, list it under `skip` with a +# comment explaining why, e.g. +# skip = [{ crate = "foo@1.2.3", reason = "bar still depends on foo 1.x" }] +multiple-versions = "deny" +skip = [] +wildcards = "deny" +allow-wildcard-paths = true + +[sources] +# Every crate must come from crates.io. +unknown-registry = "deny" +unknown-git = "deny" +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +allow-git = []