Fail when failing, stop when stopped, and survive what is neither
Four installer bugs, all in the space between exit codes and intent: - A flatpak-only extras category -- most of them -- died at the grep that filters out its dnf half, because grep exits 1 on zero matches and set -e read that as failure. sed deletes lines without editorial comment. The extras contract now runs a flatpak-only category under the installer's own strict options so this stays fixed. - A rate-limited GitHub API call aborted the whole package stage while resolving the RustDesk URL, even though the empty-result fallback was sitting right below it. The pipeline is now guarded so the fallback is reachable. - Ctrl-C did not stop the install: the INT trap ran cleanup and bash carried on with the remaining stages, MOK enrollment and firmware included. INT and TERM now exit explicitly; cleanup rides EXIT. - change-settings and link-dotfiles ran without set -e, so a failed copy over / or a failed symlink fell through to guarded no-ops and the stage reported success. Turning strictness on immediately caught what it had been hiding: link-dotfiles never created ~/.config, so on a truly fresh HOME every symlink was failing silently.
This commit is contained in:
@@ -38,7 +38,13 @@ cleanup() {
|
|||||||
# design, and one of them is an email address.
|
# design, and one of them is an email address.
|
||||||
[[ -n "${PANAMA_ANSWERS:-}" ]] && rm -f "$PANAMA_ANSWERS"
|
[[ -n "${PANAMA_ANSWERS:-}" ]] && rm -f "$PANAMA_ANSWERS"
|
||||||
}
|
}
|
||||||
trap cleanup EXIT INT TERM
|
trap cleanup EXIT
|
||||||
|
# A bare `trap cleanup INT` is not an abort: bash runs the handler and then
|
||||||
|
# carries on with the script, so Ctrl-C would kill only the current stage and
|
||||||
|
# the remaining ones -- MOK enrollment, firmware -- would still run. Exit
|
||||||
|
# explicitly instead; the EXIT trap above does the actual cleanup.
|
||||||
|
trap 'exit 130' INT
|
||||||
|
trap 'exit 143' TERM
|
||||||
|
|
||||||
gsettings set org.gnome.desktop.screensaver lock-enabled false 2>/dev/null || true
|
gsettings set org.gnome.desktop.screensaver lock-enabled false 2>/dev/null || true
|
||||||
gsettings set org.gnome.desktop.session idle-delay 0 2>/dev/null || true
|
gsettings set org.gnome.desktop.session idle-delay 0 2>/dev/null || true
|
||||||
|
|||||||
@@ -214,9 +214,12 @@ if rpm -q rustdesk >/dev/null 2>&1; then
|
|||||||
log "RustDesk already installed"
|
log "RustDesk already installed"
|
||||||
else
|
else
|
||||||
log "Resolving the latest RustDesk release..."
|
log "Resolving the latest RustDesk release..."
|
||||||
|
# `|| true` because a failed curl -- unauthenticated GitHub API calls get
|
||||||
|
# rate-limited -- would otherwise trip set -e and kill the stage before the
|
||||||
|
# empty-result fallback below could do its job.
|
||||||
rustdesk_url="$(curl -fsSL https://api.github.com/repos/rustdesk/rustdesk/releases/latest 2>/dev/null \
|
rustdesk_url="$(curl -fsSL https://api.github.com/repos/rustdesk/rustdesk/releases/latest 2>/dev/null \
|
||||||
| jq -r '.assets[].browser_download_url | select(test("x86_64\\.rpm$")) | select(test("suse") | not)' \
|
| jq -r '.assets[].browser_download_url | select(test("x86_64\\.rpm$")) | select(test("suse") | not)' \
|
||||||
| head -1)"
|
| head -1 || true)"
|
||||||
if [[ -n "$rustdesk_url" ]]; then
|
if [[ -n "$rustdesk_url" ]]; then
|
||||||
log "Installing RustDesk from $rustdesk_url"
|
log "Installing RustDesk from $rustdesk_url"
|
||||||
# The RPM ships rustdesk.service already enabled, which is what provides
|
# The RPM ships rustdesk.service already enabled, which is what provides
|
||||||
@@ -271,7 +274,9 @@ install_extra_category() {
|
|||||||
name="$(basename "$file")"
|
name="$(basename "$file")"
|
||||||
|
|
||||||
local dnf_packages flatpak_ids
|
local dnf_packages flatpak_ids
|
||||||
dnf_packages=$(catalog_all_targets "$file" | grep -v '^flatpak:' | tr "\n" " ")
|
# sed rather than grep -v: most categories are flatpak-only, and grep exits 1
|
||||||
|
# when it selects nothing, which set -e above turns into a dead stage.
|
||||||
|
dnf_packages=$(catalog_all_targets "$file" | sed '/^flatpak:/d' | tr "\n" " ")
|
||||||
flatpak_ids=$(catalog_all_targets "$file" | sed -n 's/^flatpak://p' | tr "\n" " ")
|
flatpak_ids=$(catalog_all_targets "$file" | sed -n 's/^flatpak://p' | tr "\n" " ")
|
||||||
|
|
||||||
if [[ -n "${dnf_packages// /}" ]]; then
|
if [[ -n "${dnf_packages// /}" ]]; then
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Strict: a failed mv or ln here used to fall through and exit 0, so a machine
|
||||||
|
# could end up half-linked while the installer's summary reported the stage as
|
||||||
|
# fine. Anything genuinely optional below carries its own `|| true`.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
# --- Helper functions ---
|
# --- Helper functions ---
|
||||||
log() { echo -e "\033[1;34m[INFO]\033[0m $*"; }
|
log() { echo -e "\033[1;34m[INFO]\033[0m $*"; }
|
||||||
|
|
||||||
@@ -10,8 +15,10 @@ PANAMA_DOT="$PANAMA_PATH/config/dot"
|
|||||||
PANAMA_OLD="$PANAMA_PATH/config/old"
|
PANAMA_OLD="$PANAMA_PATH/config/old"
|
||||||
CONFIG="$HOME/.config"
|
CONFIG="$HOME/.config"
|
||||||
|
|
||||||
# Make backup folder if it doesn't exist
|
# Make backup folder if it doesn't exist -- and ~/.config itself, which a
|
||||||
mkdir -p "$PANAMA_OLD"
|
# truly fresh HOME does not have yet. Before set -e above, its absence made
|
||||||
|
# every symlink below fail silently while the stage still reported success.
|
||||||
|
mkdir -p "$PANAMA_OLD" "$CONFIG"
|
||||||
|
|
||||||
# --- Bashrc ---
|
# --- Bashrc ---
|
||||||
echo -e "\n--- Replacing .bashrc ---"
|
echo -e "\n--- Replacing .bashrc ---"
|
||||||
|
|||||||
@@ -115,6 +115,31 @@ if grep 'flatpak install' <<<"$recorded" | grep -q 'from-dnf'; then
|
|||||||
note 'a dnf package is passed to flatpak'
|
note 'a dnf package is passed to flatpak'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# A flatpak-only category -- which most of the real ones are -- must survive
|
||||||
|
# the installer's own strict options. Filtering the dnf half with `grep -v`
|
||||||
|
# once left an exit 1 for zero matches, and set -e killed the stage before its
|
||||||
|
# flatpak half ran. Run under those options, not the contract's laxer ones;
|
||||||
|
# the status is captured rather than `||`-guarded because a condition context
|
||||||
|
# would switch errexit off inside the subshell and hide the very failure this
|
||||||
|
# pins.
|
||||||
|
: >"$calls"
|
||||||
|
flatpak_only="$work/flatpak-only"
|
||||||
|
printf 'flatpak:org.example.OnlyFlatpak\n' >"$flatpak_only"
|
||||||
|
(
|
||||||
|
set -euo pipefail
|
||||||
|
PATH="$stub_dir:$PATH"
|
||||||
|
log() { :; }
|
||||||
|
source "$catalog"
|
||||||
|
eval "$filter"
|
||||||
|
eval "$loop"
|
||||||
|
install_extra_category "$flatpak_only"
|
||||||
|
)
|
||||||
|
flatpak_only_status=$?
|
||||||
|
(( flatpak_only_status == 0 )) \
|
||||||
|
|| note 'a flatpak-only category aborts the installer under set -euo pipefail'
|
||||||
|
grep -q 'flatpak install -y flathub org.example.OnlyFlatpak' <<<"$(cat "$calls" 2>/dev/null)" \
|
||||||
|
|| note 'a flatpak-only category installs nothing'
|
||||||
|
|
||||||
# Choosing nothing installs nothing.
|
# Choosing nothing installs nothing.
|
||||||
: >"$calls"
|
: >"$calls"
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -52,8 +52,15 @@ done <<<"$consumed"
|
|||||||
|
|
||||||
# ── 3. Nothing is left behind ────────────────────────────────────────────────
|
# ── 3. Nothing is left behind ────────────────────────────────────────────────
|
||||||
|
|
||||||
grep -q 'trap cleanup EXIT INT TERM' "$install_script" \
|
# Cleanup rides the EXIT trap; INT and TERM must exit explicitly, because a
|
||||||
|| note 'install does not arm a cleanup trap on EXIT INT TERM'
|
# trap handler that merely cleans up lets bash carry on with the remaining
|
||||||
|
# stages after a Ctrl-C -- MOK enrollment and firmware included.
|
||||||
|
grep -q 'trap cleanup EXIT' "$install_script" \
|
||||||
|
|| note 'install does not arm a cleanup trap on EXIT'
|
||||||
|
grep -qE "trap 'exit [0-9]+' INT" "$install_script" \
|
||||||
|
|| note 'install does not exit on SIGINT, so Ctrl-C would keep installing'
|
||||||
|
grep -qE "trap 'exit [0-9]+' TERM" "$install_script" \
|
||||||
|
|| note 'install does not exit on SIGTERM, so a kill would keep installing'
|
||||||
grep -q 'rm -f "$PANAMA_ANSWERS"' "$install_script" \
|
grep -q 'rm -f "$PANAMA_ANSWERS"' "$install_script" \
|
||||||
|| note 'the cleanup trap does not delete the answers file'
|
|| note 'the cleanup trap does not delete the answers file'
|
||||||
grep -qE 'mktemp' "$install_script" \
|
grep -qE 'mktemp' "$install_script" \
|
||||||
|
|||||||
Reference in New Issue
Block a user