Feature: Bei fehlgeschlagenem Befehl Wiederholen/Ignorieren/Abbrechen abfragen
Der ERR-Trap fragt den Benutzer nun interaktiv, wie mit einem fehlgeschlagenen Befehl verfahren werden soll, statt die Installation sofort abzubrechen. Zusätzlich aktiviert setup_err_trap jetzt errtrace/functrace, damit Fehler auch innerhalb von lib/*.sh-Funktionen abgefangen werden statt das Skript stillschweigend zu beenden. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017WMUd2fGsPCtTCVQkSS5TA
This commit is contained in:
@@ -91,6 +91,7 @@ Dieses Repository ist ein modulares, automatisiertes Setup- und Konfigurations-F
|
|||||||
source "$SCRIPT_DIR/../lib/utils.sh"
|
source "$SCRIPT_DIR/../lib/utils.sh"
|
||||||
setup_err_trap
|
setup_err_trap
|
||||||
```
|
```
|
||||||
|
- `setup_err_trap` aktiviert `errtrace`/`functrace`, damit Fehler auch innerhalb von `lib/*.sh`-Funktionen abgefangen werden, und registriert `error_handler` als ERR-Trap. Schlägt ein Befehl fehl, wird der Benutzer interaktiv gefragt, ob der Befehl **wiederholt**, **ignoriert** oder die **Installation abgebrochen** werden soll (`ask_retry_ignore_abort`). In nicht-interaktiven Läufen (`CI=1`, `DEBIAN_FRONTEND=noninteractive`, `AUTO_CONFIRM=1`, keine TTY) wird immer automatisch abgebrochen, wie zuvor.
|
||||||
- Verwende für temporäre Verzeichnisse immer `mktemp -d` und stelle sicher, dass am Skriptende oder bei Fehlern aufgeräumt wird:
|
- Verwende für temporäre Verzeichnisse immer `mktemp -d` und stelle sicher, dass am Skriptende oder bei Fehlern aufgeräumt wird:
|
||||||
```bash
|
```bash
|
||||||
TMP_DIR="$(mktemp -d)"
|
TMP_DIR="$(mktemp -d)"
|
||||||
@@ -123,6 +124,8 @@ Dieses Repository ist ein modulares, automatisiertes Setup- und Konfigurations-F
|
|||||||
- `log_error "Nachricht"`: Rote Fehlermeldung (`[ERROR]`).
|
- `log_error "Nachricht"`: Rote Fehlermeldung (`[ERROR]`).
|
||||||
- `log_step "Titel"`: Hervorgehobener Abschnitts-Header.
|
- `log_step "Titel"`: Hervorgehobener Abschnitts-Header.
|
||||||
- `log_substep "Titel"`: Eingerückter Unterabschnitt.
|
- `log_substep "Titel"`: Eingerückter Unterabschnitt.
|
||||||
|
- `setup_err_trap`: Aktiviert `errtrace`/`functrace` und registriert `error_handler` als ERR-Trap für das gesamte Skript (inkl. `lib/*.sh`-Funktionen).
|
||||||
|
- `error_handler` / `ask_retry_ignore_abort` / `resume_after_error`: Zentrale Fehlerbehandlung. Bei einem fehlgeschlagenen Befehl wird interaktiv nach Wiederholen/Ignorieren/Abbrechen gefragt; nicht-interaktiv wird immer abgebrochen.
|
||||||
- `require_root`: Bricht ab, wenn das Skript nicht mit Root-Rechten ausgeführt wird.
|
- `require_root`: Bricht ab, wenn das Skript nicht mit Root-Rechten ausgeführt wird.
|
||||||
- `command_exists "tool"`: Prüft, ob ein Befehl im Pfad verfügbar ist.
|
- `command_exists "tool"`: Prüft, ob ein Befehl im Pfad verfügbar ist.
|
||||||
- `prompt_target_user`: Erkennt `$SUDO_USER` oder fragt interaktiv nach dem Zielbenutzer.
|
- `prompt_target_user`: Erkennt `$SUDO_USER` oder fragt interaktiv nach dem Zielbenutzer.
|
||||||
|
|||||||
@@ -58,15 +58,101 @@ log_substep() {
|
|||||||
|
|
||||||
# Error trap handler
|
# Error trap handler
|
||||||
setup_err_trap() {
|
setup_err_trap() {
|
||||||
|
# Required so the ERR trap also fires for failures inside sourced
|
||||||
|
# lib/*.sh functions (errtrace) and nested function calls (functrace),
|
||||||
|
# not just for top-level commands in the stage script itself.
|
||||||
|
set -o errtrace
|
||||||
|
set -o functrace
|
||||||
trap 'error_handler $? $LINENO "$BASH_COMMAND"' ERR
|
trap 'error_handler $? $LINENO "$BASH_COMMAND"' ERR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Re-arms 'set -e' for the *next* command only, via a self-removing DEBUG
|
||||||
|
# trap. Bash normally terminates the script right after an ERR trap when
|
||||||
|
# errexit is set, no matter what the trap does - the only way to make the
|
||||||
|
# script actually resume past the failed line is to have errexit disabled
|
||||||
|
# at the moment the trap returns. Must be the very last statement executed
|
||||||
|
# on the "resume" path (no trailing 'return'/'break'/...): any further
|
||||||
|
# command run inside error_handler itself would consume the DEBUG trap's
|
||||||
|
# one-shot reset before control ever gets back to the failed line.
|
||||||
|
resume_after_error() {
|
||||||
|
set +e
|
||||||
|
trap 'set -e; trap - DEBUG' DEBUG
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ask the user what to do about a failed command.
|
||||||
|
# Echoes one of: r (retry), i (ignore), a (abort). Defaults to "a" when
|
||||||
|
# not running interactively (piped install, CI, DEBIAN_FRONTEND=noninteractive,
|
||||||
|
# AUTO_CONFIRM=1) or when the prompt can't be read, so unattended installs
|
||||||
|
# keep failing hard exactly like before this feature existed.
|
||||||
|
ask_retry_ignore_abort() {
|
||||||
|
local is_interactive=0
|
||||||
|
if { [[ -t 0 ]] || has_tty; } && [[ "${DEBIAN_FRONTEND:-}" != "noninteractive" && "${CI:-}" != "1" && "${AUTO_CONFIRM:-}" != "1" ]]; then
|
||||||
|
is_interactive=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$is_interactive" -ne 1 ]]; then
|
||||||
|
echo "a"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local tty_in="/dev/tty"
|
||||||
|
local tty_out="/dev/tty"
|
||||||
|
if ! has_tty; then
|
||||||
|
tty_in="/dev/stdin"
|
||||||
|
tty_out="/dev/stderr"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local prompt answer=""
|
||||||
|
prompt="$(printf "%b[W]iederholen%b / %b[i]gnorieren%b / %b[a]bbrechen%b (Standard: a): " \
|
||||||
|
"$CLR_BOLD" "$CLR_RESET" "$CLR_BOLD" "$CLR_RESET" "$CLR_BOLD" "$CLR_RESET")"
|
||||||
|
read -r -p "$prompt" answer < "$tty_in" > "$tty_out" 2>&1 || answer="a"
|
||||||
|
answer="$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' <<< "$answer")"
|
||||||
|
|
||||||
|
case "$answer" in
|
||||||
|
[Ww]*) echo "r" ;;
|
||||||
|
[Ii]*) echo "i" ;;
|
||||||
|
*) echo "a" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
error_handler() {
|
error_handler() {
|
||||||
local exit_code="$1"
|
local exit_code="$1"
|
||||||
local line_no="$2"
|
local line_no="$2"
|
||||||
local bash_command="$3"
|
local bash_command="$3"
|
||||||
|
|
||||||
log_error "Command failed with exit code $exit_code at line $line_no: '$bash_command'"
|
log_error "Command failed with exit code $exit_code at line $line_no: '$bash_command'"
|
||||||
|
|
||||||
|
local choice
|
||||||
|
choice="$(ask_retry_ignore_abort)"
|
||||||
|
|
||||||
|
case "$choice" in
|
||||||
|
r)
|
||||||
|
# Bare shell keywords (return/exit/break/continue) can't be
|
||||||
|
# meaningfully re-run via eval - they'd just unwind the current
|
||||||
|
# function instead of redoing any actual work. Treat these like
|
||||||
|
# "ignore" instead of silently aborting on a nonsensical retry.
|
||||||
|
if [[ "$bash_command" =~ ^(return|exit|break|continue)([[:space:]]|$) ]]; then
|
||||||
|
log_warn "'$bash_command' kann nicht wiederholt werden - Fehler wird ignoriert."
|
||||||
|
resume_after_error
|
||||||
|
else
|
||||||
|
log_info "Wiederhole: $bash_command"
|
||||||
|
if eval "$bash_command"; then
|
||||||
|
log_success "Wiederholung erfolgreich, Installation wird fortgesetzt."
|
||||||
|
resume_after_error
|
||||||
|
else
|
||||||
|
error_handler "$?" "$line_no" "$bash_command"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
i)
|
||||||
|
log_warn "Fehler wird ignoriert, Installation wird fortgesetzt: $bash_command"
|
||||||
|
resume_after_error
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_error "Installation abgebrochen."
|
||||||
exit "$exit_code"
|
exit "$exit_code"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
# Root permission check
|
# Root permission check
|
||||||
|
|||||||
Reference in New Issue
Block a user