diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b9fff7..3b5a328 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -99,3 +99,31 @@ jobs: # advisories (unsound / unmaintained) are reported as warnings only — # Dependabot does not surface those at all, so the log is the record. run: cargo audit + + deny: + name: cargo deny + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Cache cargo-deny binary + id: cache-deny + uses: actions/cache@v6 + with: + path: ~/.cargo/bin/cargo-deny + key: ${{ runner.os }}-cargo-deny + + - name: Install cargo-deny + if: steps.cache-deny.outputs.cache-hit != 'true' + run: cargo install cargo-deny --locked + + - name: Check licenses, bans and sources + # `advisories` is intentionally omitted — the cargo audit job above + # already covers RustSec. This job guards the other three: license + # compatibility with GPL-3.0-only, duplicate crate versions (a second + # crypto trait generation in the graph), and dependencies resolving to + # anything other than crates.io. + run: cargo deny check licenses bans sources diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5d95cbc..1a7ed31 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -103,8 +103,9 @@ benchmark/ scripts/ release.sh Automated release + Homebrew formula update .github/ - workflows/ci.yml GitHub Actions CI (fmt, clippy, test, cargo audit) + workflows/ci.yml GitHub Actions CI (fmt, clippy, test, cargo audit, cargo deny) dependabot.yml Weekly cargo + github-actions dependency updates +deny.toml cargo-deny policy (licenses, duplicate versions, sources) ``` ## Development Guidelines @@ -115,6 +116,23 @@ scripts/ - Run `cargo clippy` and fix any warnings - Follow standard Rust naming conventions +### Dependencies + +CI enforces a dependency policy on top of the test suite. Both tools are +optional locally (`cargo install cargo-audit cargo-deny --locked`), but a PR +that trips either one will fail: + +- `cargo audit` fails on known RustSec vulnerabilities. Informational + advisories (unsound / unmaintained) are reported as warnings only. +- `cargo deny check licenses bans sources` enforces `deny.toml`: dependency + licenses must be compatible with GPL-3.0-only, no crate may appear at two + versions, and every dependency must resolve to crates.io. The `advisories` + check is deliberately left to `cargo audit` rather than run twice. + +A duplicate-version failure matters most for the RustCrypto crates — two +generations of the `cipher` / `digest` traits in one graph will not compile. +That is why `.github/dependabot.yml` groups them into a single PR. + ### Compatibility This is the most important constraint. Gitveil must remain **byte-compatible** with git-crypt: @@ -157,7 +175,7 @@ This is the most important constraint. Gitveil must remain **byte-compatible** w 1. Fork the repository 2. Create a feature branch (`git checkout -b my-feature`) 3. Make your changes -4. Run `cargo fmt && cargo clippy && cargo test` +4. Run `cargo fmt && cargo clippy && cargo test` (and `cargo deny check licenses bans sources` if you changed dependencies) 5. Commit with a clear message 6. Open a pull request diff --git a/deny.toml b/deny.toml new file mode 100644 index 0000000..01ced87 --- /dev/null +++ b/deny.toml @@ -0,0 +1,47 @@ +# cargo-deny configuration. +# +# Scope note: the `advisories` check is deliberately NOT run in CI — the +# separate `cargo audit` job already covers RustSec advisories and reports +# informational (unsound / unmaintained) findings as warnings. Running both +# would duplicate the signal. CI runs `cargo deny check licenses bans sources`. + +[graph] +all-features = true + +[licenses] +# GPL-3.0-only is this crate's own license. Everything below is either +# permissive or GPL-3.0-compatible weak copyleft. Crates offering a choice +# (e.g. rustix: "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT") +# resolve against the first allowed term, so only the terms actually +# selected need listing here. +allow = [ + "Apache-2.0", + "GPL-3.0-only", + "MIT", + # `colored` is MPL-2.0: file-level copyleft, explicitly GPL-compatible. + "MPL-2.0", + # `unicode-ident` is "(MIT OR Apache-2.0) AND Unicode-3.0". + "Unicode-3.0", +] +confidence-threshold = 0.9 + +[bans] +# A second copy of a crypto crate in the graph means two incompatible +# trait generations (see the `rustcrypto` group in .github/dependabot.yml). +# Denying duplicates outright is what catches that. +multiple-versions = "deny" +wildcards = "deny" + +skip = [ + # syn 2 and syn 3 coexist because zeroize_derive is still on 2 while + # clap_derive and thiserror-impl moved to 3. Both are proc-macro + # build-time only and never reach the shipped binary. + { crate = "syn" }, +] + +[sources] +# Every dependency must come from crates.io. A git or alternate-registry +# source appearing in the graph is the supply-chain case worth failing on. +unknown-registry = "deny" +unknown-git = "deny" +allow-registry = ["https://github.com/rust-lang/crates.io-index"]