Compare commits
1 Commits
36
...
47-prepush
| Author | SHA1 | Date | |
|---|---|---|---|
|
6498bb138d
|
99
.githooks/pre-push
Executable file
99
.githooks/pre-push
Executable file
@@ -0,0 +1,99 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# pre-push: run the cheap local gates before a commit reaches the forge.
|
||||||
|
#
|
||||||
|
# Install once per clone:
|
||||||
|
#
|
||||||
|
# git config core.hooksPath .githooks
|
||||||
|
#
|
||||||
|
# The AKGL build and mutation harness are opt-in because they are expensive:
|
||||||
|
#
|
||||||
|
# AKBASIC_HOOK_AKGL=1 git push
|
||||||
|
# AKBASIC_HOOK_MUTATION=1 git push
|
||||||
|
#
|
||||||
|
# Bypass everything with Git's escape hatch: git push --no-verify
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
ZERO_SHA=0000000000000000000000000000000000000000
|
||||||
|
|
||||||
|
root=$(git rev-parse --show-toplevel) || exit 1
|
||||||
|
cd "$root" || exit 1
|
||||||
|
|
||||||
|
# A push that only deletes refs, or an empty push input, has no new commit to
|
||||||
|
# test. Git calls hooks for those pushes too.
|
||||||
|
has_updates=0
|
||||||
|
while read -r _local_ref local_sha _remote_ref _remote_sha; do
|
||||||
|
if [ "$local_sha" != "$ZERO_SHA" ]; then
|
||||||
|
has_updates=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ "$has_updates" -eq 0 ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f deps/libakerror/CMakeLists.txt ]; then
|
||||||
|
echo "pre-push: deps/libakerror is empty. Run:" >&2
|
||||||
|
echo " git submodule update --init --recursive" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
builddir="${AKBASIC_HOOK_BUILD_DIR:-$(git rev-parse --git-dir)/akbasic-prepush}"
|
||||||
|
mkdir -p "$builddir" || exit 1
|
||||||
|
logfile="$builddir/last.log"
|
||||||
|
|
||||||
|
# Keep output quiet on success, but retain the complete failing command output
|
||||||
|
# so a failed push is actionable without rerunning the gate by hand.
|
||||||
|
run() {
|
||||||
|
if ! "$@" >"$logfile" 2>&1; then
|
||||||
|
echo >&2
|
||||||
|
echo "pre-push: FAILED: $*" >&2
|
||||||
|
echo "---------------------------------------------------------------" >&2
|
||||||
|
cat "$logfile" >&2
|
||||||
|
echo "---------------------------------------------------------------" >&2
|
||||||
|
echo "pre-push: push aborted. Use 'git push --no-verify' to override." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -x scripts/cppcheck.sh ]; then
|
||||||
|
if command -v cppcheck > /dev/null 2>&1; then
|
||||||
|
echo "pre-push: cppcheck"
|
||||||
|
run scripts/cppcheck.sh
|
||||||
|
else
|
||||||
|
echo "pre-push: cppcheck not installed, skipping the optional check" >&2
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "pre-push: scripts/cppcheck.sh not present, skipping the optional check" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "pre-push: default build + ctest"
|
||||||
|
run cmake -S . -B "$builddir/default"
|
||||||
|
run cmake --build "$builddir/default" --parallel 2
|
||||||
|
run ctest --test-dir "$builddir/default" --output-on-failure
|
||||||
|
|
||||||
|
echo "pre-push: sanitizer build + ctest"
|
||||||
|
run cmake -S . -B "$builddir/sanitize" -DAKBASIC_SANITIZE=ON
|
||||||
|
run cmake --build "$builddir/sanitize" --parallel 2
|
||||||
|
run ctest --test-dir "$builddir/sanitize" --output-on-failure
|
||||||
|
|
||||||
|
if [ "${AKBASIC_HOOK_AKGL:-0}" = "1" ]; then
|
||||||
|
echo "pre-push: AKGL build + ctest"
|
||||||
|
run cmake -S . -B "$builddir/akgl" -DAKBASIC_WITH_AKGL=ON
|
||||||
|
run cmake --build "$builddir/akgl" --parallel 2
|
||||||
|
run env SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy \
|
||||||
|
SDL_RENDER_DRIVER=software ctest --test-dir "$builddir/akgl" --output-on-failure
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${AKBASIC_HOOK_MUTATION:-0}" = "1" ]; then
|
||||||
|
threshold="${AKBASIC_MUTATION_THRESHOLD:-65}"
|
||||||
|
mutants="${AKBASIC_HOOK_MUTANTS:-260}"
|
||||||
|
echo "pre-push: mutation testing, threshold ${threshold}%"
|
||||||
|
run python3 scripts/mutation_test.py \
|
||||||
|
--target src/symtab.c \
|
||||||
|
--max-mutants "$mutants" \
|
||||||
|
--threshold "$threshold"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "pre-push: OK"
|
||||||
|
exit 0
|
||||||
12
README.md
12
README.md
@@ -22,6 +22,18 @@ cmake --build build --parallel
|
|||||||
ctest --test-dir build --output-on-failure
|
ctest --test-dir build --output-on-failure
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To run the same fast build, sanitizer and test gates before every push, install the advisory
|
||||||
|
pre-push hook once in this clone:
|
||||||
|
|
||||||
|
```sh norun
|
||||||
|
git config core.hooksPath .githooks
|
||||||
|
```
|
||||||
|
|
||||||
|
The hook keeps its build trees under `.git/akbasic-prepush`, skips the optional cppcheck gate
|
||||||
|
when its script or tool is unavailable, and leaves the AKGL build and mutation harness behind
|
||||||
|
`AKBASIC_HOOK_AKGL=1` and `AKBASIC_HOOK_MUTATION=1`. `git push --no-verify` remains the escape
|
||||||
|
hatch. `core.hooksPath` is local clone configuration, so CI remains the hard gate.
|
||||||
|
|
||||||
```sh norun
|
```sh norun
|
||||||
./build/basic # the REPL
|
./build/basic # the REPL
|
||||||
./build/basic tests/language/functions.bas # run a program
|
./build/basic tests/language/functions.bas # run a program
|
||||||
|
|||||||
Reference in New Issue
Block a user