Skip to content

Commit bafad0c

Browse files
authored
Add prek-powered git pre-commit hooks (#3001)
* Add prek-powered git pre-commit hooks Adopts the fleet hook pattern established in basecamp/bc3#12423: prek (j178/prek, a Rust pre-commit reimplementation) installs as a sha256-verified prebuilt binary via mise's aqua backend, and this change introduces working commit hooks where none ran before. - .githooks/pre-commit is checked in with the bin/bcenv shebang; bcenv transparently re-execs through a login shell so GUI git clients resolve mise-managed tools - bin/setup points core.hooksPath at .githooks; anyone who runs bin/setup (including outside contributors) gets local blocking hooks from then on - prek.toml declares rubocop (staged files only, --force-exclusion honors .rubocop.yml) plus the merge-conflict/case-conflict/large-file/private-key safety builtins * bcenv: resync from bc3 — seed mise's standard install locations Byte-identical resync of the canonical bc3 copy (basecamp/bc3#12426): the non-interactive login shell never reads interactive rc files, so a mise activated only there (its documented setup) was invisible to the old /usr/local/bin:/usr/bin:/bin seed. ~/.local/bin (mise.run installer) and /opt/homebrew/bin (Apple Silicon brew) join the seed so the shims probe finds mise regardless. Verified upstream by bc3's bcenv behavioral matrix (22/22, negative control confirmed).
1 parent a1f9a85 commit bafad0c

5 files changed

Lines changed: 82 additions & 0 deletions

File tree

.githooks/pre-commit

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/usr/bin/env -S bin/bcenv bash
2+
exec prek run

.mise.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ idiomatic_version_file_enable_tools = ["ruby"]
55

66
[tools]
77
gum = "0.17.0"
8+
prek = "0.4.11"
89

910
[env]
1011
PROMETHEUS_EXPORTER_URL = "http://127.0.0.1:9306/metrics"

bin/bcenv

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
4+
# macOS doesn't launch GUI apps through a login shell, but
5+
# login shell profiles are how we conventionally set user-
6+
# specific environment variables like PATH. This can cause
7+
# trouble with apps like Tower that need to run git hooks.
8+
#
9+
# The `bcenv` command transparently re-execs the command
10+
# specified in the argument list through a login shell.
11+
12+
if [ $# -eq 0 ]; then
13+
echo "usage: bcenv command [args...]" 2>&1
14+
exit 1
15+
fi
16+
17+
if command -v dscl >/dev/null; then
18+
read -r SHELL < <( dscl . -read "/Users/$(whoami)" UserShell )
19+
elif command -v getent >/dev/null; then
20+
SHELL="$( getent passwd "$(whoami)" | cut -d: -f7 )"
21+
else
22+
SHELL="/bin/sh"
23+
fi
24+
25+
SHELL="${SHELL#*: }"
26+
27+
# mise-managed tools (node, ruby, bundler) must resolve in this context even when
28+
# the user's profile only activates mise for interactive shells — git hooks run
29+
# through bcenv as non-interactive login shells. Shims are mise's recommended
30+
# non-interactive activation; no-op when mise isn't on the login PATH. The snippet
31+
# is generated for the detected login shell; unknown shells get no preamble.
32+
#
33+
# The argument vector is forwarded POSITIONALLY, never serialized into shell
34+
# source text: no quoting dialect survives every login shell (bash %q output
35+
# is not fish syntax — newline and tab arguments made fish exit 127). POSIX
36+
# shells receive the vector after a $0 sentinel; fish exposes it as $argv.
37+
#
38+
# Tested support surface: bash, zsh, fish (test/bcenv-matrix-test). C shells
39+
# were never supported — csh/tcsh reject `-l` combined with `-c` outright,
40+
# before any payload syntax is reached.
41+
# Seed the base PATH a login shell normally inherits from PAM, plus mise's
42+
# standard install locations: ~/.local/bin (the mise.run installer) and
43+
# /opt/homebrew/bin (Apple Silicon brew; Intel brew is /usr/local/bin, package
44+
# managers are /usr/bin). The login shell here is non-interactive (-l -c), so
45+
# a profile that only activates mise in interactive rc files (.bashrc/.zshrc —
46+
# mise's documented setup) never adds it to PATH; the shims probe below must
47+
# find mise from the seed alone. With PATH unset, /etc/profile.d scripts run
48+
# without /usr/bin, and a not-found command can send a command-not-found
49+
# handler recursing into a fork bomb. The login profile still rebuilds its
50+
# PATH on top.
51+
base_path="$HOME/.local/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
52+
53+
# shellcheck disable=SC2016 # the preamble expands in the login shell, not here
54+
case "${SHELL##*/}" in
55+
bash | zsh | sh )
56+
mise_shims='command -v mise >/dev/null && eval "$(mise activate bash --shims)"; '
57+
exec env PATH="$base_path" "$SHELL" -l -c "${mise_shims}exec \"\$@\"" bcenv "$@" ;;
58+
fish )
59+
mise_shims='command -v mise >/dev/null; and mise activate fish --shims | source; '
60+
exec env PATH="$base_path" "$SHELL" -l -c "${mise_shims}exec \$argv" "$@" ;;
61+
* )
62+
exec env PATH="$base_path" "$SHELL" -l -c 'exec "$@"' bcenv "$@" ;;
63+
esac

bin/setup

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ fi
146146
step "Installing Ruby and tools" mise install --yes
147147
eval "$(mise hook-env -s bash)"
148148

149+
step "Installing Git pre-commit hooks" git config core.hooksPath .githooks
150+
149151
# Install gh if needed
150152
if ! command -v gh &>/dev/null; then
151153
echo

prek.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[[repos]]
2+
repo = "local"
3+
hooks = [
4+
{ id = "rubocop", name = "rubocop", language = "system", entry = "bin/rubocop --force-exclusion", types = ["ruby"] },
5+
]
6+
7+
[[repos]]
8+
repo = "builtin"
9+
hooks = [
10+
{ id = "check-merge-conflict" },
11+
{ id = "check-case-conflict" },
12+
{ id = "check-added-large-files" },
13+
{ id = "detect-private-key" },
14+
]

0 commit comments

Comments
 (0)