gruff-rs 0.4.0

Rust static analyzer and quality linter for CI: dead-code, complexity, security, secrets, and architecture rules with deterministic SARIF/JSON output and baseline support.
# CI Integration

gruff-rs is designed to run as a deterministic CI quality gate.

## GitHub Actions

```yaml
name: gruff-rs

on: [push, pull_request]

jobs:
  analyse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - run: cargo run -- analyse src --format sarif --fail-on none > gruff-rs.sarif
      - uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: gruff-rs.sarif
```

## Quality Gate

For blocking jobs, choose the lowest severity that should fail the build:

```sh
cargo run -- analyse src --fail-on warning
```

Use `--fail-on none` when the job should only publish reports.

## Baselines

Generate an adoption baseline after reviewing current findings:

```sh
cargo run -- analyse src --generate-baseline --fail-on none
```

Future scans auto-apply `gruff-baseline.json` when present. Use
`--no-baseline` to audit the full unsuppressed result.

## Diff Scans

Rust keeps a safety-biased diff contract:

```sh
cargo run -- analyse src --diff-patch /tmp/gruff.patch --format json --fail-on none
cargo run -- analyse src --diff staged --diff-git-unsafe --fail-on warning
```

`--diff-git-unsafe` is required for Git-backed diff modes because they shell out
to Git. Prefer `--diff-patch` in locked-down CI environments.

## Ignore Scope (Coding-Agent Hook)

Config `paths.ignore` in `.gruff-rs.yaml` is **authoritative in every invocation
mode** — directory walk, explicit file arguments, and every diff mode. A path
matching `paths.ignore` produces no findings however it was supplied, so a
coding-agent hook can pass the agent's changed files directly without surfacing
out-of-scope findings. `--include-ignored` opts into git-ignored and built-in
default-directory paths only; it never reveals a config-ignored path, and VCS
internals stay blocked.

Ignored paths are reported additively under `paths.ignoredPathDetails` (the
existing `paths.ignoredPaths` string list is unchanged):

```jsonc
"ignoredPathDetails": [
  { "path": "vendor/lib.rs", "source": "config", "pattern": "vendor/**" }
]
```

`source` is one of `config`, `gitignore`, `default`, or `generated`.

## check-ignore

`check-ignore` answers, per path, whether gruff would ignore it and why — using
the same config and ignore engine as `analyse`, with no analysis. A hook can call
it to scope its own work:

```sh
cargo run -- check-ignore --format json src/app.css vendor/lib.rs src/main.rs
# [{ "path": "vendor/lib.rs", "ignored": true, "source": "config", "pattern": "vendor/**" },
#  { "path": "src/main.rs", "ignored": false, "source": null, "pattern": null }]
```

Exit codes mirror `git check-ignore`: `0` when at least one path is ignored, `1`
when none are, `2` on error. Text output lists the ignored paths; add `-v` to
append `\t<source>:<pattern>`.

## Preflight

Run the full local gate before releases:

```sh
scripts/preflight-checks.sh
```