diff --git a/modules/desktop_display.sh b/modules/desktop_display.sh index 396731c..821da5a 100644 --- a/modules/desktop_display.sh +++ b/modules/desktop_display.sh @@ -22,8 +22,9 @@ desktop_environment_menu() { while true; do local -a env_items=() env_items+=("1" "XFCE") - env_items+=("2" "Back") - # Future desktop environments: add "2" "GNOME", "3" "KDE", ... here + env_items+=("2" "LXDE") + env_items+=("3" "Back") + # Future desktop environments: add "3" "GNOME", "4" "KDE", ... here # and a matching case below calling its own menu (e.g. gnome_menu). local choice choice=$(_menu "Desktop Environment" \ @@ -33,11 +34,44 @@ desktop_environment_menu() { clear case "$choice" in 1) xfce_menu ;; - 2) break ;; + 2) lxde_menu ;; + 3) break ;; esac done } +lxde_menu() { + local choice + choice=$(_radiolist "LXDE" \ + "Select an option:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \ + "1" "LXDE (Escritorio completo)" OFF \ + "2" "LXDE Core (Instalación mínima)" OFF) + [ -z "$choice" ] && return 0 + clear + case "$(echo "$choice" | tr -d '"')" in + 1) _install_lxde_full ;; + 2) _install_lxde_core ;; + esac +} + +_install_lxde_full() { + echo -e "${GREEN}Installing LXDE (Full) + LightDM...${NC}" + echo "lightdm shared/default-x-display-manager select lightdm" | sudo debconf-set-selections + _run_cmd "LXDE Full" "sudo apt install -y lxde lightdm" \ + "Installing LXDE (Full Meta Package) + LightDM..." + sudo systemctl enable lightdm + echo -e "${GREEN}LXDE installed. LightDM enabled.${NC}" +} + +_install_lxde_core() { + echo -e "${GREEN}Installing LXDE Core + LightDM...${NC}" + echo "lightdm shared/default-x-display-manager select lightdm" | sudo debconf-set-selections + _run_cmd "LXDE Core" "sudo apt install -y lxde-core lightdm" \ + "Installing LXDE Core (Minimal) + LightDM..." + sudo systemctl enable lightdm + echo -e "${GREEN}LXDE Core installed. LightDM enabled.${NC}" +} + xfce_menu() { while true; do local -a xf_items=() diff --git a/modules/extras/internet/internet.sh b/modules/extras/internet/internet.sh index 707a01d..de7fb0b 100644 --- a/modules/extras/internet/internet.sh +++ b/modules/extras/internet/internet.sh @@ -185,13 +185,13 @@ _cat_internet() { local has_firefox=false local has_firefox_esr=false + local fchoice="" for _p in $cleaned; do [ "$_p" = "firefox" ] && has_firefox=true [ "$_p" = "firefox-esr" ] && has_firefox_esr=true done if $has_firefox && $has_firefox_esr; then - local fchoice fchoice=$(_menu "Firefox" "You selected both Firefox (Mozilla) and Firefox ESR.\nWhat do you want to do?" 12 60 3 \ "mozilla" "Remove Firefox ESR — keep Firefox" \ "esr" "Remove Firefox — keep Firefox ESR" \ @@ -206,10 +206,10 @@ _cat_internet() { for pkg in $cleaned; do case $pkg in firefox) - install_firefox_mozilla + install_firefox_mozilla "$fchoice" ;; firefox-esr) - install_firefox_esr + install_firefox_esr "$fchoice" ;; floorp) _enable_floorp_repo @@ -271,6 +271,7 @@ _cat_internet() { } install_firefox_mozilla() { + local fchoice="${1:-}" if [ "$DEBIAN_VERSION" -lt 12 ] 2>/dev/null; then _msg "Firefox" "Mozilla Firefox is only available on\nDebian 12 (Bookworm) and 13 (Trixie).\n\nSkipping installation." 10 60 return 1 @@ -282,13 +283,24 @@ install_firefox_mozilla() { fi if is_installed "firefox-esr"; then - if _confirm "Firefox ESR" "Firefox ESR is installed.\nRemove it before installing Mozilla Firefox?"; then - echo "Removing Firefox ESR..." - sudo apt remove -y firefox-esr - else - echo "Keeping Firefox ESR." - return - fi + case "$fchoice" in + both) + echo -e "${CYAN}[INFO] Keeping Firefox ESR (both selected).${NC}" + ;; + mozilla) + echo "Removing Firefox ESR (as selected)..." + sudo apt remove -y firefox-esr + ;; + *) + if _confirm "Firefox ESR" "Firefox ESR is installed.\nRemove it before installing Mozilla Firefox?"; then + echo "Removing Firefox ESR..." + sudo apt remove -y firefox-esr + else + echo "Keeping Firefox ESR." + return + fi + ;; + esac fi _enable_mozilla_repo @@ -297,19 +309,31 @@ install_firefox_mozilla() { } install_firefox_esr() { + local fchoice="${1:-}" if is_installed "firefox-esr"; then echo "Firefox ESR is already installed." return fi if command -v firefox &>/dev/null; then - if _confirm "Firefox" "Mozilla Firefox is installed.\nRemove it before installing Firefox ESR?"; then - echo "Removing Mozilla Firefox..." - sudo apt remove -y firefox - else - echo "Keeping Mozilla Firefox." - return - fi + case "$fchoice" in + both) + echo -e "${CYAN}[INFO] Keeping Mozilla Firefox (both selected).${NC}" + ;; + esr) + echo "Removing Mozilla Firefox (as selected)..." + sudo apt remove -y firefox + ;; + *) + if _confirm "Firefox" "Mozilla Firefox is installed.\nRemove it before installing Firefox ESR?"; then + echo "Removing Mozilla Firefox..." + sudo apt remove -y firefox + else + echo "Keeping Mozilla Firefox." + return + fi + ;; + esac fi _run_cmd "Firefox ESR" "sudo apt install -y firefox-esr" "Installing Firefox ESR..." diff --git a/modules/firmware.sh b/modules/firmware.sh index 7394e78..1cefa23 100644 --- a/modules/firmware.sh +++ b/modules/firmware.sh @@ -299,7 +299,8 @@ After the firmware is downloaded, reboot the system." 14 75 done if $has_broadcom_bt; then echo -e "${YELLOW}Broadcom combo card (WiFi + Bluetooth) detected.${NC}" - cat > /etc/modprobe.d/broadcom-combo.conf <<'EOF' + sudo mkdir -p /etc/modprobe.d + sudo tee /etc/modprobe.d/broadcom-combo.conf > /dev/null <<'EOF' # Broadcom combo: ensure btusb loads after wl softdep wl post: btusb EOF @@ -326,15 +327,27 @@ EOF } # ── Ensure non-free repository is enabled ── +# Token-exact check for the "non-free" component. "non-free-firmware" +# contains the substring but is a different component, so word-boundary +# matching (\b) would produce false positives. +_has_nonfree_component() { + local file="$1" + grep -qE '^[^#]*[[:space:]]non-free([[:space:]]|$)' "$file" 2>/dev/null +} + _ensure_nonfree_repo() { local nonfree_found=false - if [ -f /etc/apt/sources.list ] && grep -Eq '^[^#]*\bnon-free\b' /etc/apt/sources.list 2>/dev/null; then + if [ -f /etc/apt/sources.list ] && _has_nonfree_component /etc/apt/sources.list; then nonfree_found=true fi if ! $nonfree_found && [ -d /etc/apt/sources.list.d ]; then - if grep -qr 'Components:.*\bnon-free\b' /etc/apt/sources.list.d/*.sources 2>/dev/null; then - nonfree_found=true - fi + for f in /etc/apt/sources.list.d/*.sources /etc/apt/sources.list.d/*.list; do + [ -f "$f" ] || continue + if _has_nonfree_component "$f"; then + nonfree_found=true + break + fi + done fi if $nonfree_found; then return 0 @@ -352,21 +365,36 @@ _ensure_nonfree_repo() { fi else if [ -f /etc/apt/sources.list ]; then - sudo sed -i '/^deb / { /non-free/! s/\(main[^ ]*\)/\1 non-free non-free-firmware/ }' /etc/apt/sources.list + # Add each missing component after "main", never duplicating + sudo sed -i -E '/^deb / { /(^|[[:space:]])non-free([[:space:]]|$)/! s/(main[^[:space:]]*)/\1 non-free/ }' /etc/apt/sources.list + # non-free-firmware does not exist on Bullseye + if [ "$DEBIAN_VERSION" != "11" ]; then + sudo sed -i -E '/^deb / { /(^|[[:space:]])non-free-firmware([[:space:]]|$)/! s/(main[^[:space:]]*)/\1 non-free-firmware/ }' /etc/apt/sources.list + fi fi if [ -d /etc/apt/sources.list.d ]; then for f in /etc/apt/sources.list.d/*.sources; do [ -f "$f" ] || continue - sudo sed -i '/^Components:/ { /non-free/! s/$/ non-free non-free-firmware/ }' "$f" + sudo sed -i -E '/^Components:/ { /(^|[[:space:]])non-free([[:space:]]|$)/! s/$/ non-free/ }' "$f" + if [ "$DEBIAN_VERSION" != "11" ]; then + sudo sed -i -E '/^Components:/ { /(^|[[:space:]])non-free-firmware([[:space:]]|$)/! s/$/ non-free-firmware/ }' "$f" + fi done fi fi # Verify the component was actually added before reporting success local nonfree_ok=false - [ -f /etc/apt/sources.list ] && grep -Eq '^[^#]*\bnon-free\b' /etc/apt/sources.list 2>/dev/null && nonfree_ok=true - [ -d /etc/apt/sources.list.d ] && grep -qr 'Components:.*\bnon-free\b' /etc/apt/sources.list.d/*.sources 2>/dev/null && nonfree_ok=true - [ -d /etc/apt/sources.list.d ] && grep -qrE '^[^#]*\bnon-free\b' /etc/apt/sources.list.d/*.list 2>/dev/null && nonfree_ok=true + [ -f /etc/apt/sources.list ] && _has_nonfree_component /etc/apt/sources.list && nonfree_ok=true + if ! $nonfree_ok && [ -d /etc/apt/sources.list.d ]; then + for f in /etc/apt/sources.list.d/*.sources /etc/apt/sources.list.d/*.list; do + [ -f "$f" ] || continue + if _has_nonfree_component "$f"; then + nonfree_ok=true + break + fi + done + fi if ! $nonfree_ok; then echo -e "${RED}Failed to enable non-free repository. Check your APT sources.${NC}" return 1 diff --git a/modules/repos.sh b/modules/repos.sh index 89fbc32..472ff60 100644 --- a/modules/repos.sh +++ b/modules/repos.sh @@ -55,8 +55,8 @@ cleanup_repo_backup() { _clean_embedded_backports_classic() { local codename="$1" local file="/etc/apt/sources.list" - [ ! -f "$file" ] && return - grep -qE "^[^#]*${codename}-backports\b" "$file" 2>/dev/null || return + [ ! -f "$file" ] && return 0 + grep -qE "^[^#]*${codename}-backports\b" "$file" 2>/dev/null || return 0 if _confirm "Clean Classic Sources" "Remove backports line from ${file}?"; then sudo sed -i "/${codename}-backports/d" "$file" echo "Removed backports from ${file}" @@ -66,10 +66,10 @@ _clean_embedded_backports_classic() { _clean_embedded_backports_deb822() { local codename="$1" local file="/etc/apt/sources.list.d/debian.sources" - [ ! -f "$file" ] && return + [ ! -f "$file" ] && return 0 # Only proceed if the Suites line contains the backports token - grep -qE "^Suites:.*${codename}-backports\b" "$file" 2>/dev/null || return + grep -qE "^Suites:.*${codename}-backports\b" "$file" 2>/dev/null || return 0 if _confirm "Clean deb822 Sources" "Remove backports suite from ${file}?"; then # Remove only the backports token, leaving other suites intact @@ -113,9 +113,9 @@ _write_deb822() { # Backports: always in separate file if [ "$bp_enabled" = true ]; then - _write_deb822_backports "$codename" + _write_deb822_backports "$codename" || true else - _remove_deb822_backports "$codename" + _remove_deb822_backports "$codename" || true fi # On migration from classic, disable the old file @@ -148,12 +148,14 @@ _write_deb822_backports() { sudo mkdir -p /etc/apt/sources.list.d echo -e "$bp_content" | sudo tee "$bp_file" > /dev/null echo "Wrote ${bp_file}" + else + return 1 fi fi # If backports were formerly embedded in debian.sources, clean them grep -qE "^Suites:.*${codename}-backports\b" /etc/apt/sources.list.d/debian.sources 2>/dev/null && \ - _clean_embedded_backports_deb822 "$codename" + _clean_embedded_backports_deb822 "$codename" || true } _remove_deb822_backports() { @@ -164,6 +166,8 @@ _remove_deb822_backports() { if _confirm "Remove Backports" "Remove ${bp_file}?"; then sudo rm -f "$bp_file" echo "Removed ${bp_file}" + else + return 1 fi fi @@ -205,9 +209,9 @@ _write_classic() { # Backports: always in separate file if [ "$bp_enabled" = true ]; then - _write_classic_backports "$codename" + _write_classic_backports "$codename" || true else - _remove_classic_backports "$codename" + _remove_classic_backports "$codename" || true fi # On migration from deb822, disable the old file @@ -238,12 +242,14 @@ _write_classic_backports() { sudo mkdir -p /etc/apt/sources.list.d echo -e "$bp_content" | sudo tee "$bp_file" > /dev/null echo "Wrote ${bp_file}" + else + return 1 fi fi # If backports were formerly embedded in sources.list, clean them grep -qE "^[^#]*${codename}-backports\b" /etc/apt/sources.list 2>/dev/null && \ - _clean_embedded_backports_classic "$codename" + _clean_embedded_backports_classic "$codename" || true } _remove_classic_backports() { @@ -254,6 +260,8 @@ _remove_classic_backports() { if _confirm "Remove Backports" "Remove ${bp_file}?"; then sudo rm -f "$bp_file" echo "Removed ${bp_file}" + else + return 1 fi fi @@ -491,9 +499,17 @@ Writing debian.sources may duplicate your configuration. Continue?" 10 65; then return fi fi - _write_deb822 "$DEBIAN_CODENAME" "write" "$bp_enabled" "$bp_location" "$components" + if ! _write_deb822 "$DEBIAN_CODENAME" "write" "$bp_enabled" "$bp_location" "$components"; then + echo "No changes made." + _pause + return + fi else - _write_classic "$DEBIAN_CODENAME" "write" "$bp_enabled" "$bp_location" "$components" + if ! _write_classic "$DEBIAN_CODENAME" "write" "$bp_enabled" "$bp_location" "$components"; then + echo "No changes made." + _pause + return + fi fi echo "Updating package lists..." @@ -582,18 +598,23 @@ Answer NO to disable or remove backports if they are currently enabled." 16 70; backup_current_repos + local write_ok=true if $enable_backports; then if [ "$current_format" = "deb822" ]; then - _write_deb822_backports "$DEBIAN_CODENAME" + _write_deb822_backports "$DEBIAN_CODENAME" || write_ok=false else - _write_classic_backports "$DEBIAN_CODENAME" + _write_classic_backports "$DEBIAN_CODENAME" || write_ok=false fi + elif [ "$current_format" = "deb822" ]; then + _remove_deb822_backports "$DEBIAN_CODENAME" || write_ok=false else - if [ "$current_format" = "deb822" ]; then - _remove_deb822_backports "$DEBIAN_CODENAME" - else - _remove_classic_backports "$DEBIAN_CODENAME" - fi + _remove_classic_backports "$DEBIAN_CODENAME" || write_ok=false + fi + + if ! $write_ok; then + echo "No changes made." + _pause + return fi echo "Updating package lists..." diff --git a/modules/repos/migrate.sh b/modules/repos/migrate.sh index 3c14e73..4dd2fe2 100644 --- a/modules/repos/migrate.sh +++ b/modules/repos/migrate.sh @@ -27,27 +27,35 @@ _restore_backup() { echo -e "${GREEN}Backup restored from $_MIGRATE_BACKUP${NC}" } +# Write the target branch repository configuration. +# DEB822 is only supported on Debian 13 (apt >= 2.3.14); Debian 11/12 +# use the classic one-line format. SID has no security archive. +_write_branch_sources() { + local target="$1" + + if [ "$DEBIAN_VERSION" = "13" ]; then + _write_deb822_branch "$target" + else + _write_classic_branch "$target" + fi +} + _write_deb822_branch() { local target="$1" local main_file="/etc/apt/sources.list.d/debian.sources" local main_content="" + main_content+="Types: deb\n" + main_content+="URIs: https://deb.debian.org/debian\n" if [ "$target" = "sid" ]; then - main_content+="Types: deb\n" - main_content+="URIs: https://deb.debian.org/debian\n" main_content+="Suites: sid\n" - main_content+="Components: main contrib non-free non-free-firmware\n" - main_content+="\n" - main_content+="Types: deb\n" - main_content+="URIs: https://security.debian.org/debian-security\n" - main_content+="Suites: sid\n" - main_content+="Components: main contrib non-free non-free-firmware\n" else - # testing - main_content+="Types: deb\n" - main_content+="URIs: https://deb.debian.org/debian\n" main_content+="Suites: testing testing-updates\n" - main_content+="Components: main contrib non-free non-free-firmware\n" + fi + main_content+="Components: main contrib non-free non-free-firmware\n" + + # SID receives all updates via unstable itself; there is no sid-security suite + if [ "$target" != "sid" ]; then main_content+="\n" main_content+="Types: deb\n" main_content+="URIs: https://security.debian.org/debian-security\n" @@ -60,6 +68,22 @@ _write_deb822_branch() { echo "Wrote $main_file" } +_write_classic_branch() { + local target="$1" + local main_file="/etc/apt/sources.list" + + local main_content="" + main_content+="deb https://deb.debian.org/debian ${target} main contrib non-free non-free-firmware\n" + + # SID receives all updates via unstable itself; there is no sid-security suite + if [ "$target" != "sid" ]; then + main_content+="deb https://security.debian.org/debian-security ${target}-security main contrib non-free non-free-firmware\n" + fi + + echo -e "$main_content" | sudo tee "$main_file" > /dev/null + echo "Wrote $main_file" +} + _branch_migration() { # ── Screen 1: Risk warning ── _msg_red "WARNING: Branch Migration" \ @@ -82,7 +106,7 @@ so you can restore if things go wrong." 16 70 local plan="This operation will:\n\n" plan+=" 1. Backup current APT sources to /var/backups/\n" plan+=" 2. Remove any backports configuration\n" - plan+=" 3. Write new DEB822 sources for the target branch\n" + plan+=" 3. Write new sources for the target branch\n" plan+=" 4. Run: apt update\n" plan+=" 5. Run: apt upgrade -y\n" plan+=" 6. Run: apt full-upgrade -y\n" @@ -131,7 +155,7 @@ so you can restore if things go wrong." 16 70 [ -f /etc/apt/sources.list ] && sudo rm -f /etc/apt/sources.list # 4d. Write new sources - _write_deb822_branch "$target" + _write_branch_sources "$target" # 4e. SID guardrails: install bug alerts before upgrade if [ "$target" = "sid" ]; then diff --git a/modules/system/audio.sh b/modules/system/audio.sh index aec8f70..ab8c43f 100644 --- a/modules/system/audio.sh +++ b/modules/system/audio.sh @@ -9,7 +9,11 @@ _prefs_audio_menu() { local pw_label="PipeWire Audio Stack (Bluetooth Hi-Res)" - local pw_state; pw_state=$(_state "pipewire-audio") + # Bullseye ships the "pipewire" package; the pipewire-audio meta-package + # only exists from Bookworm onwards + local pw_pkg="pipewire-audio" + [ "$DEBIAN_VERSION" = "11" ] && pw_pkg="pipewire" + local pw_state; pw_state=$(_state "$pw_pkg") items+=( "pipewire-audio" "$pw_label" "$pw_state" ) diff --git a/modules/utils.sh b/modules/utils.sh index 62cd85d..da37af0 100644 --- a/modules/utils.sh +++ b/modules/utils.sh @@ -603,6 +603,11 @@ _checklist() { whiptail --title "$title" --ok-button "Apply" --checklist "$text" "$h" "$w" "$lh" "$@" 3>&1 1>&2 2>&3 || true } +_radiolist() { + local title="$1" text="$2" h="$3" w="$4" lh="$5"; shift 5 + whiptail --title "$title" --ok-button "Install" --radiolist "$text" "$h" "$w" "$lh" "$@" 3>&1 1>&2 2>&3 || true +} + _inputbox() { whiptail --title "$1" --ok-button "Apply" --inputbox "$2" "${3:-10}" "${4:-60}" "${5:-}" 3>&1 1>&2 2>&3 || true }