Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
dc2d8fa
fix: validate CI env for GitHub link remaps
zeitlinger Apr 28, 2026
1cd214b
docs: clarify GitHub token scope
zeitlinger Apr 29, 2026
61205f3
docs: clarify lychee GitHub token scope
zeitlinger Apr 29, 2026
8d01922
docs: keep lychee docs specific
zeitlinger Apr 29, 2026
69a2849
fix: validate renovate GitHub token env
zeitlinger Apr 29, 2026
d753269
fix: patch renovate config during init
zeitlinger Apr 29, 2026
abc56e4
refactor: move renovate init config helper
zeitlinger Apr 29, 2026
88472ca
fix: limit renovate init config patching
zeitlinger Apr 29, 2026
12cf57b
refactor: run selected linter init hooks
zeitlinger Apr 29, 2026
934f7c2
refactor: move linter-specific hooks to registry
zeitlinger Apr 29, 2026
8389717
style: apply clippy suggestion
zeitlinger Apr 29, 2026
b8c5127
style: format adaptive hook check
zeitlinger Apr 29, 2026
127e515
test: expect renovate token warning in explicit fast run
zeitlinger Apr 29, 2026
7d1535d
refactor: move biome init hook to linter module
zeitlinger Apr 29, 2026
deb0fe8
refactor: move renovate init logic to linter module
zeitlinger Apr 29, 2026
9a4de14
refactor: move remaining init hooks to linter modules
zeitlinger Apr 29, 2026
c3848d4
fix: run shared init hooks once
zeitlinger Apr 29, 2026
809db77
refactor: keep init hook identity with linters
zeitlinger Apr 29, 2026
c4ab468
refactor: model linter-owned hooks
zeitlinger Apr 29, 2026
a347334
refactor: keep special binaries with linters
zeitlinger Apr 29, 2026
4b6d286
refactor: extract special linter metadata
zeitlinger Apr 29, 2026
8233840
refactor: execute special linters via trait
zeitlinger Apr 29, 2026
b46eb63
refactor: drop unused special kinds
zeitlinger Apr 29, 2026
bf8c0fc
refactor: move special config display to linter
zeitlinger Apr 29, 2026
86a498e
refactor: replace special kind with setup flag
zeitlinger Apr 29, 2026
b14a0d3
refactor: use explicit fix builder
zeitlinger Apr 29, 2026
fa51fdf
refactor: rename linter model to check type
zeitlinger Apr 29, 2026
72aad8b
fix: avoid invalid renovate extends patch for empty config
zeitlinger Apr 29, 2026
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
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,10 @@ run = "flint run --fix"
run: mise run lint
```

The GitHub environment variables let flint remap base-branch links to the PR
branch when link checking. `fetch-depth: 0` is required for merge-base
detection.
`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](docs/linters.md#lychee) for PR remap environment
requirements.

---

Expand Down
2 changes: 1 addition & 1 deletion docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ A check runs against all matching files when:
- another supported baseline config for the check changed, such as
`.editorconfig` for `editorconfig-checker`
- `flint.toml` changed under `[settings]`
- `flint.toml` changed the check-specific config for a special check, such as
- `flint.toml` changed the check-specific config for a native check, such as
`[checks.links]` or `[checks.renovate-deps]`

`--full` is still the explicit whole-repo mode. The automatic baseline behavior
Expand Down
25 changes: 20 additions & 5 deletions docs/linters.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ still choose the config directory via `FLINT_CONFIG_DIR` where supported.
| Description | Keep Flint setup current and mise.toml lint tooling canonical |
| Fix | yes |
| Binary | (built-in) |
| Scope | [special](#scope-special) |
| Scope | [native](#scope-native) |
| Patterns | `mise.toml` |

Checks the repo's Flint-managed setup state and `mise.toml` layout.
Expand Down Expand Up @@ -182,7 +182,7 @@ With `--fix`, rewrites Flint-managed config in place and advances
| Description | Check source files have the required license header |
| Fix | no |
| Binary | (built-in) |
| Scope | [special](#scope-special) |
| Scope | [native](#scope-native) |

## `lychee`

Expand All @@ -191,7 +191,7 @@ With `--fix`, rewrites Flint-managed config in place and advances
| Description | Check for broken links |
| Fix | no |
| Binary | `lychee` |
| Scope | [special](#scope-special) |
| Scope | [native](#scope-native) |
| Config | via `[checks.links]` in flint.toml |

Orchestrates [lychee](https://lychee.cli.rs/) for link checking. Requires `lychee` in `[tools]`.
Expand All @@ -201,6 +201,13 @@ Default behavior: checks all links in changed files. When
in all files — useful when broken internal links from unchanged files also
matter.

In CI, `lychee` requires `GITHUB_TOKEN` so GitHub link checks can authenticate.
On GitHub Actions PR runs in changed-file mode, link remaps also require
`GITHUB_REPOSITORY`, `GITHUB_BASE_REF`, `GITHUB_HEAD_REF`, and `PR_HEAD_REPO`.
GitHub Actions provides the first three; set `PR_HEAD_REPO` from
`github.event.pull_request.head.repo.full_name`. `--full` does not require
the PR remap metadata.

Configure via `flint.toml`:

```toml
Expand All @@ -216,14 +223,22 @@ check_all_local = true
| Description | Verify Renovate dependency snapshot is up to date |
| Fix | yes |
| Binary | `renovate` |
| Scope | [special](#scope-special) |
| 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 |

Verifies `.github/renovate-tracked-deps.json` is up to date by running
Renovate locally and comparing its output against the committed snapshot.
Requires `renovate` in `[tools]`.

In CI, `renovate-deps` requires `GITHUB_COM_TOKEN` or `GITHUB_TOKEN`
so Renovate can authenticate GitHub requests. If `GITHUB_COM_TOKEN` is
unset, flint forwards `GITHUB_TOKEN` to Renovate as `GITHUB_COM_TOKEN`.

When `flint init` writes a new `flint.toml`, it includes this section if
`renovate-deps` is selected. During v1 setup migration it also carries
legacy `RENOVATE_TRACKED_DEPS_EXCLUDE` values into `exclude_managers`.

With `--fix`, automatically regenerates and commits the snapshot.

Configure via `flint.toml`:
Expand Down Expand Up @@ -347,7 +362,7 @@ Invoked once with no file args; for checks with patterns set (e.g.
whole project when it does run. `golangci-lint` is the exception — it uses
`--new-from-rev` to scope analysis to changed code even within the project run.

### Scope: special
### Scope: native

Implemented in-process rather than via a command template. These checks may run
without file arguments or use custom orchestration logic.
Expand Down
2 changes: 1 addition & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ pub fn load(config_dir: &Path) -> Result<Config> {
// FLINT_BASE_BRANCH, FLINT_EXCLUDE → settings.*
// FLINT_LYCHEE_CONFIG, FLINT_LYCHEE_* → checks.lychee.*
// FLINT_RENOVATE_DEPS_EXCLUDE_MANAGERS → checks.renovate_deps.*
// New Special checks added to the registry get env support automatically.
// New native checks added to the registry get env support automatically.
.merge(Env::prefixed("FLINT_").map(move |k| {
let k = k.as_str();
for (prefix, namespace) in &sections {
Expand Down
53 changes: 52 additions & 1 deletion src/files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::linters::renovate_deps::COMMITTED_PATHS;
/// Files managed by flint itself — always excluded from generic linter checks.
const BUILTIN_EXCLUDES: &[&str] = COMMITTED_PATHS;

#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct FileList {
pub files: Vec<PathBuf>,
/// Changed paths from git before user excludes are applied.
Expand Down Expand Up @@ -187,6 +187,57 @@ fn filter_names(
.collect()
}

pub fn match_files<'a>(
files: &'a [PathBuf],
patterns: &[&str],
exclude_patterns: &[&str],
project_root: &Path,
) -> Vec<&'a PathBuf> {
files
.iter()
.filter(|p| {
let rel = p.strip_prefix(project_root).unwrap_or(p);
let rel_str = rel.to_string_lossy();
let file_name = p
.file_name()
.map(|n| n.to_string_lossy())
.unwrap_or_default();
let included = patterns.iter().any(|pat| {
if *pat == "*" {
return true;
}
glob_match(pat, file_name.as_ref()) || glob_match(pat, rel_str.as_ref())
});
let excluded = exclude_patterns.iter().any(|pat| {
glob_match(pat, file_name.as_ref()) || glob_match(pat, rel_str.as_ref())
});
included && !excluded
})
.collect()
}

fn glob_match(pattern: &str, name: &str) -> bool {
// Simple glob: splits on `*` and checks that each segment appears in order.
// Handles `*.ext`, `prefix*`, `dir/*.yml`, etc.
let parts: Vec<&str> = pattern.splitn(2, '*').collect();
match parts.as_slice() {
[only] => name == *only || name.ends_with(&format!("/{only}")),
[prefix, suffix] => {
let n = name;
// The prefix must match the start of the name (or the part after the last slash).
let anchor_start = prefix.is_empty() || n.starts_with(prefix) || {
// Allow matching the basename portion for patterns like `*.sh`.
n.contains('/') && {
let after_slash = n.rfind('/').map(|i| &n[i + 1..]).unwrap_or(n);
prefix.is_empty() || after_slash.starts_with(prefix)
}
};
anchor_start && n.ends_with(suffix)
}
_ => false,
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
Loading
Loading