diff options
| author | blasty <peter@haxx.in> | 2026-08-21 12:14:27 +0200 |
|---|---|---|
| committer | blasty <peter@haxx.in> | 2026-08-21 12:14:27 +0200 |
| commit | 93ca90558aa6016e77625c627a70640e693e3303 (patch) | |
| tree | 284dfb0d126aefd0bb69f517ec64ca12d2130bcb /.githooks/pre-commit | |
| parent | tests: repair the two seams the ida-nexus port left behind (diff) | |
| download | ida-tui-93ca90558aa6016e77625c627a70640e693e3303.tar.gz ida-tui-93ca90558aa6016e77625c627a70640e693e3303.tar.xz ida-tui-93ca90558aa6016e77625c627a70640e693e3303.zip | |
adopt ruff: pinned formatter + import sorting, opt-in pre-commit hook
PR #1 arrived black-formatted, which forced the question. ruff's formatter
is black's output, it is one static binary rather than a dependency chain,
and it is fast enough that a hook is free. ruff.toml pins the version
(a formatter whose output moves between releases turns 'formatted' into
'formatted by whoever committed last') and selects only import sorting on
the lint side -- a hook that refused a commit over an unused variable would
be a different feature.
.githooks/pre-commit (install: git config core.hooksPath .githooks) formats
exactly what is staged, refuses partly-staged files rather than quietly
widening a commit built with git add -p, and prefers the pinned .venv ruff.
Also drops the blanket '# ruff: noqa' PR #1 left in remote_ops.py -- it
would have exempted that file from the only lint rule we enforce.
Diffstat (limited to '.githooks/pre-commit')
| -rwxr-xr-x | .githooks/pre-commit | 53 |
1 files changed, 53 insertions, 0 deletions
diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..39cd4a5 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,53 @@ +#!/bin/sh +# Format the Python being committed, so what lands in git is already formatted. +# Imports sorted, then laid out -- both by ruff (ruff.toml pins the version). +# +# Install with `git config core.hooksPath .githooks`. Hooks are not versioned +# by git, so a hook in a repo has to be a file somebody opts into -- there is +# no way to ship one that runs on clone, and a repo that could would be a repo +# that runs code on clone. +# +# Skip a commit with `git commit --no-verify` when you mean to. +set -e + +py_files=$(git diff --cached --name-only --diff-filter=ACMR | grep -E '\.py$' || true) +[ -n "$py_files" ] || exit 0 + +# Where ruff is. The repo-local venv first, before $PATH: ruff.toml pins a +# version, and a system ruff of the wrong one would refuse the commit while +# the venv sitting right there has the version it asked for. Off the toplevel, +# so this works from a subdirectory. +root=$(git rev-parse --show-toplevel) +if [ -x "$root/.venv/bin/ruff" ]; then + ruff="$root/.venv/bin/ruff" +else + ruff=$(command -v ruff 2>/dev/null || true) +fi +if [ -z "$ruff" ]; then + echo "pre-commit: ruff is not installed, and this commit touches Python." >&2 + echo " uv sync --extra dev (a venv in .venv, version pinned)" >&2 + echo " ...or commit with --no-verify if you know what you are doing." >&2 + exit 1 +fi + +# A file with unstaged changes is the one case where formatting in place is +# dangerous: the formatter rewrites the *working tree*, and re-staging +# afterwards would commit work that was deliberately left out of the index. +# So say so and stop, rather than quietly widening a commit somebody built +# with `git add -p`. +partial="" +for f in $py_files; do + if ! git diff --quiet -- "$f"; then partial="$partial $f"; fi +done +if [ -n "$partial" ]; then + echo "pre-commit: these files are only partly staged, so formatting them" >&2 + echo " in place would add work you left out of the commit:" >&2 + for f in $partial; do echo " $f" >&2; done + echo " stage them fully, stash the rest, or use --no-verify." >&2 + exit 1 +fi + +cd "$root" +"$ruff" check --select I --fix --quiet $py_files +"$ruff" format --quiet $py_files +git add $py_files |
