Formatting and Linting
This section covers repository formatting, pre-commit validations, custom linter
rules, and CI quality gates in Arminius.
Getting Started
The supported development environment is the Nix development shell. Entering
nix develop or using direnv provides pinned versions of every formatter and
linter.
For shell setup and caching details, see the Development Setup.
Common Commands
The following table summarizes the primary tools and their effects:
| Command | Effect | Typical Use |
|---|---|---|
nix fmt | Rewrites tracked files | Auto-formats code and configuration across the repository |
prek | May rewrite files and report failure | Runs pre-commit checks on staged files |
prek --all-files | May rewrite files and report failure | Runs pre-commit checks on all files, matching GitLab CI |
prek -s main -o HEAD | May rewrite files and report failure | Validates all commits on the current branch |
ast-grep scan --error | Report only | Runs custom structural lints and reports findings |
nix flake check | Report only | Evaluates and runs all local flake checks. Takes quite some time to run. |
Optional Git Hook
prek and prek --all-files work without installing a Git hook. To also run
those checks automatically on every commit, install the hook from inside the
Nix development shell:
prek install
Pre-Merge Quality Gate
Before submitting a merge request or requesting review:
- Run
nix fmtto ensure all files adhere to project formatting. - Run
prek --all-filesto verify repository hygiene, licenses, and spellings. - Run
ast-grep scan --errorto ensure custom architectural rules pass. - Run
nix flake checkto verify static checks locally.
Reference
Configuration File Ownership
Nix modules from the pinned engineering-standards flake generate several
repository-root configuration files. Each generated file includes a
managed-by: engineering-standards header that forbids hand edits and lists
the regenerate command.
Do not edit those files by hand. Instead, change the sources:
- Famedly Engineering Standards
- Local Nix formatter module:
nix/formatter-module.nix - Local pre-commit module:
nix/prek-pre-commit.nix
To regenerate configuration files after modifying Nix modules, run:
nix run .#filegen-activate
Formatting
nix fmt invokes treefmt with the complete formatter set required by this
repository.
To debug a single formatter, inspect the flags in nix/formatter-module.nix
and in the pinned Famedly Engineering Standards
treefmt module. Those sources are authoritative for the dev-shell and CI.
Pre-Commit Checks
The repository uses prek for pre-commit checks. Commands are listed under “Common Commands”.
prek also runs ast-grep scan --error for staged Rust files.
prek --all-files runs it for all Rust files, but changes limited to
.ast-grep/ or sgconfig.yml still rely on the separate flake check.
Linting and Policy Checks
Getting Started
Run all flake checks with:
nix flake check
Note, that this command performs CPU-/memory-intensive operations and takes at least a couple of minutes to complete, even on modern machines.
For ast-grep setup, commands, and custom rule documentation, see our ast-grep documentation.
CI Execution
GitLab CI splits the flake checks into parallel source-checks and
rust-checks jobs. Each job builds a named check derivation, for example:
nix build -L .#checks.x86_64-linux.ast-grep-lints
The CI pipeline also runs the pre-commit job separately. nix flake check
does not replace that job or run nix fmt.
Rust Lints
Clippy and the compiler lints catch many problems before review. CI turns every warning into an error, so a lint that fires blocks the merge request.
Clippy
The clippy flake check runs Clippy over the whole workspace in the rust-checks CI job.
It passes --all-features --all-targets -- --deny warnings.
Run the same check in the development shell with:
cargo clippy --all-features --all-targets -- --deny warnings
You can also build the flake check with nix build -L .#checks.x86_64-linux.clippy.
Lint Configuration
Lints are defined in the [workspace.lints.rust] and [workspace.lints.clippy] tables of the root Cargo.toml.
Workspace crates must opt in with [lints] workspace = true in their own Cargo.toml.
.clippy.toml holds options for individual lints.
Documentation Lints
Every item needs a doc comment, whether it is public or private.
The lints missing_docs and clippy::missing_docs_in_private_items enforce this.
Module documentation sits inside the module, as //! comments.
The ast-grep rule external-module-comments enforces this, and Coding Style shows both forms.
Lint Exceptions
When a lint does not fit one item, silence it for that item with #[expect] and a reason:
#[expect(
clippy::too_many_lines,
reason = "Being a big match statement, the complexity doesn't really increase with length that much"
)]
pub fn construct_outgoing_request(&self, /* ... */) { /* ... */ }
Two lints enforce this form.
clippy::allow_attributes flags #[allow(...)] and asks for #[expect(...)] instead.
clippy::allow_attributes_without_reason flags any exception without a reason.