Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ 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
- **Local + CI aligned** — one binary, one config model, local defaults tuned
for day-to-day work and broader coverage in CI
- **Sensible defaults** — `flint init` scaffolds a working setup quickly, and most
repos can stick with the generated defaults
- **Opinionated config** — Flint chooses canonical config filenames per linter,
Expand Down Expand Up @@ -88,6 +89,19 @@ description = "Auto-fix lint issues"
run = "flint run --fix"
```

Execution defaults:

| Invocation | Local (non-CI) | CI |
| -------------------- | ---------------------------- | --------------------------------------------------- |
| `flint run` | Linters triggered by changes | All active linters, still diff-aware where possible |
| `flint run --full` | All active linters | All active linters |
| `flint run <linter>` | Run that linter explicitly | Run that linter explicitly |

This means some failures may show up only in CI. When that happens, flint tells
you which command to run locally, usually `--full` or the linter name. That is
a reasonable default for day-to-day work: local runs stay focused on what you
changed, while CI runs linters based on changed files where possible.

### CI setup

```yaml
Expand Down
14 changes: 7 additions & 7 deletions docs/alternatives.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ the main [why/principles page](why.md).
Ratings are relative and intentionally coarse. The sections below explain the
"why" behind each row in more detail.

| Tool / approach | Speed | Setup effort | Cross-platform | Cross-language | Autofix support | Delta / diff-aware | Predictable and updatable linter versions | Local == CI |
| Tool / approach | Speed | Setup effort | Cross-platform | Cross-language | Autofix support | Delta / diff-aware | Predictable and updatable linter versions | Local + CI aligned |
| ------------------------- | --------------------- | ----------------------------- | --------------- | -------------- | ---------------------- | ------------------ | ----------------------------------------- | ------------------------- |
| flint | high | low | yes | yes | yes, where supported | yes | yes | yes |
| pre-commit | medium | medium | yes | yes | mixed | mixed | mixed | mixed |
Expand All @@ -19,7 +19,7 @@ Ratings are relative and intentionally coarse. The sections below explain the
Use these sections as relative comparisons against Flint on a few recurring
dimensions: speed, setup effort, cross-platform support, cross-language scope,
autofix support, delta/diff awareness, predictable and updatable linter
versions, and how closely local behavior matches CI.
versions, and how closely local and CI behavior stay aligned.

## Flint

Expand All @@ -41,7 +41,7 @@ linter or formatter should govern each domain.
| Autofix support | yes, where supported | `flint run --fix` uses each tool's fixer when one exists and reports what still needs review. |
| Delta / diff-aware | yes | Changed-file execution is the default model, with baseline expansion only when coverage changes require it. |
| Predictable and updatable linter versions | yes | Linter versions are pinned by the repo, so behavior stays stable until the repo intentionally updates to a newer version, for example through Renovate updates to `mise.toml`. |
| Local == CI | yes | The same binary, config model, and pinned tools are used in both environments. |
| Local + CI aligned | yes | The same binary, config model, and pinned tools are used in both environments, with local defaults tuned for changed-file feedback and CI activating the full linter set. |

## pre-commit

Expand All @@ -66,7 +66,7 @@ lives in hook wiring rather than in a single built-in policy.
| Autofix support | mixed | Some hooks fix in place, some only report, and behavior depends on the chosen hooks. |
| Delta / diff-aware | mixed | Hook-based runs are often scoped to staged files, but broader CI parity and baseline behavior depend on how each hook is configured. |
| Predictable and updatable linter versions | mixed | Hook revisions can be pinned, but version management lives in separate hook configuration instead of flowing through Renovate updates to `mise.toml`. |
| Local == CI | mixed | Teams often use pre-commit locally but a different command or environment in CI. |
| Local + CI aligned | mixed | Teams often use pre-commit locally but a different command or environment in CI. |

## Husky

