Switch git hooks from pre-commit to prek #276

Merged
eblume merged 1 commit from feature/switch-to-prek into main 2026-03-02 18:15:24 -08:00
12 changed files with 192 additions and 170 deletions
Showing only changes of commit 63263abb97 - Show all commits

Switch git hooks from pre-commit to prek

prek is a faster, Rust-native drop-in replacement for pre-commit.
Migrates config from .pre-commit-config.yaml to prek.toml, adds
built-in checks for case conflicts, private key detection, and
executable shebangs. Updates all doc references.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Erich Blume 2026-03-02 18:14:03 -08:00

View file

@ -6,4 +6,4 @@ This directory contains configuration for GitHub-ecosystem tooling only.
## Contents
- `actionlint.yaml` - Configuration for actionlint pre-commit hook (custom runner labels)
- `actionlint.yaml` - Configuration for actionlint prek hook (custom runner labels)

View file

@ -1,148 +0,0 @@
---
# See https://pre-commit.com for more information
# Run: uvx pre-commit run --all-files
# Install: uvx pre-commit install && uvx pre-commit install --hook-type commit-msg
repos:
# General file hygiene
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-added-large-files
args: ['--maxkb=1000']
- id: check-merge-conflict
- id: check-json
- id: check-yaml
args: ['--unsafe'] # Allow custom tags (ansible uses them)
- id: check-toml
# Secret detection
- repo: https://github.com/trufflesecurity/trufflehog
rev: v3.93.4
hooks:
- id: trufflehog
entry: trufflehog git file://. --since-commit HEAD --no-verification --fail
stages: [pre-commit, pre-push]
# YAML linting
- repo: https://github.com/adrienverge/yamllint
rev: v1.38.0
hooks:
- id: yamllint
args: ['-c', '.yamllint.yaml']
# Ansible linting
- repo: local
hooks:
- id: ansible-lint
name: ansible-lint
entry: env ANSIBLE_ROLES_PATH=ansible/roles ansible-lint
language: python
files: ^ansible/
additional_dependencies:
- ansible-lint>=26.1.1
- ansible-core>=2.15
# Python - ruff for linting and formatting
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.2
hooks:
- id: ruff
args: ['--fix']
- id: ruff-format
# Shell scripts - shellcheck and shfmt
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.11.0.1
hooks:
- id: shellcheck
args: ['--severity=warning']
- repo: https://github.com/scop/pre-commit-shfmt
rev: v3.12.0-2
hooks:
- id: shfmt
args: ['-i', '2', '-ci', '-bn'] # 2-space indent, case indent, binary newline
# TOML - taplo
- repo: https://github.com/ComPWA/taplo-pre-commit
rev: v0.9.3
hooks:
- id: taplo-format
- id: taplo-lint
# JSON formatting (prettier for consistent style)
- repo: https://github.com/rbubley/mirrors-prettier
rev: v3.8.1
hooks:
- id: prettier
types_or: [json]
args: ['--tab-width', '2']
# GitHub/Forgejo Actions workflow linting
- repo: https://github.com/rhysd/actionlint
rev: v1.7.11
hooks:
- id: actionlint-system
args: ['-config-file', '.github/actionlint.yaml']
files: ^\.forgejo/workflows/
# Forgejo workflow schema validation (via Dagger + forgejo-runner validate)
- repo: local
hooks:
- id: validate-workflows
name: validate-workflows
entry: mise run validate-workflows
language: system
files: ^\.forgejo/workflows/
pass_filenames: false
# Container version consistency
- repo: local
hooks:
- id: container-version-check
name: container-version-check
entry: mise run container-version-check
language: system
files: ^(containers/|service-versions\.yaml)
pass_filenames: false
# Mikado Branch Invariant (C2 changes)
- repo: local
hooks:
- id: mikado-branch-invariant-check
name: mikado-branch-invariant-check
entry: mise run mikado-branch-invariant-check
language: system
always_run: true
stages: [commit-msg]
# Documentation validation
- repo: local
hooks:
- id: docs-check-filenames
name: docs-check-filenames
entry: mise run docs-check-filenames
language: system
files: ^docs/.*\.md$
pass_filenames: false
- id: docs-check-links
name: docs-check-links
entry: mise run docs-check-links
language: system
files: ^docs/.*\.md$
pass_filenames: false
- id: docs-check-index
name: docs-check-index
entry: mise run docs-check-index
language: system
files: ^docs/.*\.md$
pass_filenames: false
- id: docs-check-frontmatter
name: docs-check-frontmatter
entry: mise run docs-check-frontmatter
language: system
files: ^docs/.*\.md$
pass_filenames: false

