docs: correct plan's rollback trap design from ERR to EXIT

die() calls exit directly; bash's ERR trap does not fire on an explicit
exit, only when a command's non-zero status propagates through normal
control flow. Verified empirically during Task 4 implementation - switched
to a ROLLBACK_ARMED-guarded EXIT trap, which fires on any shell exit.
This commit is contained in:
Marek Miklewicz
2026-07-05 21:49:51 +02:00
parent 42ec50afe4
commit c59d5b8c77
@@ -23,9 +23,11 @@ existing plugin helper library `mysql_common.sh`.
- Match existing test conventions in `tests/`: plain bash scripts with `fail()`/custom - Match existing test conventions in `tests/`: plain bash scripts with `fail()`/custom
asserts, fake command stubs installed via `PATH`/env-var injection, no external test asserts, fake command stubs installed via `PATH`/env-var injection, no external test
framework. framework.
- Every new/changed script keeps `set -Eeuo pipefail` (the `-E` flag matters here: it's - Every new/changed script keeps `set -Eeuo pipefail` and the existing `log()`/`die()`
what makes the `ERR` trap fire inside functions, which the rollback design depends on) pattern. The rollback design uses a `trap ... EXIT` (guarded by a `ROLLBACK_ARMED` flag),
and the existing `log()`/`die()` pattern. not `trap ... ERR``die()` calls `exit` directly, and bash's `ERR` trap does not fire
on an explicit `exit`, only on a command's non-zero status propagating through normal
control flow. This was verified empirically during Task 4's implementation.
- Runs only on AlmaLinux 8 or 9 — a stricter, separate check from `install_db.sh`'s own - Runs only on AlmaLinux 8 or 9 — a stricter, separate check from `install_db.sh`'s own
permissive `detect_os()`, refusing immediately (before touching anything) on CloudLinux, permissive `detect_os()`, refusing immediately (before touching anything) on CloudLinux,
Rocky, CentOS, plain RHEL, or AlmaLinux outside 8/9. Rocky, CentOS, plain RHEL, or AlmaLinux outside 8/9.
@@ -1057,7 +1059,10 @@ verify_da_connectivity_new_port() {
log "Potwierdzono łączność DirectAdmina z natywną instancją na porcie $NEW_NATIVE_PORT." log "Potwierdzono łączność DirectAdmina z natywną instancją na porcie $NEW_NATIVE_PORT."
} }
ROLLBACK_ARMED=0
rollback_service_state() { rollback_service_state() {
[ "$ROLLBACK_ARMED" -eq 1 ] || return 0
rollback_config_changes rollback_config_changes
restore_custombuild_mysql_management restore_custombuild_mysql_management
if [ -n "$NATIVE_SERVICE_NAME" ]; then if [ -n "$NATIVE_SERVICE_NAME" ]; then
@@ -1086,7 +1091,16 @@ main() {
preflight_new_port_free preflight_new_port_free
detect_native_service_name detect_native_service_name
trap 'rollback_service_state' ERR # Deliberately an EXIT trap, not an ERR trap. die() (used by every failure
# path below) calls `exit` directly, and bash's ERR trap does not fire for
# a function that terminates via an explicit `exit` call - it only fires
# when a command's non-zero status propagates through normal control flow.
# An EXIT trap fires on *any* shell exit (explicit exit, errexit-triggered
# exit, or falling off the end of the script), which is what "any failure
# from this point on triggers rollback" actually requires. ROLLBACK_ARMED
# guards it so it is a no-op before this point and after a successful run.
ROLLBACK_ARMED=1
trap 'rollback_service_state' EXIT
disable_custombuild_mysql_management disable_custombuild_mysql_management
apply_config_mutations apply_config_mutations
@@ -1095,7 +1109,8 @@ main() {
restart_directadmin_service restart_directadmin_service
verify_da_connectivity_new_port verify_da_connectivity_new_port
trap - ERR ROLLBACK_ARMED=0
trap - EXIT
log "Przejęcie portu natywnej instancji MariaDB zakończone powodzeniem: $NATIVE_CURRENT_PORT -> $NEW_NATIVE_PORT." log "Przejęcie portu natywnej instancji MariaDB zakończone powodzeniem: $NATIVE_CURRENT_PORT -> $NEW_NATIVE_PORT."
} }
``` ```