Expand All @@ -86,7 +86,7 @@ with no install step and no language runtime dependency.
| Autofix support | hook-dependent | Whether fixes are available depends entirely on the commands wired into the hooks. |
| Delta / diff-aware | hook-dependent | It can run on changed or staged files, but only if the hook commands are written that way. |
| Predictable and updatable linter versions | hook-dependent | Husky only runs whatever commands the repo wires into hooks, so version stability depends on those underlying tools and how the repo manages them. |
| Local == CI | mixed | Husky is usually local-hook infrastructure, while CI often uses separate scripts or commands. |
| Local + CI aligned | mixed | Husky is usually local-hook infrastructure, while CI often uses separate scripts or commands. |

## Spotless and formatter plugins

Expand All @@ -112,7 +112,7 @@ clean.
| Autofix support | yes, formatter-focused | Formatter plugins are usually good at in-place fixes. |
| Delta / diff-aware | usually no | They commonly run at project or module scope rather than being natively optimized around changed-file diffs. |
| Predictable and updatable linter versions | usually yes in that ecosystem | Build plugins and formatter versions are often pinned through the build system, but the model is tied to that ecosystem rather than being a general lint-runner property. |
| Local == CI | usually yes in that build | Reusing the same build plugin in local and CI is straightforward when the repo already standardizes on that build system. |
| Local + CI aligned | usually yes in that build | Reusing the same build plugin in local and CI is straightforward when the repo already standardizes on that build system. |

## MegaLinter and super-linter

