Package sections: always-on + advanced menu #3

Merged
yuri merged 2 commits from feat/package-sections into main 2026-10-27 13:57:40 +01:00
+173 -1
View File
@@ -395,7 +395,179 @@ fi
#------------------------------------------
# 6 Done
#------------------------------------------
step "7 Summary"
#------------------------------------------
# 6 Always-installed packages
#------------------------------------------
# Add a category here and it is installed on every run, no questions asked.
# Keep each category one line so diffs stay readable.
ALWAYS_PACKAGES=(
# Terminals
"ghostty ghostty-bash-completion"
)
step "6 Always-installed packages"
rule
for category in "${ALWAYS_PACKAGES[@]}"; do
# shellcheck disable=SC2086
install_packages $category
done
ok "Always-packages installed."
pause_if_debug
#------------------------------------------
# 7 Advanced: optional tool selection (menu-driven)
#------------------------------------------
# Add categories/apps by editing these parallel arrays:
# ADV_CATEGORIES : display order
# ADV_<NAME> : "pkg1 pkg2 ..." for each category (dots/minus -> underscore)
ADV_CATEGORIES=(
"Virtualisation"
"Browsers"
)
# shellcheck disable=SC2034 # arrays are read via nameref from ADV_CATEGORIES
ADV_Virtualisation=(
"qemu-kvm:QEMU/KVM (full virt stack):qemu-kvm libvirt virt-install virt-manager virt-viewer edk2-ovmf swtpm guestfs-tools libguestfs-tools"
"virtualbox:VirtualBox (Oracle):VirtualBox kernel-devel kernel-headers dkms akmod-VirtualBox"
)
# shellcheck disable=SC2034 # arrays are read via nameref from ADV_CATEGORIES
ADV_Browsers=(
"firefox:Firefox:firefox"
"chromium:Chromium:chromium"
"google-chrome:Google Chrome (adds repo):google-chrome-stable"
"brave:Brave (adds repo):brave-browser"
)
run_advanced_menu() {
local done_any=false
while true; do
printf "\n"
color_gray "Optional tools - select a category to pick from:"
printf "\n"
local i=1 cat
for cat in "${ADV_CATEGORIES[@]}"; do
echo " $i) $cat"
i=$((i+1))
done
echo " x) Done with optional tools"
local choice
if ! read -t 90 -rp "Category [1-${#ADV_CATEGORIES[@]}/x] (default: x): " choice; then
printf "\n"; choice="x"
fi
[[ "${choice,,}" == "x" || -z "$choice" ]] && break
if [[ ! "$choice" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > ${#ADV_CATEGORIES[@]} )); then
err "Invalid choice: $choice"
continue
fi
cat="${ADV_CATEGORIES[$((choice-1))]}"
# apps for this category — nameref, because ${!var} on an array name
# would only give element 0 (that bug showed in testing: Browsers
# listed exactly one entry)
local var="ADV_${cat//[ .-]/_}"
local -n entries_ref="$var"
local -a entries
# shellcheck disable=SC2206
entries=("${entries_ref[@]}")
printf "\n"
color_cyan "$cat:"
local j=1 entry
local -a ids=() labels=()
for entry in "${entries[@]}"; do
ids+=("${entry%%:*}") # entry id
local rest="${entry#*:}"
labels+=("${rest%%:*}") # display label
echo " $j) ${labels[-1]}"
j=$((j+1))
done
echo " x) Back"
local sel
if ! read -t 90 -rp "Install which? (comma-separate for multiple, default: back): " sel; then
printf "\n"; continue
fi
[[ "${sel,,}" == "x" || -z "$sel" ]] && continue
# Resolve picks to package lists. Entry format: id:Label:packages...
local -a to_install=()
local pick n rest
IFS=',' read -ra picks <<< "$sel"
for pick in "${picks[@]}"; do
pick="${pick// /}"
[[ "$pick" =~ ^[0-9]+$ ]] || { err "Ignoring invalid pick: $pick"; continue; }
n=${#entries[@]}
if (( pick < 1 || pick > n )); then err "Ignoring out-of-range pick: $pick"; continue; fi
entry="${entries[$((pick-1))]}"
rest="${entry#*:}"; rest="${rest#*:}" # drop id and label -> packages
# word-split into SEPARATE packages: quoting $rest whole makes dnf
# look for one package named "qemu-kvm libvirt ..." (seen on the VM)
# shellcheck disable=SC2206
to_install+=(${rest})
done
if (( ${#to_install[@]} > 0 )); then
step "Installing from $cat: ${to_install[*]}"
# install_category_pkgs wires any needed third-party repo first.
# Failure must NOT kill the script: warn in red and drop back to
# the menu (set -e is bypassed for this one call).
if install_category_pkgs "$cat" "${to_install[@]}"; then
done_any=true
else
color_red " Installation failed: ${to_install[*]}"
color_red " (dnf reported no match / transaction failure — see output above)"
warn " Returning to the menu. The system is unchanged."
pause_if_debug
fi
unset to_install; local -a to_install=() # fresh slate for next pick
fi
done
$done_any && ok "Optional tools installed."
}
# Per-category pre-repo wiring. Add third-party repos here when you add an
# app that needs one; packages alone stay generic.
install_category_pkgs() {
local cat="$1"; shift
case "$cat" in
Browsers)
local needs_chrome=false needs_brave=false item
for item in "$@"; do
[[ "$item" == *google-chrome* ]] && needs_chrome=true
[[ "$item" == *brave* ]] && needs_brave=true
done
if $needs_chrome && ! pkg_installed google-chrome-stable; then
info "Adding Google Chrome repo..."
sudo dnf install -y fedora-workstation-repositories 2>/dev/null || true
sudo dnf config-manager setopt google-chrome.enabled=1 2>/dev/null || \
sudo dnf install -y "https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm"
fi
if $needs_brave && ! pkg_installed brave-browser; then
info "Adding Brave repo..."
sudo dnf install -y dnf-plugins-core
sudo dnf config-manager add --from-repofile=https://brave-browser-rpm-release.s3.brave.com/brave-browser.repo || \
true
sudo rpm --import https://brave-browser-rpm-release.s3.brave.com/brave-core.asc 2>/dev/null || true
fi
;;
esac
# Resilient install: report failure but return non-zero instead of
# aborting under set -e; the caller turns it into a red warning.
install_packages "$@" || return 1
return 0
}
# Interactive only: an unattended --yes run must not hang on this menu.
# Skip silently there; use --de/--twm/--dotfiles for everything unattended.
if [[ "$ASSUME_YES" != "true" ]]; then
run_advanced_menu
fi
pause_if_debug
step "8 Summary"
rule
ok "DE: $DE"
ok "TWM: $TWM"