#!/usr/bin/env bash # Gemeinsame Hilfsfunktionen für die Phase-0-Spike-Skripte. # Wird von den 0x-Skripten eingebunden, nicht direkt ausgeführt. # Farben nur wenn wir auf einem Terminal landen — sonst verschmutzen sie das Protokoll. if [ -t 1 ]; then C_OK=$'\033[32m'; C_WARN=$'\033[33m'; C_ERR=$'\033[31m' C_HEAD=$'\033[1;36m'; C_DIM=$'\033[2m'; C_OFF=$'\033[0m' else C_OK=""; C_WARN=""; C_ERR=""; C_HEAD=""; C_DIM=""; C_OFF="" fi REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" OUT_DIR="$REPO_ROOT/spike/out" mkdir -p "$OUT_DIR" # Jeder Lauf bekommt sein eigenes Protokoll. Die Sammlung ist später die halbe # Auswertung — deshalb wird alles mitgeschrieben, auch was funktioniert hat. LOG="${LOG:-$OUT_DIR/$(basename "${0%.sh}").log}" log() { printf '%s\n' "$*" | tee -a "$LOG"; } section() { log ""; log "${C_HEAD}══ $* ══${C_OFF}"; } ok() { log "${C_OK} ✓${C_OFF} $*"; } warn() { log "${C_WARN} !${C_OFF} $*"; } fail() { log "${C_ERR} ✗${C_OFF} $*"; } hint() { log "${C_DIM} $*${C_OFF}"; } # Befehl ausführen, Ausgabe ins Protokoll, Fehler nicht tödlich. # Für alles, was scheitern DARF, ohne den Spike abzubrechen. try() { log "${C_DIM} \$ $*${C_OFF}" if "$@" >>"$LOG" 2>&1; then return 0 else local rc=$? warn "fehlgeschlagen (Code $rc): $*" return $rc fi } # Wie try(), aber ein Fehlschlag beendet den Spike. Für echte Voraussetzungen. need() { log "${C_DIM} \$ $*${C_OFF}" if ! "$@" >>"$LOG" 2>&1; then fail "Abbruch — notwendiger Schritt fehlgeschlagen: $*" log "" log "Vollständiges Protokoll: $LOG" exit 1 fi } have() { command -v "$1" >/dev/null 2>&1; } in_termux() { case "${PREFIX:-}" in *com.termux*) [ -d "$PREFIX/bin" ] ;; *) return 1 ;; esac } require_termux() { if ! in_termux; then fail "Dieses Skript läuft in Termux auf dem Telefon, nicht auf dem PC." exit 1 fi } # Prüft, ob die Grundlage steht, BEVOR ein Beweis losläuft. Ohne das scheitert ein # Test mitten im Ablauf und sieht aus wie ein inhaltlicher Fehlschlag, obwohl nur # die Vorbereitung fehlt — die irreführendste Sorte Fehler. voraussetzungen_pruefen() { local distro="${1:-debian}" fehlt=0 section "Voraussetzungen" if ! have proot-distro; then fail "proot-distro ist nicht installiert" hint "Nachholen mit: bash spike/00-bootstrap-termux.sh" return 1 fi # 'cannot execute: required file not found' heißt NICHT, dass das Skript fehlt. # Es heißt, dass der Interpreter aus seiner Shebang-Zeile fehlt. Der Unterschied # kostet sonst eine halbe Stunde Suche an der falschen Stelle. local pfad interpreter pfad="$(command -v proot-distro)" if ! proot-distro list >/dev/null 2>&1; then fail "proot-distro lässt sich nicht ausführen" interpreter="$(head -1 "$pfad" 2>/dev/null | sed 's|^#!||; s| .*||')" log " Datei: $pfad" log " Shebang: ${interpreter:-(keine)}" if [ -n "$interpreter" ] && [ ! -x "$interpreter" ]; then fail "Der Interpreter $interpreter fehlt oder ist nicht ausführbar." hint "Das ist die Ursache, nicht proot-distro selbst." hint "Reparieren mit: pkg install -y bash coreutils proot proot-distro" else hint "Reparieren mit: pkg reinstall -y proot-distro" fi return 1 fi ok "proot-distro ausführbar" # Anmeldeversuch statt Pfad- oder Ausgabeprüfung: proot-distro hat sein # Ablageformat gewechselt, und danach lag das Rootfs woanders als erwartet. # Diese Prüfung fragt nicht, wo etwas liegt, sondern ob es funktioniert. if proot-distro login "$distro" -- true >/dev/null 2>&1; then ok "$distro-Rootfs lauffähig" else fail "$distro-Rootfs fehlt oder startet nicht" hint "Nachholen mit: bash spike/00-bootstrap-termux.sh" fehlt=1 fi return $fehlt } # Fragt am Ende jedes Beweises nach dem, was nur ein Mensch beurteilen kann: # ob es tatsächlich funktioniert hat. Das Ergebnis geht ins Protokoll. verdict() { local beweis="$1" frage="$2" log "" log "${C_HEAD}── Beurteilung $beweis ──${C_OFF}" log "$frage" printf ' [j] ja [n] nein [t] teilweise > ' read -r antwort ' read -r notiz >"$OUT_DIR/ergebnisse.tsv" log "" log "Ins Protokoll übertragen: docs/spike-protokoll.md" log "Rohdaten: $LOG" } # Gerätesteckbrief — landet in jedem Protokoll, damit Meldungen vergleichbar sind. geraete_steckbrief() { section "Gerät" local p for p in ro.product.manufacturer ro.product.model ro.product.device \ ro.build.version.release ro.build.version.sdk ro.board.platform \ ro.hardware ro.soc.model; do log " $p = $(getprop "$p" 2>/dev/null || echo '?')" done log " Arch = $(uname -m)" log " Kernel = $(uname -r)" if have free; then log " RAM = $(free -h 2>/dev/null | awk '/Mem:/{print $2}')"; fi }