aboutsummaryrefslogtreecommitdiffstats
path: root/.githooks/pre-commit
diff options
context:
space:
mode:
authorblasty <peter@haxx.in>2026-08-21 12:14:27 +0200
committerblasty <peter@haxx.in>2026-08-21 12:14:27 +0200
commit93ca90558aa6016e77625c627a70640e693e3303 (patch)
tree284dfb0d126aefd0bb69f517ec64ca12d2130bcb /.githooks/pre-commit
parenttests: repair the two seams the ida-nexus port left behind (diff)
downloadida-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-commit53
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