Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

CommandEffectTypical Use
nix fmtRewrites tracked filesAuto-formats code and configuration across the repository
prekMay rewrite files and report failureRuns pre-commit checks on staged files
prek --all-filesMay rewrite files and report failureRuns pre-commit checks on all files, matching GitLab CI
prek -s main -o HEADMay rewrite files and report failureValidates all commits on the current branch
ast-grep scan --errorReport onlyRuns custom structural lints and reports findings
nix flake checkReport onlyEvaluates 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:

  1. Run nix fmt to ensure all files adhere to project formatting.
  2. Run prek --all-files to verify repository hygiene, licenses, and spellings.
  3. Run ast-grep scan --error to ensure custom architectural rules pass.
  4. Run nix flake check to 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:

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.