linter
Rules enforced on every PR. CI fails on any violation.
Standard Rust tooling
| Tool | Command | Gate |
|---|---|---|
rustfmt | cargo fmt --all -- --check | fail on any diff |
clippy | cargo clippy --all-targets --all-features -- -D warnings | fail on any warning |
typos | typos | fail on misspellings |
cargo-deny | cargo deny check | license + advisory + source gates |
Clippy configuration
clippy.toml:
avoid-breaking-exported-api = false
msrv = "1.82"
Lint levels in src/lib.rs:
#![allow(unused)] #![forbid(unsafe_code)] #![deny( fn main() { clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::dbg_macro, clippy::print_stdout, clippy::print_stderr, clippy::todo, clippy::unimplemented, )] #![warn( clippy::pedantic, clippy::nursery, missing_docs, )] }
Tests and benches relax via #![allow(clippy::unwrap_used, clippy::expect_used)] at crate root for test binaries.
Custom rules
Output centralization
- No
println!/eprintln!/print!/eprint!outsidesrc/cli/output.rs. - Enforced by
clippy::print_stdout+clippy::print_stderr= deny. - All output goes through the formatter which honors
--json/--plain/ TTY detection.
Error handling discipline
- Library modules (
src/manifest,src/pack,src/plugin,src/actions,src/packtypes,src/fetchers,src/concurrency): usethiserrortyped errors.anyhowbanned here. - Binary modules (
src/cli,src/main.rs,src/mcp): may useanyhow. - No
unwrap()/expect()in production paths. Startup-only paths mayexpect()with a human-meaningful message if the invariant is unrecoverable (e.g. inventory registry empty = developer bug).
No direct shell-spawning outside actions/exec
tokio::process::Commandandstd::process::Commandallowed ONLY insrc/actions/exec.rs,src/packtypes/scripted.rs, andsrc/fetchers/git.rs(for CLI fallback).- Any other file invoking
Commandfails lint. - Enforced by CI grep rule:
if grep -rn 'process::Command' src/ --include='*.rs' \
| grep -vE '^src/(actions/exec|packtypes/scripted|fetchers/git)\.rs'; then
echo "shell invocation outside allowed modules"; exit 1
fi
Path rules (ported from legacy .scripts/test.py)
-
No hardcoded absolute paths in source, config, or embedded strings.
-
Banned:
C:\,D:\,E:\,/home/,/Users/,/mnt/,/opt/. -
CI grep:
if grep -rn -E '([A-Z]:\\|/home/|/Users/|/mnt/|/opt/)' src/ --include='*.rs'; then echo "hardcoded path detected"; exit 1 fi
-
-
No
~in source strings. Home expansion lives in aPackCtx::envhelper usingdirs::home_dir(). -
No string concatenation with path separators. Use
std::path::PathBuf+push()/join(). Clippy'spath_buf_push_overwritehelps.
Manifest rules (runtime + lint)
pack.yamlchildren[].pathMUST be bare name. Enforced at parse bypack::schema::validate()and at doctor-time bygrex doctor.grex.jsonleventpathfield likewise bare. No drive letters anywhere in manifest.
Plugin trait discipline
- Every module under
src/actions/MUST contain exactly oneimpl ActionPlugin. - Every module under
src/packtypes/MUST contain exactly oneimpl PackTypePlugin. - Every module under
src/fetchers/MUST contain exactly oneimpl Fetcher. - Enforced by code review + presence of
inventory::submit!block.
Shim rules — N/A
Legacy .scripts/ had Python-specific shim rules (no shutil.rmtree, no subprocess.run(shell=True), etc.). Rust has no direct analogue:
std::fs::remove_dir_allis cross-platform — no native-script indirection needed.- Shell invocation is already gated by the "no shell-spawning outside allowed modules" rule above.
- Symlinks use
std::os::{unix,windows}::fsdirectly.
CI job
.github/workflows/lint.yml (or a job in ci.yml):
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with: { components: rustfmt, clippy }
- run: cargo fmt --all -- --check
- run: cargo clippy --all-targets --all-features -- -D warnings
- uses: crate-ci/typos@master
- uses: EmbarkStudios/cargo-deny-action@v1
- name: hardcoded paths
run: |
! grep -rn -E '([A-Z]:\\|/home/|/Users/|/mnt/|/opt/)' src/ --include='*.rs'
- name: shell invocation scope
run: |
! grep -rn 'process::Command' src/ --include='*.rs' \
| grep -vE '^src/(actions/exec|packtypes/scripted|fetchers/git)\.rs'
Pre-commit hook
.grex/hooks/pre-commit:
#!/usr/bin/env bash
set -e
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
Activated by grex init via git config core.hooksPath .grex/hooks.