Skip to content

grafana/flint

flint logo

flint — fast lint

Lint GitHub Release

Linter runner built for speed, consistency, and low setup friction:

  • Fast — native execution (no Docker), parallel, diff-aware (changed files only), opt-in (undeclared tools don't run), small binary cached by mise
  • Local == CI — one binary, one config, identical behavior
  • Sensible defaultsflint init scaffolds a working setup quickly, and most repos can stick with the generated defaults
  • Opinionated config — Flint chooses canonical config filenames per linter, while still letting you keep them in a directory such as .github/config
  • AI-friendly — fix silently, surface only what needs review
  • Separated ownership — dedicated linters and formatters own their file types to avoid overlapping rules and editor-config conflicts
  • Predictable and updatable linter versions — lint behavior stays stable until the repo intentionally updates pinned linter versions, for example via Renovate updates to mise.toml
  • Cross-platform — Linux, macOS, Windows
  • Autofix--fix fixes what's fixable; reports what still needs review

Read the background and principles and alternatives/comparisons.

Tip

Legacy v1 (bash task scripts): see README-V1.md.


Getting Started

Installation

Add flint to your repo's mise.toml:

[tools]
"aqua:grafana/flint" = "0.21.0"

Bootstrap a repo with flint init (scaffolds config). Install a pre-commit hook with flint hook install. This is appropriate even if the repo already has an existing mise.toml; flint init is not just for greenfield repos. You can choose which linters to enable during the prompt, or trim the generated tool list afterward if you run flint init --yes.

mise.toml setup

Flint reads your [tools] section to discover which linters to run — declaring a tool is the opt-in. No separate configuration needed to activate a check: if ShellCheck's Flint-managed tool key is present in [tools], flint runs shellcheck; otherwise that check is skipped. mise install puts all declared tools on PATH; flint picks up whatever is there.

Add the linting tools your project needs alongside the flint binary itself:

[tools]
"aqua:grafana/flint" = "0.21.0"

# Add whichever linters apply to your repo:
shellcheck              = "0.11.0"
shfmt                   = "v3.13.1"
actionlint              = "1.7.10"

Then wire up lint tasks:

[tasks.lint]
description = "Run all lints"
run = "flint run"

[tasks."lint:fix"]
description = "Auto-fix lint issues"
run = "flint run --fix"

CI setup

- name: Checkout code
  uses: actions/checkout@...
  with:
    fetch-depth: 0 # needed for merge-base detection

- name: Setup mise
  uses: jdx/mise-action@...

- name: Lint
  env:
    GITHUB_REPOSITORY: ${{ github.repository }}
    GITHUB_BASE_REF: ${{ github.base_ref }}
    GITHUB_HEAD_REF: ${{ github.head_ref }}
    PR_HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
    GITHUB_TOKEN: ${{ github.token }}
  run: mise run lint

fetch-depth: 0 is required for merge-base detection. GITHUB_TOKEN is needed by some checks that query GitHub, but not every check. If lychee link checks are enabled, see lychee for PR remap environment requirements.


Reference

CLI

See the CLI reference for commands and flags.

Config (flint.toml)

Optional. Place in the repo root (or in FLINT_CONFIG_DIR — see below). All settings have defaults.

[settings]
# base_branch = "dev"                   # branch to diff against; defaults to "main"
exclude = ["CHANGELOG.md", "vendor/**"] # glob patterns — exclude matching files

See linters for per-linter configuration.

FLINT_CONFIG_DIR

Set this env var to consolidate config files in one directory (e.g. .github/config):

# mise.toml
[env]
FLINT_CONFIG_DIR = ".github/config"

When set, flint.toml is loaded from that directory, and each linter that supports an explicit config path via a CLI flag will have it injected automatically when the corresponding canonical Flint-managed file exists there (see the "Config file" column in the table below). Files that are absent are silently skipped. Some tools still rely on project-root discovery semantics, and some alternate upstream config locations are rejected to avoid config drift. In practice, Flint is opinionated about which config filename each linter should use, but flexible about the directory those files live in.

Note

editorconfig-checker's config file (.editorconfig-checker.json) controls its own settings, not .editorconfig itself. Editorconfig discovery always walks up from the file being linted and cannot be redirected via a flag.

When a formatter explicitly owns line length for a file type, Flint writes that carve-out into the shared root .editorconfig so editors and editorconfig-checker stay aligned. Today this applies to Markdown via rumdl, Rust via rustfmt, and Java via google-java-format.

Note

Biome is also root-discovered on purpose. Flint treats root biome.jsonc as the canonical Biome config rather than managing it through FLINT_CONFIG_DIR.

Built-in linter registry

Click a name in the table below for details. See the linter reference for scope semantics and per-linter notes.

Name Description Fix
actionlint Lint GitHub Actions workflow files
biome Lint JS/TS/JSON files yes
biome-format Format JS/TS/JSON files yes
cargo-clippy Lint Rust code; runs on all .rs files, not just changed yes
cargo-fmt Format Rust code; runs on all .rs files, not just changed yes
codespell Check for common spelling mistakes yes
dotnet-format Format C# code yes
editorconfig-checker Check files comply with EditorConfig settings
flint-setup Keep Flint setup current and mise.toml lint tooling canonical yes
gofmt Format Go code yes
golangci-lint Lint Go code; uses --new-from-rev to scope analysis to changed code
google-java-format Format Java code yes
hadolint Lint Dockerfiles
ktlint Lint and format Kotlin code yes
license-header Check source files have the required license header
lychee Check for broken links
renovate-deps Verify Renovate dependency snapshot is up to date yes
ruff Lint Python code yes
ruff-format Format Python code yes
rumdl Lint Markdown files for style and consistency yes
ryl Lint YAML files for style and consistency yes
shellcheck Lint shell scripts for common mistakes
shfmt Format shell scripts yes
taplo Format TOML files yes
xmllint Validate XML files are well-formed

Versioning

This project uses Semantic Versioning. Breaking changes will be documented in CHANGELOG.md and will result in a major version bump.

Releasing

See RELEASING.md.

About

Flint is a fast linter runner that keeps local and CI checks consistent with one binary, one config, and only the tools your repo declares.

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Contributors