#!/usr/bin/env bash
#
# pre_push_check.sh -- Ein Skript, zwei Stufen:
# Stufe 1 (immer, ca. 1 Minute): msgfmt-Syntaxcheck + sphinx-lint + eigener
# Rollen-Konsistenz-Check (findet die meisten "echten" Bugs wie
# falsche/fehlende/ueberzaehlige :ref:/:term:/:class:/... -Rollen,
# OHNE dass ein kompletter Sphinx-Build noetig ist) + Auszeichnungs-
# pruefung (ueberfluessige Backtick-Klammern, uebersetzte Code-
# bloecke, abweichende ``Literale``)
# Stufe 2 (optional, ca. 10-20 Min. auf einem M1 Mac): echter Sphinx-Build
# wie in test-build.yml, fuer die letzte Sicherheit vor dem Push
#
# Der zu pruefende Branch wird automatisch erkannt (aktuell ausgecheckter
# Branch), nicht abgefragt.
#
# Nutzung:
# ./pre_push_check.sh # fragt interaktiv beide Stufen ab
# ./pre_push_check.sh --quick # nur Stufe 1, keine Nachfrage
# ./pre_push_check.sh --full # Stufe 1 + 2, keine Nachfrage
# ./pre_push_check.sh --full --version 3.15 # Stufe 2 mit expliziter Version
set -uo pipefail
REPO_ROOT="${REPO_ROOT:-$(pwd)}"
CPYTHON_ROOT="${CPYTHON_ROOT:-/Volumes/Dev_SSD/GitHub}"
CHECK_ROLES_PY="${CHECK_ROLES_PY:-$REPO_ROOT/check_roles.py}"
CHECK_MARKUP_PY="${CHECK_MARKUP_PY:-$REPO_ROOT/tools/check_markup.py}"
EXCLUDE_DIRS=(".venv" ".git" "cpython-src")
BRANCH="$(git -C "$REPO_ROOT" branch --show-current 2>/dev/null)"
if [[ -z "$BRANCH" ]]; then
echo "Konnte den aktuellen Git-Branch nicht erkennen (bist du in einem Git-Repo?)."
exit 1
fi
echo "Aktiver Branch: $BRANCH"
RUN_QUICK=true
RUN_FULL=false
VERSION="$BRANCH"
args=("$@")
for ((i=0; i/dev/null; then
echo "powrap nicht gefunden -- installiere mit: pip install powrap"
skip=$((skip + 1))
else
# Die CI formatiert nach jedem Push automatisch mit powrap und
# committet das Ergebnis. Wer lokal nicht formatiert, bekommt beim
# naechsten Rebase Konflikte in genau diesen Zeilen. Deshalb hier.
vorher=$(git -C "$REPO_ROOT" diff --name-only -- '*.po' | wc -l | tr -d ' ')
powrap -m 2>/dev/null
nachher=$(git -C "$REPO_ROOT" diff --name-only -- '*.po' | wc -l | tr -d ' ')
if [[ "$vorher" != "$nachher" ]]; then
echo "powrap hat weitere Dateien umbrochen -- bitte mit committen."
else
echo "OK -- Umbruch war bereits sauber."
fi
pass=$((pass + 1))
fi
log_section "1b/5 msgfmt --check (Syntax)"
if ! command -v msgfmt &>/dev/null; then
echo "msgfmt nicht gefunden -- gehoert zu gettext (brew install gettext)."
echo "Schritt uebersprungen."
skip=$((skip + 1))
msgfmt_errors=-1
fi
msgfmt_errors=${msgfmt_errors:-0}
while IFS= read -r -d '' po; do
[[ $msgfmt_errors -lt 0 ]] && break
rel="${po#"$REPO_ROOT"/}"
is_excluded "$rel" && continue
if ! out=$(msgfmt --check --check-format -o /dev/null "$po" 2>&1); then
echo "FEHLER: $rel"
echo "$out" | sed 's/^/ /'
msgfmt_errors=$((msgfmt_errors + 1))
fi
done < /dev/null; then
echo "sphinx-lint nicht gefunden -- installiere mit: pip install sphinx-lint"
skip=$((skip + 1))
else
lint_targets=()
while IFS= read -r -d '' po; do
rel="${po#"$REPO_ROOT"/}"
is_excluded "$rel" && continue
lint_targets+=("$po")
done < &1)
if [[ -z "$lint_out" || "$lint_out" == "No problems found."* ]]; then
echo "OK -- keine sphinx-lint-Warnungen."
pass=$((pass + 1))
else
echo "$lint_out"
fail=$((fail + 1))
fi
fi
log_section "1d/5 Rollen-Konsistenz (eigener Check, kein Build noetig)"
if [[ ! -f "$CHECK_ROLES_PY" ]]; then
echo "check_roles.py nicht gefunden unter $CHECK_ROLES_PY -- Schritt uebersprungen."
skip=$((skip + 1))
else
if python3 "$CHECK_ROLES_PY" "$REPO_ROOT" --exclude "$(IFS=,; echo "${EXCLUDE_DIRS[*]}")"; then
pass=$((pass + 1))
else
fail=$((fail + 1))
fi
fi
log_section "1e/5 Auszeichnung (ueberfluessige Backticks, Codebloecke, Literale)"
if [[ ! -f "$CHECK_MARKUP_PY" ]]; then
echo "check_markup.py nicht gefunden unter $CHECK_MARKUP_PY -- Schritt uebersprungen."
skip=$((skip + 1))
else
if python3 "$CHECK_MARKUP_PY" "$REPO_ROOT" \
--exclude "$(IFS=,; echo "${EXCLUDE_DIRS[*]}")" --max 10; then
pass=$((pass + 1))
else
fail=$((fail + 1))
fi
fi
fi
if ! $NONINTERACTIVE; then
echo
echo "Stufe 2: Vollstaendiger Sphinx-Build (Version $VERSION, wie test-build.yml)"
echo " -- ca. 10-20 Minuten auf einem M1 Mac."
read -r -p "Durchfuehren? [j/N]: " ans
if [[ "$ans" == "j" || "$ans" == "J" ]]; then
RUN_FULL=true
fi
fi
if $RUN_FULL; then
log_section "2/2 Voller Sphinx-Build (Version $VERSION)"
CPYTHON_SRC="$CPYTHON_ROOT/cpython-${VERSION}-src"
if [[ ! -d "$CPYTHON_SRC" ]]; then
echo "Kein Checkout unter $CPYTHON_SRC gefunden -- klone frisch (--depth 1)..."
git clone --branch "$VERSION" --single-branch --depth 1 \
https://github.com/python/cpython.git "$CPYTHON_SRC" || exit 1
else
echo "Aktualisiere vorhandenen Checkout..."
(cd "$CPYTHON_SRC" && git pull)
fi
if ! command -v uv &>/dev/null; then
echo "'uv' nicht gefunden -- installiere mit: pip install uv"
exit 1
fi
echo "Baue venv (make venv)..."
(cd "$CPYTHON_SRC/Doc" && make venv) || { echo "make venv fehlgeschlagen."; exit 1; }
echo "Installiere rsvg-convert, falls noetig (fuer LaTeX-Build)..."
if ! command -v rsvg-convert &>/dev/null; then
if command -v brew &>/dev/null; then
brew install librsvg
else
echo "Hinweis: rsvg-convert fehlt und Homebrew wurde nicht gefunden."
fi
fi
LOCALE_DIR="$CPYTHON_SRC/Doc/locales/de/LC_MESSAGES"
echo "Verknuepfe aktuellen Uebersetzungsstand ($BRANCH) nach $LOCALE_DIR ..."
rm -rf "$LOCALE_DIR"
mkdir -p "$(dirname "$LOCALE_DIR")"
cp -r "$REPO_ROOT/." "$LOCALE_DIR/"
rm -rf "$LOCALE_DIR/.git" "$LOCALE_DIR/.venv"
echo "Starte 'make html' mit -W --keep-going (wie CI) ..."
BUILD_LOG=$(mktemp)
(cd "$CPYTHON_SRC/Doc" && \
make -e SPHINXOPTS="--color -D language='de' -W --keep-going" html \
> "$BUILD_LOG" 2>&1)
build_status=$?
echo
echo "--- Warnungen/Fehler ---"
relevant=$(grep -E "WARNING|ERROR" "$BUILD_LOG" || true)
if [[ -z "$relevant" ]]; then
echo "Keine weiteren Warnungen gefunden."
else
echo "$relevant"
fi
echo
echo "Vollstaendiges Build-Log: $BUILD_LOG"
if [[ $build_status -eq 0 && -z "$relevant" ]]; then
echo "OK -- Build erfolgreich, keine (neuen) Warnungen."
pass=$((pass + 1))
else
echo "Build meldete Warnungen/Fehler (Exit-Code $build_status)."
fail=$((fail + 1))
fi
fi
log_section "Zusammenfassung (Branch: $BRANCH)"
if [[ $pass -eq 0 && $fail -eq 0 ]]; then
echo "Keine Pruefungen durchgefuehrt."
exit 0
fi
echo "Bestanden: $pass Fehlgeschlagen: $fail Uebersprungen: $skip"
if [[ $skip -gt 0 ]]; then
echo "Achtung: $skip Pruefung(en) konnten mangels Werkzeug nicht laufen."
fi
if [[ $fail -eq 0 ]]; then
echo "Alles sauber -- bereit zum Push."
exit 0
else
echo "Bitte die oben aufgefuehrten Punkte beheben, bevor gepusht wird."
exit 1
fi