Expand All @@ -134,4 +134,4 @@ explicit style ownership instead of a broad kitchen-sink layer.
| Autofix support | mixed | Some integrated tools can fix in place, but support varies across the bundled linter set and may be awkward in container workflows. |
| Delta / diff-aware | limited / mixed | Some support changed-file or PR-oriented modes, but the model is usually broader and less native than a runner built around git diffs. |
| Predictable and updatable linter versions | mixed | The wrapper itself is versioned predictably, but the bundled linter set and containerized execution model can still make upgrades feel more indirect. |
| Local == CI | mixed | CI often uses the canonical containerized flow, while local usage may be slower, less common, or configured differently. |
| Local + CI aligned | mixed | CI often uses the canonical containerized flow, while local usage may be slower, less common, or configured differently. |
12 changes: 7 additions & 5 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ it do not need to re-learn the interface.
| -------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `--fix` | Fix what's fixable, report `clean` / `fixed` / `partial` / `review` outcomes; exit non-zero if anything needs action |
| `--full` | Lint all files instead of only changed files |
| `--fast-only` | Skip checks tagged as slow in the registry. Overridden by explicit linter names. |
| `--fast-only` | Force the filtered local run policy explicitly. Overridden by explicit linter names and `--full`. |
| `--short` | Compact summary output, no per-check noise |
| `--verbose` | Show all linter output, not just failures |
| `--new-from-rev REV` | Diff base (default: merge base with base branch) |
Expand All @@ -31,15 +31,17 @@ Every flag has an env var equivalent: `FLINT_FIX`, `FLINT_FULL`, `FLINT_FAST_ONL

| Context | Command | Why |
| ---------------------------- | -------------------------------------- | ----------------------------------------------------------------- |
| Interactive development | `flint run` or `flint run --fast-only` | Full output so you can read the details |
| Interactive development | `flint run` | Full output so you can read the details |
| Human wanting a summary | `flint run --short` | Compact output, no per-check noise |
| Pre-push hook (CC / agentic) | `flint run --fix --fast-only` | Fixes what it can silently, surfaces only what needs human review |
| Pre-push hook (CC / agentic) | `flint run --fix` | Fixes what it can silently, surfaces only what needs human review |
| CI | `flint run` | Full output for humans reading CI logs |

## Changed-file and baseline runs

By default, `flint run` checks only files changed relative to the merge base.
Use `--full` to check every matching file explicitly.
By default, local `flint run` checks linters triggered by changes relative to
the merge base. In CI, `flint run` activates the full linter set while still
keeping diff-aware scoping where each linter supports it. Use `--full` to
check every matching file explicitly.

Some changed-file runs intentionally expand one or more affected checks to all
matching files. This establishes a baseline when lint coverage changes, while
Expand Down
11 changes: 6 additions & 5 deletions docs/linters.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ check_all_local = true
| Binary | `renovate` |
| Scope | [native](#scope-native) |
| Patterns | `renovate.json renovate.json5 .github/renovate.json .github/renovate.json5 .renovaterc .renovaterc.json .renovaterc.json5` |
| Run policy | adaptive — runs in `--fast-only` only when relevant |
| Run policy | adaptive — runs in local default runs and `--fast-only` only when relevant |

Verifies `renovate-tracked-deps.json` next to the active Renovate
config is up to date by running Renovate locally and comparing its
Expand Down Expand Up @@ -384,11 +384,12 @@ without file arguments or use custom orchestration logic.

Checks use one of three run policies:

- `fast` — always runs, including in `--fast-only`
- `slow` — skipped by `--fast-only`
- `adaptive` — runs in `--fast-only` only when the changed files are relevant
- `fast` — always runs, including in local default runs and `--fast-only`
- `slow` — skipped in local default runs and by `--fast-only`
- `adaptive` — runs in local default runs and `--fast-only` only when the changed files are relevant

Use `--fast-only` for local/pre-push feedback and the full set in CI.
Local `flint run` already uses the filtered policy by default. Use `--full`,
an explicit linter name, or CI to run the broader set.

**`editorconfig-checker` defers to formatters**: `editorconfig-checker` runs on
all files, but automatically skips file types owned by an active formatter. If
Expand Down
10 changes: 6 additions & 4 deletions docs/why.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ This is the primary goal; everything else serves it.
- Small binary, cached by mise
- Diff-aware by default: changed files only unless `--full` is requested
- Opt-in activation: undeclared tools are skipped entirely
- Slow checks can be skipped via `--fast-only`
- Local runs skip slower checks by default unless you use `--full` or name the
linter explicitly

## Local same as CI
## Local and CI stay aligned

One binary, one config model, identical behavior. There is no "native mode
subset" distinction. If it passes locally, it passes in CI.
One binary, one config model, and the same pinned tools in both environments.
Local runs default to the change-triggered subset for day-to-day speed, while
CI activates the full linter set.

## Predictable and updatable linter versions

Expand Down
2 changes: 1 addition & 1 deletion src/hook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::process::Command;

const HOOK_CONTENT: &str = "#!/bin/sh\n\
# Installed by flint — run `flint hook install` to reinstall\n\
mise exec -- flint run --fix --fast-only\n";
mise exec -- flint run --fix\n";

/// Returns the repository-local pre-commit hook path for this git checkout.
pub(crate) fn pre_commit_path(project_root: &Path) -> Result<PathBuf> {
Expand Down
4 changes: 1 addition & 3 deletions src/init/scaffold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,7 @@ pub(super) fn maybe_install_hook(project_root: &Path, yes: bool) -> Result<()> {
let install = if yes {
true
} else {
print!(
"Install pre-commit hook (runs `flint run --fix --fast-only` before each commit)? [Y/n] "
);
print!("Install pre-commit hook (runs `flint run --fix` before each commit)? [Y/n] ");
io::stdout().flush()?;
let mut input = String::new();
io::stdin().lock().read_line(&mut input)?;
Expand Down
2 changes: 1 addition & 1 deletion src/linters/renovate_deps/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ struct PreparedRenovateDeps {
}

fn prepare(ctx: NativePrepareContext<'_>) -> Option<Box<dyn PreparedNativeCheck>> {
if !is_relevant(ctx.file_list, ctx.project_root) {
if ctx.filtered_run_policy && !is_relevant(ctx.file_list, ctx.project_root) {
return None;
}

Expand Down
Loading
Loading