Files
transcience/.githooks/pre-commit
T
claude c4beeae38f Initial commit: Transcience MVP
Top-down twin-stick bullet-hell, Godot 4.7, server-authoritative dedicated
server with client-side prediction. Clients send input only; the server
resolves every hit for both players and enemies (no PvP).

- SimWorld: whole simulation as plain RefCounted objects (no nodes, no
  physics server), ~0.24ms/tick at peak load -- runs headless for free and
  drives 78 tests in under a second
- BulletPool: struct-of-arrays bullet storage, replicated as spawn/despawn
  events rather than per-tick state
- Emitter framework (Ring/AimedSpread/WallGap/ArcSweep) shared by trash
  enemies and bosses -- a new boss is data in src/content/content.gd, no
  simulation changes
- The Warden of the Fold: stationary 4-phase boss built entirely on that
  format
- Lobby hub with a portal into on-demand dungeon instances; one process
  hosts the hub plus every concurrent dungeon
- Emergency escape: 3s server-owned channel, cancelled by damage
- tools/check.sh, test.sh (GUT), smoke.sh (real server + bot clients over
  ENet), bench.gd; git hooks wired to the same scripts
- docs/ARCHITECTURE.md, NETCODE.md, WORKFLOW.md, ROADMAP.md
2026-09-03 16:03:57 +02:00

87 lines
2.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# Fast, local sanity checks before a commit is allowed to happen.
#
# Deliberately dependency-free (plain bash + git + godot), matching the rest
# of tools/*.sh -- no pip install, no pre-commit framework, so `tools/install-hooks.sh`
# is the entire setup cost for a fresh clone.
#
# Scope: things that are cheap enough to run on every commit (a few seconds).
# The full end-to-end smoke test lives in pre-push instead, where 35s is a
# reasonable price to pay less often.
set -uo pipefail
cd "$(git rev-parse --show-toplevel)"
fail=0
staged=$(git diff --cached --name-only --diff-filter=ACMR)
if [[ -z "$staged" ]]; then
exit 0
fi
echo "pre-commit checks..."
# --- Hygiene, on every commit, regardless of file type -----------------
if git diff --cached --check --diff-filter=ACMR | grep -q .; then
echo "FAIL: unresolved merge conflict markers"
git diff --cached --check --diff-filter=ACMR
fail=1
fi
big=$(git diff --cached --diff-filter=ACMR --name-only -z | \
xargs -0 -I{} du -k "{}" 2>/dev/null | awk '$1 > 5120 {print}')
if [[ -n "$big" ]]; then
echo "FAIL: file(s) over 5MB staged -- game assets belong outside git history,"
echo " or need Git LFS if they truly must be tracked:"
echo "$big" | sed 's/^/ /'
fail=1
fi
if grep -qE '^\.godot/$' <(git diff --cached --name-only) 2>/dev/null || \
git diff --cached --name-only | grep -qE '^\.godot/'; then
echo "FAIL: .godot/ is generated (import cache + class cache) and must stay gitignored"
fail=1
fi
# --- GDScript-specific, only when a .gd file is staged ------------------
# addons/ is vendored (GUT) -- not ours to hold to our own style or to parse-
# check on every commit; it never changes except on a deliberate upgrade.
gd_files=$(echo "$staged" | grep -E '\.gd$' | grep -vE '^addons/' || true)
if [[ -n "$gd_files" ]]; then
# The Godot style guide (and every file in this repo) indents with tabs.
# A stray space-indented line is almost always a paste from somewhere else
# and silently breaks nothing today but fights every future diff.
space_indented=""
while IFS= read -r f; do
[[ -f "$f" ]] || continue
if grep -nP '^\t* ' "$f" | grep -qv '^\s*#'; then
space_indented+="$f"$'\n'
fi
done <<< "$gd_files"
if [[ -n "$space_indented" ]]; then
echo "FAIL: space-indented line(s) in (Godot style is tabs):"
echo "$space_indented" | sed 's/^/ /'
fail=1
fi
echo " running tools/check.sh (parse check)..."
if ! tools/check.sh; then
fail=1
fi
echo " running tools/test.sh (GUT suite)..."
if ! tools/test.sh; then
fail=1
fi
fi
if [[ $fail -ne 0 ]]; then
echo
echo "pre-commit FAILED. Fix the above, or skip deliberately with --no-verify."
exit 1
fi
echo "pre-commit OK"