# Hooks for prek (https://prek.j178.dev/). See README for setup. # The commit-msg hook needs its own git shim, so `prek install` alone does not # get it. Naming it here means a plain `prek install` installs both. default_install_hook_types = ["pre-commit", "commit-msg"] # crates/mms/tests/corpus/ is excluded from the whitespace hooks throughout. # It is MegaMek's scenario library copied verbatim, and the tests over it are # a claim about reading *their* files: normalising the line endings or adding # a final newline would edit the evidence, and one of the things worth knowing # is that the reader copes with a library written on three decades of # editors. [[repos]] repo = "https://github.com/pre-commit/pre-commit-hooks" rev = "v6.0.0" hooks = [ { id = "check-merge-conflict" }, { id = "check-added-large-files" }, { id = "check-toml" }, { id = "check-yaml" }, { id = "check-json" }, { id = "mixed-line-ending", args = ["--fix=lf"], exclude = '^(crates/mms/tests/corpus/|services/api/src/matches/scenarios/)' }, { id = "end-of-file-fixer", exclude = '^(crates/mms/tests/corpus/|services/api/src/matches/scenarios/)' }, { id = "trailing-whitespace", exclude = '^(crates/mms/tests/corpus/|services/api/src/matches/scenarios/)' }, ] [[repos]] repo = "https://github.com/shellcheck-py/shellcheck-py" rev = "v0.11.0.1" # Same flags as arena's shellcheck gate: -x follows sourced files, and # info-level notes do not gate. hooks = [{ id = "shellcheck", args = ["-x", "--severity=warning"] }] [[repos]] repo = "https://github.com/rbubley/mirrors-prettier" rev = "v3.9.6" # web/ only, and no JSON: package-lock.json is npm's to format, and reflowing # it would churn the diff on every install. # # Astro components need a parser prettier does not ship. web/.prettierrc.json # names it by path rather than by package name on purpose: prettier resolves a # bare name from the working directory, which is this one and has no # node_modules, and a path is resolved from the config file instead. hooks = [{ id = "prettier", files = '^web/.*\.(ts|mjs|css|astro)$' }] [[repos]] repo = "local" # The vendored schemas. They are copies of another repo's files, so this is # not where a schema gets fixed — a failure here means the copy is wrong. [[repos.hooks]] id = "goat-lex-lint" name = "goat lex lint" language = "system" entry = "goat lex lint" files = '^lexicons/.*\.json$' # A commit's scope is an epic id from plan/, so that a commit can be found # from the epic it belongs to. The script says what else is allowed and why. [[repos.hooks]] id = "commit-scope" name = "commit scope" language = "system" entry = "scripts/check-commit-scope.sh" stages = ["commit-msg"] # The other half of that: the epic files themselves. Their frontmatter is read # as an Astro content collection, so a malformed one breaks a site build rather # than a commit — too late and in the wrong place. A failure here names the file # and says what to change. # # Shape only: the six frontmatter keys, the id matching the filename, the links # resolving, and the `## Done` invariant plan/README.md describes. Whether an # epic is well scoped is nobody's script to judge. Reads all of plan/ every time # because half the checks are about the set — one file cannot tell whether it is # in the register — and stdlib python over thirty files is milliseconds. [[repos.hooks]] id = "plan-shape" name = "plan shape" language = "system" entry = "scripts/check-plan.py" pass_filenames = false files = '^plan/.*\.md$' # plan/README.md's five tables are generated from that same frontmatter, so a # row cannot quietly stop matching the epic it points at, and adding an epic # does not mean editing tables every other branch is also editing. A failure # here means the register is stale: run scripts/gen-plan-readme.py and stage # plan/README.md. It prints the diff, so the message says which rows moved. # # Checks rather than rewrites, unlike the social-card hook below. A commit # touching plan/ is a human writing prose in these same files, and a hook that # edited the register underneath them would be changing the thing being # written rather than reporting on it. # The unit table against the reports it is collected from. Both are committed # and neither regenerates the other, so a report refreshed without re-running # the script leaves every weight on a force card describing a machine that is # no longer in the fight - and a stale figure renders exactly as well as a # current one. # # Hermetic and instant: the inputs are the two report.json files in this repo, # so unlike boards.json or megamek-text.ts this needs no MegaMek to check. [[repos.hooks]] id = "unit-stats" name = "unit stats" language = "system" entry = "scripts/unit-stats.py --out web/src/content/unit-stats.json --check" pass_filenames = false files = '^(scripts/unit-stats\.py|web/src/content/unit-stats\.json|services/api/src/matches/(daily|scenarios)/report\.json)$' [[repos.hooks]] id = "plan-register" name = "plan register" language = "system" entry = "scripts/gen-plan-readme.py --check" pass_filenames = false files = '^plan/(.*\.md|order\.txt)$' [[repos.hooks]] id = "cargo-fmt" name = "cargo fmt" language = "system" entry = "cargo fmt --all --check" pass_filenames = false files = '\.rs$' [[repos.hooks]] id = "cargo-clippy" name = "cargo clippy" language = "system" entry = "cargo clippy --workspace --all-targets -- -D warnings" pass_filenames = false files = '(\.rs|Cargo\.(toml|lock))$' # The same gate `npm run build` runs first. Checks the components as well as # the TypeScript, which is why it is astro's checker and not tsc. Needs # web/node_modules, so run `npm --prefix web ci` after a fresh clone. [[repos.hooks]] id = "astro-check" name = "astro check" language = "system" entry = "npm --prefix web run typecheck" pass_filenames = false files = '^web/.*\.(ts|astro|md|json|mjs)$' # The card is committed, so nothing at build time notices when it stops # matching the drawing. These two are its whole input: the script reads # public/logo.svg and nothing else, and its palette and its text are # transcribed into the script rather than read from styles.css. Not # src/insignia.ts — that is the same drawing as DOM, and the card never reads # it; a change to the drawing lands in logo.svg too, which is what fires here. # # Rewrites the PNG, so a real change fails the commit and asks to be staged. # Byte-identical from unchanged input on one machine, but the text is drawn # with a system font, so a machine with a different one will rewrite the card # the first time it touches an input. Needs web/node_modules for sharp. [[repos.hooks]] id = "social-card" name = "social card" language = "system" entry = "npm --prefix web run social-card" pass_filenames = false files = '^web/(public/logo\.svg|scripts/social-card\.mjs)$'