View file

@ -58,12 +58,12 @@ You'll need [Homebrew](https://brew.sh) and [mise](https://mise.jdx.dev):
```bash
brew bundle # install CLI tools (argocd, tea, flyctl, etc.)
mise install # install managed toolchains (ansible, pulumi, dagger, etc.)
uvx pre-commit install # set up pre-commit hooks
prek install # set up git hooks
```
Pre-commit hooks enforce secret scanning (TruffleHog), linting, formatting, and
custom checks like doc link validation and the Mikado branch invariant. Run
them manually with `uvx pre-commit run --all-files`.
Git hooks (via [prek](https://github.com/j178/prek)) enforce secret scanning
(TruffleHog), linting, formatting, and custom checks like doc link validation
and the Mikado branch invariant. Run them manually with `prek run --all-files`.
Operational tasks are driven through mise. Run `mise tasks` to see what's
available. Key examples:

View file

@ -0,0 +1 @@
Switch git hooks from pre-commit to [prek](https://github.com/j178/prek), a faster Rust-native drop-in replacement. Adds built-in checks for case conflicts, private key detection, and executable shebangs. Configuration migrated from `.pre-commit-config.yaml` to `prek.toml`.

View file

@ -281,7 +281,7 @@ tags:
| `mise run docs-mikado --resume` | Resume a chain: detect branch, show state and next steps |
| `mise run docs-mikado --resume <chain>` | Resume a specific chain with branch consistency check |
The `mikado-branch-invariant-check` commit-msg hook runs automatically on `mikado/*` branches, validating commit message conventions and invariant ordering. Requires `uvx pre-commit install --hook-type commit-msg`.
The `mikado-branch-invariant-check` commit-msg hook runs automatically on `mikado/*` branches, validating commit message conventions and invariant ordering. Requires `prek install --hook-type commit-msg`.
## Related

View file

@ -17,7 +17,7 @@ Monthly maintenance cycle for updating development tooling and CI dependencies.
| Category | Location | What to check |
|----------|----------|---------------|
| Pre-commit hooks | `.pre-commit-config.yaml` | `rev:` tags for all remote repos |
| Prek hooks | `prek.toml` | `rev:` tags for all remote repos |
| Fly.io proxy | `fly/Dockerfile` | Pinned image tags (nginx, alloy) |
| Mise task scripts | `mise-tasks/*` | Python `# dependencies` lower bounds |
| Forgejo workflows | `.forgejo/workflows/*.yaml` | `uses:` action versions |
@ -26,14 +26,14 @@ Out of scope: ArgoCD-deployed service images, Ansible role versions, NixOS flake
## Procedure
### 1. Check pre-commit hook versions
### 1. Check prek hook versions
For each repo in `.pre-commit-config.yaml` with a `rev:` tag, check the upstream GitHub releases page for a newer tag. Update each `rev:` to the latest release tag. Also check `additional_dependencies` entries for PyPI version bumps.
For each repo in `prek.toml` with a `rev =` value, check the upstream GitHub releases page for a newer tag. Update each `rev` to the latest release tag. Also check `additional_dependencies` entries for PyPI version bumps.
Verify after updating:
```fish
uvx pre-commit run --all-files
prek run --all-files
```
### 2. Check Fly.io Dockerfile pins
@ -77,5 +77,5 @@ Create a single PR with all dependency bumps. The changelog fragment type is `in
## Notes
- **Alloy version gaps**: Grafana Alloy releases frequently. Large version jumps (e.g., v1.5 to v1.13) are normal and generally safe — check the [changelog](https://github.com/grafana/alloy/releases) for breaking changes in the Alloy River config syntax.
- **Ruff minor bumps**: Ruff adds new lint rules in minor versions. A bump may surface new warnings. Run `uvx pre-commit run ruff --all-files` to check before committing.
- **Ruff minor bumps**: Ruff adds new lint rules in minor versions. A bump may surface new warnings. Run `prek run ruff --all-files` to check before committing.
- **shellcheck bumps**: New shellcheck versions may flag previously-ignored patterns. Review any new failures before updating.

View file

@ -28,7 +28,7 @@ Task-oriented instructions for common BlumeOps operations. These guides assume y
| [[expose-service-publicly]] | Expose a service to the public internet via Fly.io + Tailscale |
| [[manage-forgejo-mirrors]] | Create mirrors, update PATs, and rotate GitHub credentials |
| [[update-documentation]] | Publish docs via build-blumeops workflow |
| [[update-tooling-dependencies]] | Monthly update cycle for pre-commit, Fly, mise, and workflow deps |
| [[update-tooling-dependencies]] | Monthly update cycle for prek hooks, Fly, mise, and workflow deps |
## Knowledge Base

View file

@ -14,7 +14,7 @@ tags:
# Add Container Version Sync Check
Add a pre-commit check that validates version consistency across the three places container versions are declared: Dockerfile ARGs, `service-versions.yaml`, and nix derivations. No VERSION files needed — the existing sources are the source of truth, and the check enforces they agree.
Add a prek check that validates version consistency across the three places container versions are declared: Dockerfile ARGs, `service-versions.yaml`, and nix derivations. No VERSION files needed — the existing sources are the source of truth, and the check enforces they agree.
## Context
@ -38,7 +38,7 @@ Blacklisted containers (utility images, not tracked services): `kubectl`, `nette
Container-to-service name mapping: `quartz``docs`, `kiwix-serve``kiwix`.
### 2. Added pre-commit hook
### 2. Added prek hook
```yaml
- id: container-version-check
@ -62,7 +62,7 @@ The check discovered that ntfy's Dockerfile pins v2.17.0 but nixpkgs has ntfy-sh
| File | Change |
|------|--------|
| `mise-tasks/container-version-check` | New: typer CLI sync validation script |
| `.pre-commit-config.yaml` | Add `container-version-check` hook |
| `prek.toml` | Add `container-version-check` hook |
| `service-versions.yaml` | Populate `current-version` for all hybrid services + authentik |
## Verification

View file

@ -42,7 +42,7 @@ Each container's version is extracted at build time from existing declarations
- **Dockerfile builds**: parsed from `ARG CONTAINER_APP_VERSION=<value>` in the Dockerfile
- **Nix builds**: extracted from `version = "..."` in `default.nix`, or `CONTAINER_APP_VERSION` from the Dockerfile, or `dagger call nix-version` for nixpkgs packages
The [[add-container-version-sync-check]] pre-commit check ensures these declarations stay in sync with `service-versions.yaml`. See [[pin-container-versions]] for the work to ensure every container has a parseable version.
The [[add-container-version-sync-check]] prek check ensures these declarations stay in sync with `service-versions.yaml`. See [[pin-container-versions]] for the work to ensure every container has a parseable version.
### Image Tag Format

View file

@ -27,7 +27,7 @@ The repo includes a `Brewfile` and `mise.toml` for easy setup, but these are opt
- `tea` - Gitea/Forgejo CLI for creating PRs
- `argocd` - ArgoCD CLI for deployments
- `pre-commit` - Git hooks for validation
- `prek` - Git hooks for validation
### Using Brewfile (Optional)
@ -42,12 +42,12 @@ Mise manages language toolchains and runs tasks:
mise install # installs Python, Node.js, etc. from mise.toml
```
### Pre-commit Hooks
### Git Hooks (prek)
Pre-commit hooks validate changes on `git commit`:
Git hooks validate changes on `git commit`:
```bash
pre-commit install
pre-commit run --all-files # verify setup
prek install
prek run --all-files # verify setup
```
All hooks should pass on a fresh clone.

View file

@ -1,5 +1,5 @@
[tools]
"pipx:ansible-core" = { version = "latest", uvx = "true", uvx_args = "--with botocore --with boto3" }
"pipx:pre-commit" = { version = "latest", uvx = "true" }
prek = "latest"
pulumi = "latest"
dagger = "latest"

169
prek.toml Normal file
View file

@ -0,0 +1,169 @@
# prek.toml - Git hooks configuration
# Run: prek run --all-files
# Install: prek install && prek install --hook-type commit-msg
# Built-in hooks (fast, Rust-native — no external dependencies)
[[repos]]
repo = "builtin"
hooks = [
{ id = "trailing-whitespace" },
{ id = "end-of-file-fixer" },
{ id = "check-added-large-files", args = [
"--maxkb=1000",
] },
{ id = "check-merge-conflict" },
{ id = "check-json" },
{ id = "check-toml" },
{ id = "check-case-conflict" },
{ id = "detect-private-key" },
{ id = "check-executables-have-shebangs" },
]
# check-yaml with --unsafe (builtin fast path doesn't support --unsafe yet)
[[repos]]
repo = "https://github.com/pre-commit/pre-commit-hooks"
rev = "v6.0.0"
hooks = [{ id = "check-yaml", args = ["--unsafe"] }]
# Secret detection
[[repos]]
repo = "https://github.com/trufflesecurity/trufflehog"
rev = "v3.93.4"
hooks = [
{ id = "trufflehog", entry = "trufflehog git file://. --since-commit HEAD --no-verification --fail", stages = [
"pre-commit",
"pre-push",
] },
]
# YAML linting
[[repos]]
repo = "https://github.com/adrienverge/yamllint"
rev = "v1.38.0"
hooks = [{ id = "yamllint", args = ["-c", ".yamllint.yaml"] }]
# Ansible linting
[[repos]]
repo = "local"
[[repos.hooks]]
id = "ansible-lint"
name = "ansible-lint"
entry = "env ANSIBLE_ROLES_PATH=ansible/roles ansible-lint"
language = "python"
files = "^ansible/"
additional_dependencies = ["ansible-lint>=26.1.1", "ansible-core>=2.15"]
# Python - ruff for linting and formatting
[[repos]]
repo = "https://github.com/astral-sh/ruff-pre-commit"
rev = "v0.15.2"
hooks = [{ id = "ruff", args = ["--fix"] }, { id = "ruff-format" }]
# Shell scripts - shellcheck and shfmt
[[repos]]
repo = "https://github.com/shellcheck-py/shellcheck-py"
rev = "v0.11.0.1"
hooks = [{ id = "shellcheck", args = ["--severity=warning"] }]
[[repos]]
repo = "https://github.com/scop/pre-commit-shfmt"
rev = "v3.12.0-2"
hooks = [{ id = "shfmt", args = ["-i", "2", "-ci", "-bn"] }]
# TOML - taplo
[[repos]]
repo = "https://github.com/ComPWA/taplo-pre-commit"
rev = "v0.9.3"
hooks = [{ id = "taplo-format" }, { id = "taplo-lint" }]
# JSON formatting (prettier for consistent style)
[[repos]]
repo = "https://github.com/rbubley/mirrors-prettier"
rev = "v3.8.1"
hooks = [{ id = "prettier", types_or = ["json"], args = ["--tab-width", "2"] }]
# GitHub/Forgejo Actions workflow linting
[[repos]]
repo = "https://github.com/rhysd/actionlint"
rev = "v1.7.11"
hooks = [
{ id = "actionlint-system", args = [
"-config-file",
".github/actionlint.yaml",
], files = '\.forgejo/workflows/' },
]
# Custom local hooks
# Forgejo workflow schema validation (via Dagger + forgejo-runner validate)
[[repos]]
repo = "local"
[[repos.hooks]]
id = "validate-workflows"
name = "validate-workflows"
entry = "mise run validate-workflows"
language = "system"
files = '\.forgejo/workflows/'
pass_filenames = false
# Container version consistency
[[repos]]
repo = "local"
[[repos.hooks]]
id = "container-version-check"
name = "container-version-check"
entry = "mise run container-version-check"
language = "system"
files = "^(containers/|service-versions\\.yaml)"
pass_filenames = false
# Mikado Branch Invariant (C2 changes)
[[repos]]
repo = "local"
[[repos.hooks]]
id = "mikado-branch-invariant-check"
name = "mikado-branch-invariant-check"
entry = "mise run mikado-branch-invariant-check"
language = "system"
always_run = true
stages = ["commit-msg"]
# Documentation validation
[[repos]]
repo = "local"
[[repos.hooks]]
id = "docs-check-filenames"
name = "docs-check-filenames"
entry = "mise run docs-check-filenames"
language = "system"
files = '^docs/.*\.md$'
pass_filenames = false
[[repos.hooks]]
id = "docs-check-links"
name = "docs-check-links"
entry = "mise run docs-check-links"
language = "system"
files = '^docs/.*\.md$'
pass_filenames = false
[[repos.hooks]]
id = "docs-check-index"
name = "docs-check-index"
entry = "mise run docs-check-index"
language = "system"
files = '^docs/.*\.md$'
pass_filenames = false
[[repos.hooks]]
id = "docs-check-frontmatter"
name = "docs-check-frontmatter"
entry = "mise run docs-check-frontmatter"
language = "system"
files = '^docs/.*\.md$'
pass_filenames = false