Build ErsatzTV Image / Delimiter ban (release path) (push) Successful in 25s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 9m14s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 6m37s
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (push) Successful in 6m16s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Skipped
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 4m19s
Co-authored-by: Timothy <timothy@noreply.gitea.tblindustries.be>
72 lines
3.9 KiB
Bash
Executable File
72 lines
3.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# H11 (ersatztv#311) — refuse to push a branch that is BEHIND origin/main: rebase first, do NOT
|
|
# merge main in. A merge commit drags in files you never touched (e.g. the ~2500 legacy-BOM .cs),
|
|
# which then trips the pre-commit `dotnet format` hook on code that isn't yours (the #309 session).
|
|
# Rebasing keeps your diff to exactly what you changed.
|
|
#
|
|
# Fail-OPEN on anything we can't decide (a git pre-push hook has no "ask"): not a git repo,
|
|
# offline / fetch fails, no origin/main, HEAD unresolved -> allow the push. The only hard block is
|
|
# a positively-proven "behind origin/main". Deliberate exception: ETV_SKIP_REBASE_CHECK=1.
|
|
set -uo pipefail
|
|
|
|
# ersatztv#776 — report that this hook fired. MUST precede any stdin read.
|
|
# git hook: decides by exit code, and its stdout is live progress text.
|
|
ETV_HOOK_FIRE_LIB="${CLAUDE_PROJECT_DIR:-$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." 2>/dev/null && pwd)}/scripts/hook-fire-log.sh" || true
|
|
[ -r "$ETV_HOOK_FIRE_LIB" ] && . "$ETV_HOOK_FIRE_LIB" || true
|
|
type etv_hook_fire_begin >/dev/null 2>&1 || etv_hook_fire_begin() { :; }
|
|
etv_hook_fire_begin prepush-rebase-check "" stream || true
|
|
|
|
[ "${ETV_SKIP_REBASE_CHECK:-}" = "1" ] && exit 0
|
|
git rev-parse --git-dir >/dev/null 2>&1 || exit 0
|
|
|
|
# Tag-only push exemption (ersatztv#719): the release cut tags a commit on main while the local
|
|
# branch sits 1 commit behind origin/main, so H11 blocked EVERY release -- and its "rebase first"
|
|
# advice did not even apply, since no branch was being pushed. A tag push cannot revert anyone's
|
|
# merged work, which is the failure mode H11 exists to prevent, so skip the freshness check when
|
|
# EVERY ref being pushed is under refs/tags/. (See #719 for the observed flow.)
|
|
#
|
|
# Read pushed refs from stdin: git feeds pre-push hooks one line per ref, "<local ref> <local sha>
|
|
# <remote ref> <remote sha>" (.husky/pre-push forwards the lines it already captured). Ignore blank
|
|
# lines. VACUOUS-TRUTH GUARD: "all refs are tags" is trivially true when there are zero ref lines
|
|
# (hook run manually, stdin not forwarded, etc.) -- that would silently disable H11 for every push.
|
|
# Require at least one parsed ref line before granting the exemption; with zero lines, fall through
|
|
# to the existing branch-freshness check below (current behavior preserved).
|
|
#
|
|
# `[ -t 0 ] ||` so an interactive run does not hang waiting on a terminal: this script had no stdin
|
|
# reader before #719, and its own docs call "run by hand" a supported case. A TTY yields no ref
|
|
# lines, which is exactly the zero-line fall-through.
|
|
_h11_refs_seen=0
|
|
_h11_all_tags=1
|
|
[ -t 0 ] || while IFS=' ' read -r _h11_local_ref _h11_local_sha _h11_remote_ref _h11_remote_sha \
|
|
|| [ -n "${_h11_local_ref:-}" ]; do # `|| [ -n ... ]` also processes a final line with no trailing newline
|
|
[ -z "${_h11_local_ref:-}" ] && continue
|
|
_h11_refs_seen=1
|
|
case "${_h11_remote_ref:-}" in
|
|
refs/tags/*) ;;
|
|
*) _h11_all_tags=0 ;;
|
|
esac
|
|
_h11_local_ref=''
|
|
done
|
|
if [ "$_h11_refs_seen" = "1" ] && [ "$_h11_all_tags" = "1" ]; then
|
|
exit 0
|
|
fi
|
|
|
|
# Best-effort fetch of the latest main; offline / no network -> don't block.
|
|
git fetch origin main --quiet 2>/dev/null || exit 0
|
|
git rev-parse --verify --quiet origin/main >/dev/null 2>&1 || exit 0
|
|
|
|
# Pushing main itself, or a branch already rebased on top of it, means origin/main is an ANCESTOR
|
|
# of HEAD -> nothing to rebase, allow.
|
|
if git merge-base --is-ancestor origin/main HEAD 2>/dev/null; then
|
|
exit 0
|
|
fi
|
|
|
|
behind=$(git rev-list --count HEAD..origin/main 2>/dev/null || echo '?')
|
|
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo HEAD)
|
|
echo "husky - push blocked (H11): '$branch' is behind origin/main by $behind commit(s)."
|
|
echo " Rebase before pushing — do NOT merge main in (a merge drags in files you didn't touch,"
|
|
echo " e.g. legacy-BOM .cs, and trips the format hook on code that isn't yours):"
|
|
echo " git fetch origin main && git rebase origin/main"
|
|
echo " Deliberate exception: ETV_SKIP_REBASE_CHECK=1 git push"
|
|
exit 1
|