# Third-party linter definitions, and the single place their versions are # pinned. As with GitHub Actions, each `rev:` is a full commit SHA, since a tag # can be moved to point at different code; the `# frozen: ` comment records # which release that is. Dependabot's `pre-commit` ecosystem keeps both current # (see .github/dependabot.yml). # # Two different front ends read this file: # # cargo xtask check hooks # runs every hook; needs no knowledge of prek # cargo xtask check hooks ... # runs only the named hooks, by id or by the # # aliases below: spelling, links, workflows, deps, # # structure # # ...and prek (https://prek.j178.dev) directly, as installed git hooks. CI's # Pre-commit hooks workflow (.github/workflows/hooks.yaml) goes through # `cargo xtask check hooks` as well, so CI and a local run execute the same # command. prek is a single-binary Rust # reimplementation of pre-commit; it reads this file and reimplements the # common hygiene hooks natively, so no Python environment is needed for them. # # Install: cargo binstall prek (or: uv tool install prek / brew install prek) # Enable the git hooks (optional): prek install # # Useful invocations: # prek run # staged files, commit-stage hooks # prek run --all-files # whole tree, commit-stage hooks (= check hooks) # prek run --all-files --stage pre-push # the cargo-backed check # prek run --all-files typos # a single hook # prek run --all-files --skip rustfmt # skip one hook # # The upstream Python `pre-commit` also reads this file, but spells two of those # flags differently: `--hook-stage` rather than `--stage`, and `SKIP=` in the # environment rather than `--skip`. # # Git hooks are entirely optional -- `cargo xtask ci quick` and # `cargo xtask ci full` are the documented contributor path and neither requires # `prek install`. default_install_hook_types: [pre-commit, commit-msg, pre-push] # `default_stages` only applies to hooks whose upstream manifest doesn't declare # its own `stages`; several of the hygiene hooks declare [pre-commit, pre-push, # manual], so they're pinned to the commit stage individually below. Keeping the # fixing hooks off the pre-push stage matters: a hook that rewrites a file during # `git push` leaves you with an already-made commit and unstaged fixes. default_stages: [pre-commit] repos: # --------------------------------------------------------------------------- # File hygiene. prek substitutes its own native Rust implementations for these # automatically, so nothing is cloned or installed for this block. # --------------------------------------------------------------------------- - repo: https://github.com/pre-commit/pre-commit-hooks rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0 hooks: - id: trailing-whitespace args: [--markdown-linebreak-ext=md] stages: [pre-commit] # Shell fragments inside compat test cases carry semantically meaningful # trailing whitespace; insta snapshots must match byte-for-byte; the # changelog is generated by git-cliff. exclude: ^(brush-shell/tests/cases/|CHANGELOG\.md$)|\.snap$ - id: end-of-file-fixer stages: [pre-commit] exclude: ^(brush-shell/tests/cases/|CHANGELOG\.md$)|\.snap$ - id: mixed-line-ending args: [--fix=lf] stages: [pre-commit] exclude: ^(brush-shell/tests/cases/|CHANGELOG\.md$)|\.snap$ - id: check-merge-conflict - id: check-symlinks - id: destroyed-symlinks stages: [pre-commit] - id: check-added-large-files stages: [pre-commit] - id: detect-private-key # brush is built and tested on macOS and Windows too. - id: check-case-conflict - id: check-illegal-windows-names - id: check-yaml - id: check-toml - id: check-json # .vscode/*.json are JSONC (comments), not strict JSON. exclude: ^\.vscode/ - id: check-executables-have-shebangs stages: [pre-commit] - id: check-shebang-scripts-are-executable stages: [pre-commit] # Rust files opening with an inner attribute (`#![...]`) look like they # have a shebang. exclude: \.rs$ # --------------------------------------------------------------------------- # Schema validation for release-plz.toml, so a misspelled key or wrong type # fails here instead of at release time. The schema is the one release-plz # publishes, as of a release-plz release tag. Dependabot doesn't track that # URL; bump the tag by hand when adopting newer release-plz config. # check-jsonschema caches the download, so only the first run needs the # network. # --------------------------------------------------------------------------- - repo: https://github.com/python-jsonschema/check-jsonschema rev: a76b92851fc9eede9f9bc4073eb55202f34eb0df # frozen: 0.39.0 hooks: - id: check-jsonschema name: Validate release-plz.toml files: ^release-plz\.toml$ # Upstream's manifest limits this hook to [json, yaml]. types_or: [toml] args: - --schemafile - https://raw.githubusercontent.com/release-plz/release-plz/release-plz-v0.4.3/.schema/latest.json # --------------------------------------------------------------------------- # Spelling. Configured by Cargo.toml [workspace.metadata.typos]. # --------------------------------------------------------------------------- - repo: https://github.com/crate-ci/typos rev: b859c0df7f391deba73030f79b957e62b4d81dc6 # frozen: varcon-core-v5.0.7 hooks: - id: typos alias: spelling # Report, don't rewrite. Overriding `args` replaces the upstream # manifest's `[--write-changes, --force-exclude]`, and dropping # --write-changes is the point: automatic corrections inside shell test # cases would silently change what the tests exercise. --force-exclude # is what makes typos honor its own exclude config for files named # explicitly on the command line, so keep it. args: [--force-exclude] stages: [pre-commit] # --------------------------------------------------------------------------- # GitHub Actions security analysis. Configured by zizmor.yml. This is the # offline pass, which fails fast; the online pass that uploads SARIF lives in # .github/workflows/workflow-checks.yaml and is not redundant with it. # --------------------------------------------------------------------------- - repo: https://github.com/zizmorcore/zizmor-pre-commit rev: c92afdf2c6ca336aaa8f39e2bd8d48f62dbd8e1f # frozen: v1.31.1 hooks: - id: zizmor alias: workflows args: [--no-progress, --offline] # --------------------------------------------------------------------------- # Link checking. Configured by lychee.toml. `types_or` mirrors lychee's own # default `--extensions` list (markdown, html, css, txt, xml), so this reaches # the same files as pointing lychee at the whole tree. # # Dependabot bumps this SHA but can't rewrite the comment, because lychee's # tags carry a `lychee-` prefix; fix the comment by hand in its bump PRs. # --------------------------------------------------------------------------- - repo: https://github.com/lycheeverse/lychee rev: 2bba271688c1abb1503097a064e6c3bc1d1b6a9b # frozen: lychee-v0.24.2 hooks: - id: lychee alias: links args: [--no-progress, --offline] types_or: [markdown, html, css, plain-text, xml] # --------------------------------------------------------------------------- # Dependency audit: advisories, licenses, bans, sources. Configured by # deny.toml. cargo-deny is a third-party cargo subcommand, not a toolchain # component, so rust-toolchain.toml does not pin it; this does. prek builds # the pinned release from source (`cargo install --locked`), a one-time cost # per version bump that is cached afterwards. Upstream's hook already passes # `--all-features check`, which runs every check, and only fires when a # Cargo.toml or Cargo.lock is among the files. # --------------------------------------------------------------------------- - repo: https://github.com/EmbarkStudios/cargo-deny rev: bca0dde53651ee946720e4540b5ce2610bec8f06 # frozen: 0.20.2 hooks: - id: cargo-deny alias: deps # --------------------------------------------------------------------------- # Structural lints over the syntax tree, for architectural rules that clippy # can't express. Configured by sgconfig.yml; the rules and their tests live # in .config/ast-grep/. Pre-existing violations are baselined in place, and # --error=unused-suppression fails once one of those is no longer needed, so # the baseline can only shrink. This is a thin community wrapper around the # official @ast-grep/cli npm package, whose version it tracks; a `repo: local` # hook would avoid it, but Dependabot skips those. # --------------------------------------------------------------------------- - repo: https://github.com/boidolr/ast-grep-pre-commit rev: 13126a89e67880b0dac82cf40350abd2cfedd458 # frozen: 0.51.0 hooks: - id: ast-grep alias: structure args: [--error=unused-suppression] types: [rust] # Each rule's test cases must or mustn't match. Skipping snapshot tests # skips the extra comparison of each match's exact output (span, message) # against a recorded __snapshots__ file, which would need regenerating on # every rule edit without telling us more about what the rule catches. - id: ast-grep-rule-tests args: [--skip-snapshot-tests] files: ^(sgconfig\.yml|\.config/ast-grep/) # --------------------------------------------------------------------------- # Conventional Commits, as required by CONTRIBUTING.md and consumed by # git-cliff and release-plz. # --------------------------------------------------------------------------- - repo: https://github.com/compilerla/conventional-pre-commit rev: 33b50eb042bee26eac0ee96ad84aa9c1b4dccdd8 # frozen: v4.5.0 hooks: - id: conventional-pre-commit stages: [commit-msg] args: [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test] # --------------------------------------------------------------------------- # Repo-specific checks. These delegate to xtask so that the Rust-side checks # have exactly one definition, and so that hook users and non-hook users run # identical code. # --------------------------------------------------------------------------- - repo: local hooks: # `rustfmt` rather than `cargo fmt --all`: passing the staged files formats # only what is being committed. `cargo fmt --all` would rewrite files # outside the commit, and pre-commit stashes unstaged changes before # running hooks, so rewriting a file that also has unstaged edits makes the # stash restore conflict. rustfmt reads rustfmt.toml for the edition. - id: rustfmt name: rustfmt entry: rustfmt language: system types: [rust] require_serial: true # One hook, not four. `ci quick` is the same command CONTRIBUTING.md tells # contributors to run, so people who install the hooks and people who # don't execute identical code. It covers fmt, build, clippy, and unit # tests; schemas, cargo-deny, and integration tests are left to # `cargo xtask ci full` and to CI. # # Deliberately `ci quick` and not `ci full`: `ci full` runs the pre-commit # hooks, which invoke prek, which is what is running this hook. xtask # refuses to start prek when PRE_COMMIT is set, so getting this wrong # fails fast rather than recursing. - id: xtask-ci-quick name: cargo xtask ci quick entry: cargo xtask ci quick language: system files: \.rs$|(^|/)Cargo\.(toml|lock)$|(^|/)(rustfmt|clippy)\.toml$ pass_filenames: false require_serial: true stages: [pre-push]