Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 19 additions & 10 deletions scripts/setup/git-hooks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,29 @@
# Optional global hygiene hook. Missing tools leave the current configuration alone.
set -euo pipefail
warn() { printf 'Warning: %s; repository hygiene hook installation skipped.\n' "$*" >&2; }
for tool in git python3 curl gitleaks install mktemp; do
for tool in git python3 curl install mktemp; do
if ! command -v "$tool" >/dev/null 2>&1; then
warn "missing $tool (install it and rerun scripts/setup/git-hooks.sh)"
exit 0
fi
done
revision=04da3fc73d9ead713b93700de7bbf54049b5ac3e
checker_sha256=849afd7097350fde0af1863c258bad09a6b75d9a378266001798335232046e59
hook_sha256=76f3740318ac4bc4769bbaa9ebcb3b8197ae2701980e309009967d241dde669e
hook_sha256=7d5cd9daa40ffca2b7526657f0f4aa169c125d65e448007a8dfa840894ba8eee
dest="$HOME/.local/share/git-hooks"
if [[ "${1:-}" == --dry-run ]]; then
printf 'Would install global repository hygiene %s at %s\n' "$revision" "$dest"
exit 0
fi
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT
for file in repo_hygiene.py pre-commit; do
if ! curl -fsSL "https://raw.githubusercontent.com/itpplasma/.github/$revision/scripts/$file" -o "$tmp/$file"; then
warn "cannot download $file"
exit 0
fi
done
if ! curl -fsSL --connect-timeout 5 --max-time 20 \
"https://raw.githubusercontent.com/itpplasma/.github/$revision/scripts/repo_hygiene.py" -o "$tmp/repo_hygiene.py"; then
warn 'cannot download the pinned checker'
exit 0
fi
# The public canonical hook is shared with the private prompts installer.
install -m 755 "$(dirname "${BASH_SOURCE[0]}")/pre-commit" "$tmp/pre-commit"
python3 - "$tmp" "$checker_sha256" "$hook_sha256" <<'VERIFY'
import hashlib, pathlib, sys
root = pathlib.Path(sys.argv[1])
Expand All @@ -32,8 +33,16 @@ for name, digest in zip(('repo_hygiene.py', 'pre-commit'), sys.argv[2:]):
raise SystemExit('SHA256 mismatch: ' + name)
VERIFY
mkdir -p "$dest/bin"
if [[ ! "$(command -v gitleaks)" -ef "$dest/bin/gitleaks" ]]; then
install -m 755 "$(command -v gitleaks)" "$dest/bin/gitleaks"
if command -v gitleaks >/dev/null 2>&1; then
if [[ ! "$(command -v gitleaks)" -ef "$dest/bin/gitleaks" ]]; then
install -m 755 "$(command -v gitleaks)" "$dest/bin/gitleaks"
fi
else
echo 'gitleaks not found: secret checks will warn and skip until it is installed.' >&2
fi
if curl -fsSL --connect-timeout 1 --max-time 2 \
https://api.github.com/repos/lazy-fortran/fo/releases/latest -o "$tmp/fo-latest.json"; then
install -m 644 "$tmp/fo-latest.json" "$dest/fo-latest.json"
fi
install -m 644 "$tmp/repo_hygiene.py" "$dest/repo_hygiene.py"
install -m 755 "$tmp/pre-commit" "$dest/pre-commit"
Expand Down
81 changes: 81 additions & 0 deletions scripts/setup/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/usr/bin/env bash
# One shared optional hook, also used by itpplasma/code's colleague installer.
set -euo pipefail
hook_dir="$(cd "$(dirname "$0")" && pwd)"
export PATH="$hook_dir/bin:$HOME/.local/bin:$PATH"

# The pinned checker does size/type checks before invoking gitleaks. Catch only
# its missing executable; all deterministic hygiene failures still block.
if command -v python3 >/dev/null && [[ -f "$hook_dir/repo_hygiene.py" ]]; then
python3 - "$hook_dir/repo_hygiene.py" <<'PY'
import importlib.util, os, subprocess, sys
spec = importlib.util.spec_from_file_location('repo_hygiene', sys.argv[1])
checker = importlib.util.module_from_spec(spec)
spec.loader.exec_module(checker)
try:
os.chdir(checker.git('rev-parse', '--show-toplevel').decode().strip())
status = checker.check(True, None, 'HEAD', None)
except FileNotFoundError as exc:
if exc.filename != 'gitleaks':
raise
print('gitleaks not found: secret checks skipped; install: https://github.com/gitleaks/gitleaks', file=sys.stderr)
status = 0
except (OSError, ValueError, subprocess.CalledProcessError) as exc:
print('repo-hygiene: ' + str(exc), file=sys.stderr)
status = 2
sys.exit(status)
PY
else
echo 'hygiene checker not found: checks skipped; rerun install-git-hooks.sh' >&2
fi

# Detect the project from the index, so an unstaged manifest cannot enable or
# suppress checks. Filename filters include fixed/free form and uppercase forms.
fortran=false
if git cat-file -e :fpm.toml 2>/dev/null; then
fortran=true
elif git cat-file -e :CMakeLists.txt 2>/dev/null; then
if [[ -n "$(git ls-files --cached -- '*.f' '*.F' '*.for' '*.FOR' '*.f90' '*.F90' '*.f95' '*.F95' '*.f03' '*.F03' '*.f08' '*.F08')" ]]; then
fortran=true
fi
fi
if $fortran; then
if ! command -v fo >/dev/null; then
echo 'fo not found: build/test pre-commit checks skipped; install: https://github.com/lazy-fortran/fo' >&2
elif ! fo hook --help 2>/dev/null | grep -q 'fo hook pre-commit'; then
echo 'fo hook unavailable: build/test pre-commit checks skipped; update: https://github.com/lazy-fortran/fo' >&2
else
# Release metadata is refreshed by the direct installer, never by a hook.
# Expired/missing/offline metadata produces no hint and never vetoes Git.
if command -v python3 >/dev/null; then
fo --version | python3 -c '
import json, pathlib, re, sys, time
try:
cache = pathlib.Path(sys.argv[1])
if time.time() - cache.stat().st_mtime <= 86400:
latest = json.loads(cache.read_text())["tag_name"]
current = sys.stdin.read()
version = lambda text: tuple(map(int, re.search(r"(\d+)\.(\d+)\.(\d+)", text).groups()))
if version(current) < version(latest):
print("fo update available (" + latest + "): https://github.com/lazy-fortran/fo", file=sys.stderr)
except (OSError, ValueError, KeyError, AttributeError):
pass
' "$hook_dir/fo-latest.json" || true
fi
fo hook pre-commit
fi
fi

common_dir="$(git rev-parse --git-common-dir)"
native_hook="$common_dir/hooks/pre-commit"
if [[ -x "$native_hook" && ! "$native_hook" -ef "$0" ]]; then
exec "$native_hook" "$@"
fi
if [[ -f .pre-commit-config.yaml ]]; then
if command -v prek >/dev/null; then
exec prek run --hook-stage pre-commit
elif command -v pre-commit >/dev/null; then
exec pre-commit run --hook-stage pre-commit
fi
echo 'pre-commit/prek not found: repository hook checks skipped; install: https://pre-commit.com' >&2
fi