Compare commits

..

49 Commits

Author SHA1 Message Date
stornic56 f0cea296d7 fix harden hardware detection D3cold freezes
Fixes a critical startup crash on hybrid graphics laptops (e.g., Intel + Nvidia) running fresh Debian installs without proprietary drivers. 

When the secondary GPU enters D3cold power state, kernel-level I/O operations (like `lspci` or reading `/sys/block/`) hang in Uninterruptible Sleep (State D), ignoring standard kill signals and freezing the terminal. Combined with `set -euo pipefail`, these hangs made the script silently fail or enter infinite loops.

Changes:
- Timeout Wrappers: Wrapped all hardware probing commands (`lspci`, `lsblk`, `cat /sys/...`, `ip`, `iwgetid`, `pw-cli`) with `timeout 2` to force-continue if the kernel blocks.
- Pipeline Safety: Added `|| true` to `nvidia-smi` and `dpkg` fallback queries to prevent `pipefail` from killing the script when drivers are missing.
- Loop Hardening: Fixed an infinite loop in `detect_gpu()` where empty `lspci` outputs caused by timeouts would loop forever by adding `[[ -z "$line" ]] && continue`.
- TUI Fallbacks: Added default values for `${LINES:-24}` and `${COLUMNS:-80}` to prevent `set -u` from crashing the `whiptail` menu in restricted shell environments.
2026-07-26 01:15:57 -05:00
stornic56 762285f993 fix script
-fix script and nvidia-smi
2026-07-25 22:53:18 -05:00
stornic56 cd965a6678 bash gpu detection fix & nvidia-smi hardening
- Fixed critical script termination on hybrid GPU systems (Intel+Nvidia). The `set -euo pipefail` header was causing immediate exit when `read` reached EOF with exit code 1, preventing further execution despite valid hardware detection.
- Added `|| true` to the lspci processing loop in `modules/utils.sh`. This forces the while read condition to always return success (0), allowing clean loop termination and proper variable assignment for `HAS_NVIDIA=true` and `HAS_INTEL=true`.
- Hardened nvidia-smi driver version query with timeout protection. Wrapped the command call with `|| true` to capture exit code 124 from `timeout`, preventing script abortion when GPU enters D3cold state or command fails.
- Changes applied at line 204 (while read loop) and line 243 (nvidia-smi query). Both modifications ensure the main script continues execution after hardware detection completes, enabling full menu loading on dual-GPU laptops.
2026-07-25 22:03:13 -05:00
stornic56 fa81a9f1aa amd gcn legacy support & kernel overhaul
- Fixed Mesa 32-bit dependency conflicts by removing mesa-va-drivers from gaming setup; mesa-libgallium now includes VA-API for Mesa >= 25.3.3, resolving solver errors with backports (Mesa 26.x).
- Removed corectrl detection and installation from AMD tools in gpu/amd_intel.sh (retained in System Tools checklist only).
- Added zutty terminal emulator option alongside alacritty for Debian 12+ in terminals.sh.
- Overhauled Kernel menu: replaced "Backports Kernel" with "Kernel" submenu offering Stable, RT, Cloud, and Backports options; removed hardcoded Debian 11 restriction (backports hidden only on Bullseye); headers now installed automatically with all kernel variants; NVIDIA warnings added for Backports (DKMS required) and RT (driver compatibility note).
- Implemented AMD GCN legacy driver migration: is_amd_legacy_gcn() function detects GCN 1.0/1.1 GPUs via whitelist of 85 PCI IDs; offers user prompt to force amdgpu module with GRUB parameters (radeon.si_support=0, radeon.cik_support=0, amdgpu.si_support=1, amdgpu.cik_support=1); safe sed-based injection into GRUB_CMDLINE_LINUX_DEFAULT with backup and idempotency check.
- Standardized UI across 27 checklist locations: unified text to "Check [*] the packages you want installed/updated on your system."; removed SCROLL_HINT variable entirely from all code; changed OK button to "Apply" in _checklist() and _inputbox() only (not in _msg/_menu).
- Reordered gaming menu checklists: i386 moved to top position, steam/mangohud positioned immediately after for better dependency flow.
- Enhanced kernel installation prompts: all variants (Stable, RT, Cloud, Backports) now display candidate version numbers in parentheses using apt-cache policy and madison queries.
- update readme
2026-07-23 00:18:24 -05:00
stornic56 da88e9fcee zram menu refactor, mesa fix & sysinfo ui
- Refactored `install_zram` into a dedicated submenu (`zram_menu`) within `modules/zram.sh`. Split monolithic logic into three distinct operations: `_zram_view`, `_zram_create`, and `_zram_remove`. Enables status verification before installation and safe cleanup. Updated main menu entry in `debianito.sh` to call the new sub-menu.
- Resolved Trixie backports dependency conflicts between `mesa-libgallium` and `mesa-va-drivers` (Mesa 26+). Modified package lists in `_helpers.sh` and `gpu.sh` to exclude `mesa-va-drivers` during backports installs, prioritizing `mesa-vulkan-drivers` for stable Gallium integration.
- Implemented dynamic dimensioning in `_show_sysinfo()`. Calculates optimal width/height based on terminal size (`tput cols`) and content length. Truncates long lines to prevent UI overflow in small terminals, capped at 22-line height.
2026-07-21 00:58:00 -05:00
stornic56 92d0eb7467 System Refactor, GPU & Terminal Overhaul
- Bug Fixes: Rewrote `install_gaming_bullseye()` in `modules/bullseye/legacy.sh` to use a unified `_checklist` flow (matching Debian 12/13) with automatic i386 architecture detection and driver installation. Fixed greetd "Back" menu crash on Debian 11 by adding version-specific conditional logic in `desktop_display.sh`. Corrected ZRAM text overflow in `zram.sh` using `\n` line breaks for terminal wrapping. Updated Kernel Backports warning message in `debianito.sh` to clarify Bullseye limitations.

- Architecture & Repositories: Refactored Debian 11 repository configuration (`configure_repos_bullseye`) to reuse modern submenus (`_repos_enable_components`, `_branch_migration`), eliminating ~44 lines of hardcoded logic and duplicate source list management. Restricted `non-free-firmware` component availability to Debian 12+ in `modules/repos.sh`. Implemented mandatory `apt autoremove -y` and `apt autoclean` execution immediately after enabling non-free/contrib components (`_repos_enable_components`).

- UI & Menus: Added a new "Communication" menu (Option 4) between Internet and Media Players, featuring Signal, Telegram (with Trixie backports warning), and Hexchat. Moved all terminal emulators from System Tools to the "Customization System" -> Terminals submenu (`modules/extras/terminals/terminals.sh`) with version-specific logic (Alacritty 12+, Blackbox/Ptyxis 13+). Added Joplin installer in Office & Productivity for Debian 12/13 only. Integrated Synaptic into Software Centers menu alongside GNOME and Plasma stores. Unified Essential Pack UI across versions, switching between `fastfetch` (Trixie) and `neofetch` (Bookworm/Bullseye).

- Hardware Drivers: Replaced deprecated `install_gpu_drivers()` in `modules/gpu.sh` with `_install_amd_intel_stack()` and `_install_nvidia_stack()`. Implemented specific logic for Debian 13 (Trixie): Blackwell GPUs now enable the official NVIDIA CUDA repository; Maxwell/Pascal on Backports forces a stable kernel path to ensure driver compatibility. Deprecated `install_nvidia_driver()` in favor of new stack functions while preserving global state variables (`NVIDIA_DRIVER_MODE`).

- Utilities & Cleanup: Added `gdebi` and `bleachbit` to System Tools (GUI-only). Standardized menu headers across 15 files using a centralized `SCROLL_HINT` variable. Ensured VLC installation is conditional on headless status checks in Essential Pack modules.
2026-07-19 00:57:57 -05:00
stornic56 d8878b67e1 Displays managers & zram menu
- LightDM Overhaul: Rewrote `install_lightdm()` logic to verify both lightdm and lightdm-gtk-gtk-greeter-settings. Now installs the trio in a single command if missing, skipping only when fully present. Added "Enable autologin" option modifying `/etc/lightdm/lightdm.conf` with robust `sed` patterns for `SUDO_USER`. Fixed missing `debconf-set-selections` to ensure LightDM is set as default during installation.
- GDM3 Implementation: Created a new sub-menu in `modules/desktop_display.sh` allowing independent installation of GDM3, User List toggle (hide/show), Autologin configuration via `/etc/gdm3/daemon.conf`, and conditional "Force Enable Wayland on NVIDIA" for Debian 12/13 using udev symlinks.
- SDDM Integration: Added SDDM as option 3 in the Display Manager menu. Implemented automatic session detection (Wayland > X11) to populate `/etc/sddm.conf.d/autologin.conf` without user input, defaulting to plasmawayland, lxqt-wayland, or generic fallbacks.
- greetd support: Added conditional support for Debian 12 and 13 only. Implemented sub-menu with base install, recommended `tuigreet` (with backports handling for Bookworm), and other manual setup options (`gtkgreet`, `nwg-hello`, `wlgreet`) exclusive to Trixie. 
- ZRAM UX Improvements: Updated descriptions in `modules/zram.sh` to explicitly mention I/O reduction and responsiveness benefits for low-RAM systems (e.g., 4GB). Cleaned up compression algorithm labels (`lz4`, `zstd`) to remove redundancy in menu values.
- General Code Quality: Standardized all user-facing messages to English across modules. Ensured independent package installation checks prevent script abortion if one DM fails, while maintaining strict error handling for system configurations.
- update docs and readme
2026-07-16 01:02:51 -05:00
stornic56 ba72fd9f87 add GRUB timeout TUI and restructure boot menu
- Rename "Boot Rescue & Repair" to "Boot Rescue + GRUB"
- Add interactive GRUB timeout settings (Disable, 3s, 5s, Custom)
- Implement robust `_set_grub_var` and `_apply_grub_setting` helpers
- Add /etc/default/grub.d/99_script_override.cfg to bypass VM/Cloud-init overrides
- Handle Secure Boot countdown policy restrictions by using menu style
- Fix independent install check for lightdm and lightdm-gtk-greeter-settings
2026-07-12 00:13:08 -05:00
stornic56 f043c130a8 clean TUI core
- Stripped forced-scrollbar hack from _menu and _checklist
- Eliminated TUI_ANCHO_REFORZADO overflow across 9 files
- Removed redundant _inst() function globally
- Added dynamic list height calculation for checklists
- Introduced Desktop & Display menu (Option 12)
- Fixed non-free-firmware false positive in repo detection
- Restored terminal transparency for apt/vainfo output"
2026-07-11 22:05:35 -05:00
stornic56 2ebb33460c Broadcom, Utils & Headless Fixes
- Removed dead Broadcom hardware IDs (4357/4358/4360/4727) from `_is_broadcom_b43`, retaining only valid ID 4352 to eliminate false positives in the case statement.
- Added a guard in `_handle_wireless` Level 3 to verify `linux-headers` availability via `apt-cache policy`. Skips DKMS compilation if headers are missing, preventing transaction failures during installation.
- Implemented Broadcom combo card detection (WiFi + Bluetooth) by scanning `PCI_BT_DEVS`. Automatically writes `/etc/modprobe.d/broadcom-combo.conf` with `softdep wl post: btusb` and warns users about potential reboot requirements.
- Enhanced Level 2 (`firmware-b43-installer`) with post-installation validation for `/lib/firmware/b43`. Provides clear recovery instructions via `sudo dpkg-reconfigure firmware-b43-installer` if the directory is empty or missing after installation.
- Created a new helper `_msg_red()` in `utils.sh` to handle critical error messages with colored Whiptail output, replacing standard alerts for migration warnings and failures.
- Updated `repos/migrate.sh` to use `_msg_red()` for branch migration warnings and failure states, ensuring visibility of critical issues while keeping completion messages informative.
- Verified headless mode guards across 16 separate extras files (`office.sh`, `fetch.sh`, `cursors.sh`, etc.), preventing unnecessary package installations on servers without display environments.
- Completed syntax verification (`bash -n`) across all core and extra modules, ensuring no errors in the updated logic for firmware, gaming, swap, or repository management scripts.
- update readme.md
2026-07-10 19:50:36 -05:00
stornic56 a91b0caa25 critical-fixes, swap module & gaming overhaul
- Fixed script crashes from `whiptail` Cancel/Esc under `set -e`. Added `_menu()`, `_checklist()` and `_inputbox()` wrappers with `|| true` to prevent premature exits.
- Resolved startup failures by using defaults instead of relying on unset variables with `set -u`.
- Hardened sudoers management — `_validate_sudoers()` writes to temp file, validates with `visudo -cf`, only copies if successful.
- Replaced `eval` in `_run_cmd` with safer `bash -c` execution.

Swap Management Module (`modules/swap.sh`)
- New standalone module with 9 internal functions and `manage_swap()` entry point.
- Btrfs CoW detection — applies `chattr +C` before swapfile creation to prevent mkswap rejection on Btrfs.
- Dynamic swappiness configuration — reads current system value instead of hardcoding 60, pre-fills inputbox with existing setting.
- Uses `/run/lock/debianito-swap.lock` for early init compatibility (added as option 9 in main menu).

Repository & Branch Migration (`modules/repos/migrate.sh`)
- Handles branch migration from stable (11/12) to Testing or SID with 5-screen UX flow.
- Persistent backup system — creates timestamped tar.gz backups of `/etc/apt/sources.list*`, auto-restores on apt update failure.
- SID guardrails — installs `apt-listbugs` and `apt-listchanges` before upgrade to warn about critical bugs in unstable packages.
- Dynamic menu options based on Debian version (SID shows only 1-2-3 Exit, non-SID includes backports + branch migration).
- New `_components_enabled()` helper detects current contrib/non-free state.

Gaming Module Overhaul
- Unified gaming checklist — merged i386 toggle into single prompt with lazy evaluation instead of separate pre-check.
- Lazy 32-bit architecture enablement — only enables i386 when needed (steam/lutris/i386 selected) and installs graphics drivers if `need_32bit=true`.
- New `_install_nvidia_32bit()` helper with proper driver detection, version pinning, and legacy/tesla variant handling.

Heroic & OpenRGB Downloads
- Heroic — fetches latest release from GitHub API using `jq` to extract amd64.deb URL, validates with `dpkg-deb --info`.
- OpenRGB — downloads from Codeberg Gitea API with SHA256 verification when available, fallback to `dpkg-deb` validation.
- Dynamic codename detection — now detects Bookworm vs Trixie at runtime instead of hardcoded static values for .deb download URLs.

|Other Improvements
- Renumbered main menu: 8=ZRAM, 9=Swap Management, 10=Extras, 11=Boot Rescue, 12=Exit.
- Improved `detect_storage()` — excludes loop/CD-ROM devices via `lsblk -e 7,11`, explicitly skips zram, detects USB/SD via `/sys/block/$name/removable`.
- Standardized SCROLL_HINT across all files with centralized readonly variable.
2026-07-10 01:42:57 -05:00
stornic56 4ef48eac3b NTP Sync, LightDM, Wine & Rescue Updates
-  System Time (NTP)*: Overhauled `modules/utils.sh`. `check_system_time()` now warns if year < 2025 and enables NTP silently if off but valid. `sync_system_time()` conditionally installs/starts `systemd-timesyncd` and verifies synchronization via `timedatectl NTPSynchronized`.
- LightDM Configuration: Added `_configure_lightdm()` to ensure the user list is visible in the greeter. Installs `lightdm-gtk-greeter-settings` and creates an override config (`99-show-users.conf`) during startup without interrupting the flow.
- Wine Installation: Simplified Wine setup in `system.sh` and Bullseye `extras.sh`. Removed i386 multiarch logic; now installs only `wine64 fonts-wine`. Added post-install version verification to confirm successful deployment.
- Lutris Dependencies: Updated `install_lutris()` in `tools.sh` and `legacy.sh`. Now conditionally adds `wine32` only if the system supports i386 architectures, preventing dependency failures on 64-bit-only setups.
- Boot Rescue Module: Created new standalone `modules/rescue.sh`. Provides UEFI boot repair tools: Secure Boot refirm (shim + GRUB) and Initramfs regeneration for all kernels. Integrated into the main menu as a dedicated rescue section.
- update docs
2026-07-01 00:31:54 -05:00
stornic56 110d89baf8 WiFi Detection, vainfo & Java JRE
- Implemented multi-layered WiFi detection logic in `utils.sh`. Added PCI class text grep, exact `lspci -d ::0280` matching for wireless devices, Broadcom vendor ID fallback (`14e4`), and USB discovery via `lsusb`. Improved `sysinfo.sh` fallback message to guide users toward the Firmware option.
- Added `_pause()` execution after `vainfo` outputs in `gpu/_helpers.sh` (`offer_generic_tools`) and `gpu/amd_intel.sh` (`offer_amd_tools`, `offer_intel_tools`). Ensures users can review hardware info before proceeding with tool installation.
- Refactored Java selection in `extras/java.sh`. Switched from single-option `--menu` to multi-select `--checklist`, enabling simultaneous Temurin JRE installations with all versions enabled by default and iterative installation logic.
2026-06-28 11:48:48 -05:00
stornic56 8d1a05e3e0 UX Refactor: Pauses, Status & WiFi
- Implemented global `_pause()` logic across all modules (`utils`, `gaming`, `firmware`, etc.), replacing hardcoded `read -p` calls to standardize exit pauses.
- Unified MangoHud installation into a single command (`apt install -y mangohud:i386`) for gaming and legacy branches.
- Refactored `_inst()` to return `*` instead of `(installed)`, moving the function globally to `utils.sh` and removing duplicates from helpers.
- Enhanced Broadcom WiFi detection with expanded grep patterns ('network controller', 'wireless', etc.) and ID fallback (`14e4`).
- Updated `_run_cmd` to integrate the new pause logic for consistent workflow interruptions during script execution.
2026-06-27 00:21:09 -05:00
stornic56 ad0b39e159 lang & firefox-esr
- Added `_detect_lang()` helper to `modules/utils.sh` (extracts first 2 chars from `$LANG`, fallback to `en`).
- Created `_detect_lang_pkg()` utility for LibreOffice localization: handles regional variants (`es-ar` → `es`) and skips English packages.
- Implemented `_check_network()` with cascade methods (ping→wget→curl) in `modules/utils.sh`. Warning-only pre-flight check after `check_sudo` in `debianito.sh`, compatible with `set -euo pipefail`.
- Fixed system time validation: changed `2026` threshold to `2025` in `check_system_time()` (replaced self-referential `$(date +%Y)` which always evaluated false).
- Refactored LibreOffice language selection in `office.sh`: replaced 10 lines of inline logic with `_detect_lang_pkg "libreoffice-l10n"`.
- Overhauled Firefox ESR integration in `internet.sh`: added state detection, mutual exclusion guard (both Firefox variants selected → warning), new `install_firefox_esr()` function with language support.
- Updated `install_firefox_mozilla()`: added Debian version guard (`<12`), replaced `_run_install` with `_run_cmd`.
- Standardized header in internet menu and improved whiptail UX for Firefox options.
2026-06-25 22:06:15 -05:00
stornic56 3972cc4811 Bluetooth Fix & GPU ID Expansion
- Bluetooth Package Cleanup: Replaced deprecated `bluez-utils` with `bluez-tools` in `modules/bluetooth.sh` and `modules/firmware.sh`. Ensures compatibility across all Debian versions.
- Maxwell GPU Detection: Added 4 ID blocks (`0x1340 – 0x17FF`, 480 IDs total). Captures missing cards: 980M/970M, Quadro M laptop, GTX 960/950, Quadro M2000, TITAN X, GTX 980 Ti, Quadro M6000, and Tesla M40.
- Pascal GPU Detection: Expanded 5 ID blocks (`0x15F0 – 0x1D7F`, 528 IDs total). Added support for TITAN Xp, GTX 1080 Ti, and Quadro P-series. Unified GP106/107 ranges and bounded end at `0x1D7F` to prevent Turing conflicts.
- Validation: Passed `bash -n` checks on all modified files. Confirmed zero ID collisions between Maxwell, Fermi, Kepler, and Pascal architectures.
2026-06-23 23:18:37 -05:00
stornic56 2272306313 NVIDIA Fermi/Kepler Fixes & Utils
- Refined NVIDIA GPU detection in `_helpers.sh`. Corrected Kepler GK208 range (`0x1280–0x12BF`) and added refined Fermi ID ranges to eliminate device collisions across architectures.
- Updated `gpu.sh` driver logic for Debian Bookworm (explicit Fermi veto) and Trixie (unified Kepler/Fermi check). Prevents legacy driver conflicts and ensures correct selection per version.
- Cleaned `bullseye/legacy.sh`. Removed outdated Fermi checks with collisions; now inherits unified validation from `_helpers.sh` to maintain Debian 11 compatibility.
- Fixed Wi-Fi interface regex in `utils.sh`. Added `wlan*` pattern to catch ath9k interfaces (`wlan0`) alongside standard wl/wlp/wlo identifiers.
- Validated syntax across all files with `bash -n`. Confirmed zero collision between Fermi and Kepler device IDs, ensuring stable detection on Debian 11/12/
2026-06-22 18:39:16 -05:00
stornic56 bfd3144c7e Update README.md 2026-06-20 22:58:34 -05:00
stornic56 2708d6dcb5 add retroarch, nvidia - java - internet refactor
- NVIDIA CUDA extrepo refactor in `modules/gpu/nvidia.sh`: removed unnecessary `i386_active` lines, updated warning to reference `v590 (unified metapackage)`, simplified installation from 18+10 versioned packages → `nvidia-driver-pinning-590 nvidia-driver firmware-nvidia-gsp`, eliminated `apt-mark hold` since pinning packages now handle it. DKMS verification and `NVIDIA_DRIVER_MODE="cuda-repo"` preserved.
- CUDA repo case fix in `modules/gaming.sh`: replaced silent bypass with detection of `nvidia-driver-libs:i386` v590 installation; if missing, prompts user confirmation before installing via active CUDA repo + pinning.
- Palemoon internet module overhaul (`modules/extras/internet/internet.sh`): removed deprecated `_enable_palemoon_repo()`, created new `install_palemoon()` with AVX2→AVX→SSE2 CPU detection from `/proc/cpuinfo`, proper `extrepo enable` call, and package installation.
- ProtonVPN module rewrite (`modules/extras/internet/internet.sh`): removed broken `_enable_protonvpn_repo()` that failed due to missing suite; created new `install_protonvpn()` using `stable` suite + `proton-vpn-gtk-app` package with proper validation.
- Java/Minecraft rename across `modules/gaming.sh` and `modules/extras/java.sh`: renamed `_install_gaming_java()` → `install_minecraft_java()`, updated menu title from "Java Runtimes for Gaming" to "Java Runtimes for Minecraft", changed whiptail tag from `"java-jre"` to `"java"`.
- RetroArch + 4 classic cores (`modules/gaming/tools.sh` and `.sh`): added RetroArch entry to gaming menu, new case handler in `gaming.sh`, updated installation command to include `libretro-mgba libretro-snes9x libretro-nestopia libretro-gambatte`, enhanced notice with emojis, core enumeration, DFSG warning, and wiki link.
- OnlyOffice server status (`modules/extras/office/office.sh`): added fallback message for slow or down OnlyOffice servers to improve user experience during installation.
- Full syntax validation: all modified files pass `bash -n` without errors; no residual references to old variables (`i386_active`, `590.48.01`) or functions remain.
- Documentation about Debian and the script is added to supplement important information.
- update README.md
2026-06-20 21:54:59 -05:00
stornic56 3e96906d6e firmware & wireless refactor + extrepo Migration
- Created standalone modules/bluetooth.sh  for `_install_bluetooth_stack()` with desktop-aware frontend selection (bluedevil/KDE, blueman/XFCE/other). Extracted from `firmware.sh`, reducing it from 438 to 369 lines.

- Added `detect_desktop_environment()` and `detect_audio_server()` functions in `utils.sh` with new globals `DESKTOP_ENV=""` and `AUDIO_SERVER=""`.

- New `found_active_wifi` flag detects Qualcomm Atheros AR9485 PCI chips without loaded firmware, displaying `(no driver — install firmware)` instead of silently disappearing. Auto-installs `firmware-atheros + firmware-linux-nonfree` after Firmware & Wireless Setup.

- New `is_nvidia_blackwell()` detects GPUs in ranges `0x2900–0x29BF` and `0x2B80–0x31FF`. Added `_enable_cuda_repo()` helper for extrepo-based CUDA installation with APT pinning. DKMS verification blocks added to all `_install_nvidia_*` functions.

- Standardized repository enablement across 7 modules using `_enable_*_repo()` helpers:
  - `internet.sh`: Firefox/Floorp/LibreWolf, Pale Moon, Tailscale, Mullvad VPN/Browser, ProtonVPN
  - `jellyfin.sh`, `programming.sh` (VSCodium), `java.sh` (Temurin)
  - New `extras/office/office.sh`: OnlyOffice + LibreOffice with language detection

- The main script was reduced from 233 to 137 lines. Created new `modules/sysinfo.sh` (97 lines) for `_show_sysinfo()`. Source chain: `utils.sh → sysinfo.sh → sudo_config.sh → repos → firmware.sh → bluetooth.sh → gpu.sh → kernel.sh → gaming.sh → extras.sh → zram.sh → java.sh`.

- Renamed menu to `"Firmware, Wireless & Bluetooth"`. Translated NTP dialog (English), basic programs install message, and NVIDIA 32-bit legacy messages.
2026-06-19 22:32:50 -05:00
stornic56 002a888b46 Reduce user prompts: firmware & graphics updates
- Rewrote `modules/firmware.sh` with unified detection and installation flow, reducing user interactions from multiple prompts to single confirmation across all hardware types.
- Enhanced `_detect_all_network_devices()` to scan full PCI device lists without truncation (`head -n1` removed) and filter USB devices by keyword patterns (`wireless`, `wifi`, `802.11`, `bluetooth`, `wlan`) to eliminate false positives from card readers/Realtek audio devices.
- Implemented Broadcom 3-tier firmware strategy: Tier 1 (`brcmsmac`, 8 device IDs) auto-installs without extra confirmation; Tier 2 (`b43`/`b43legacy`, ranges 0x4301–0x4331) requires contrib package warnings; Tier 3 (`broadcom-sta-dkms`) needs DKMS compilation approval with explicit precedence handling for overlapping device ID ranges.
- Streamlined `modules/gpu.sh` flow from 169 to 154 lines, cutting user clicks from 4 (intro → proceed → plan → install) to 2 (plan → install), removing redundant intro message before plan display while preserving Intel/AMD firmware auto-installation and Mesa prompting logic.
- Added Bullseye compatibility hook in `modules/bullseye/extras.sh` (`type _handle_wireless &>/dev/null && _handle_wireless`) to ensure Broadcom wireless handling works correctly in Debian 11 environment with lazy detection fallback for empty PCI network devices.
- Fixed whiptail UI spacing issues in message paragraphs for improved readability and standardized dialog geometry across firmware/graphics dialogs.
2026-06-17 15:57:07 -05:00
stornic56 3a652c28e6 system overhaul and bug fixeds
- Replaced the fragile upstream script installer with native Deb822 repository injection and automated GPG keyring management in `jellyfin.sh`. Eliminated external dependencies, checksums, and interactive flags for guaranteed non-interactive support across Bullseye/Bookworm/Trixie.
- Upgraded the NVMe module (`modules/extras/system/system.sh`) to perform multi-drive scanning with dynamic `lsblk` parsing. Replaced single-disk detection logic with a `while read` loop to capture all devices, displaying formatted SMART health diagnostics (Critical Warning, Temperature, Percentage Used).
- Overhauled the 'System Information' TUI into a multi-device dashboard featuring standalone GPU/Driver hierarchies and dynamic storage mapping. Added new globals (`DISPLAY_SERVER`, `STORAGE_SUMMARY`) and comprehensive arrays for Network/Wi-Fi interface indexing in `modules/utils.sh`.
- Redesigned the graphics stack layout to fix multi-GPU text clipping and unified Mesa version prompting logic. Integrated post-install package telemetry to display live versions in the completion screen, separating planning visualization from installation decision points in `modules/gpu.sh`.
- Fixed non-free-firmware conceptual documentation bugs and expanded Backports descriptions with hardware-focused educational text. Added non-free repository safeguards for Trixie/Bullseye compatibility under "Firmware & Wireless Drivers".
- Refactored repository logic in modules/repos.sh to eliminate dead code (redundant default_text variables) and strict-hardcoded layout behaviors, standardizing dialog geometry to a clean matrix.
- Updated README.md
2026-06-16 19:59:15 -05:00
stornic56 63ef2e7343 dual-gpu fix & nvme
- Replaced the restrictive head -n1 hardware parsing with a dynamic while read loop. The script now fully detects and registers multiple coexisting GPUs (Intel+Nvidia / AMD+Nvidia).
- Rewrote install_gpu_drivers() in modules/gpu.sh from a mutually exclusive case block to independent, sequential if conditions. Laptops now install both integrated firmware (Intel/AMD with non-free VA-API acceleration) and dedicated graphics stacks (Nvidia driver + 32-bit libs for Steam) seamlessly in a single pass.
- Improved Nvidia Kepler architecture guards under Debian 13 (Trixie). The script now safely skips missing legacy Nvidia packages without aborting or crashing the remaining Intel/AMD configurations.
- Added nvme-cli utility installation across all Debian versions. For Bookworm and Trixie, it features dynamic hardware validation via lsblk transport filtering and interactive, real-time SMART log viewing with an execution screen-pause.
- Created a brand new standalone module (modules/extras/dev/jellyfin.sh) to cleanly inject Jellyfin Media Server across Debian 11, 12, and 13 using its official setup script, protected by strict SHA256 checksum validations and guaranteed error-cleanup.
- Added OpenRGB to the Gaming menu for Debian 12 and 13. Implemented secure curl downloads with an emulated Chrome User-Agent to bypass Codeberg bot blocks, complete with automatic i2c-dev module configuration, user groups provisioning, and udev permission rules execution.
- Standardized menu headers across 15 separate files using a centralized readonly SCROLL_HINT=" [↑↓]" variable, replacing messy hardcoded strings with clean, uniform Whiptail instructions.
- update README.md
2026-06-16 16:20:30 -05:00
stornic56 23301f98b3 fix backports and system modules
- Refactored `is_backports_enabled()` to dynamically detect backports using `DEBIAN_CODENAME` instead of hardcoded strings, improving script portability.
- Added support for standalone classic backports files (`/etc/apt/sources.list.d/debian-backports.list`) and deb822 format (`*.sources`) to ensure consistent detection across different repository configurations.
- Resolved Mesa and Kernel backports detection bugs, ensuring correct dialogue prompts and preventing false error messages during installation.
- Implemented smart rounding for zram memory allocation to the nearest Gigabyte for optimized performance.
- Enhanced Whiptail manual input validation for zram to block empty, non-numeric, or zero values.
- Introduced a dedicated `software_centers.sh` module for the modular and clean installation of GNOME Software and Plasma Discover.
- Added smart desktop environment detection (GTK/QT) to automatically suggest and install the optimal software center.
- Implemented a two-phase installation flow for software centers (Base Store -> Optional Flatpak support) with static consistency warnings based on the detected desktop environment.
- update README.md
2026-06-14 22:01:56 -05:00
stornic56 872f92d6b4 add Java JDK/JRE selection and patches
- Created dedicated `extras/java.sh` module for modular Temurin/Adoptium repository handling and isolated GPG key management.
- Implemented selective JRE/JDK installation paths: Gaming (Minecraft) vs Development environments with version control selectors.
- Enhanced user group membership logic (`_ensure_sudo_group`) to auto-configure administrative privileges on fresh installs.
- Introduced maintenance-friendly passwordless sudo for `apt`, `systemctl`, and shutdown commands via `/etc/sudoers.d/`.
- Added home directory ownership repair utility to fix root-owned files in user directories after improper `sudo` usage.
- Enabled visual password feedback (`pwfeedback`) toggle for enhanced terminal usability during authentication prompts.
- Refactored Debian 13/Trixie repository sourcing logic to ensure idempotency and prevent backports/duplicate sources conflicts.
- Updated Bullseye legacy modules (extras.sh, repos.sh) to exclude deprecated packages (`hx`, `vscodium`) and enforce Firefox ESR defaults.
- Cleaned repository configurations by removing obsolete `bullseye-updates` source entries to prevent dependency conflicts.
- Implemented NVTOP conditional detection logic ensuring display only on active Nvidia drivers or supported architectures (Trixie/Bookworm).
- Corrected `/modules/bullseye/extras.sh` menu mappings (Programming, Dev Tools) and verified package lists excluding incompatible tools (`hx`, `vscodium`).
- Integrated ClamAV security module with daemon state management and background scan execution flows in all Security menus.
- update README.md
2026-06-13 19:21:55 -05:00
stornic56 656f045163 add Debian 11 support
- Created dedicated modules/bullseye/` folder for legacy isolation (Debian 11 support).
- Implemented 4-block inclusive PCI range detection for Nvidia Kepler/Fermi to prevent black screens.
- Centralized system time verification checking against year 2026 before APT execution.
- Cleaned and purged modern package lists for the Bullseye extra software module.
- Updated main menu with conditional sourcing and fixed Option 7 (Gaming Lite) for Bullseye.
- Updated README.md with explicit TUI navigation instructions and full repository directory tree mapping.
2026-06-11 17:22:11 -05:00
stornic56 ba72875960 Update README.md 2026-06-09 23:44:52 -05:00
stornic56 72032f2916 Update README.md 2026-06-09 23:04:15 -05:00
stornic56 af8fac4ac4 Add files via upload 2026-06-09 23:03:30 -05:00
stornic56 3d1738ddc0 Add files via upload 2026-06-03 20:15:52 -05:00
stornic56 eec16d0c83 Add files via upload
update
2026-05-23 22:50:36 -05:00
stornic56 43ae3e55a4 Update README.md 2026-05-20 12:28:11 -05:00
stornic56 194f021ad0 Changes
General changes to the script order
New programs
Changes in flow
Name revisions
2026-05-20 12:26:06 -05:00
stornic56 18d088f6c7 Add files via upload 2026-05-17 20:19:39 -05:00
stornic56 771558a287 Update README.md 2026-05-15 16:22:26 -05:00
stornic56 d6410ee6aa Update README.md 2026-05-15 16:19:35 -05:00
stornic56 bf6bcdf65e Create LICENSE 2026-05-15 16:15:20 -05:00
stornic56 4195d6f444 Delete LICENSE 2026-05-15 16:10:24 -05:00
stornic56 29f8d56548 Add files via upload 2026-05-14 21:52:17 -05:00
stornic56 e3978c5a4f Add files via upload
update files
2026-05-11 17:11:11 -05:00
stornic56 f8b77e7d9b Update README.md 2026-05-07 11:45:10 -05:00
stornic56 f5c431f063 Add files via upload 2026-05-07 01:31:33 -05:00
stornic56 b2e83321e2 Add files via upload 2026-05-07 01:31:06 -05:00
stornic56 364a956488 Update README.md 2026-05-07 01:30:42 -05:00
stornic56 c2e26b7f32 Update README.md 2026-05-05 20:56:10 -05:00
stornic56 17c7514896 Update README.md 2026-05-05 20:54:57 -05:00
stornic56 df68160728 Create README.md 2026-05-05 20:53:34 -05:00
stornic56 3178cf98ab Add files via upload 2026-05-05 20:27:02 -05:00
stornic56 887ec4de25 Initial commit 2026-05-05 20:18:12 -05:00
92 changed files with 3012 additions and 7465 deletions
-18
View File
@@ -1,18 +0,0 @@
name: CI
on: [push, pull_request]
jobs:
lint-and-test:
runs-on: ubuntu-latest
container: debian:trixie
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install bats + shellcheck
run: apt-get update && apt-get install -y bats shellcheck
- name: Shellcheck (entry point + all modules)
shell: bash
run: |
shopt -s globstar
shellcheck --severity=warning debianito.sh modules/**/*.sh
- name: BATS test suite
run: bats tests/
-1
View File
@@ -1 +0,0 @@
tests/.tmp/
+1 -1
View File
@@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
Debianito Copyright (C) 2026 stornic56
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
+165 -118
View File
@@ -2,7 +2,7 @@
<div align="center">
Debianito is a post-installation automation script for Debian 11 (Bullseye), Debian 12 (Bookworm) and Debian 13 (Trixie). An interactive menu guides system configuration, driver installation (including NVIDIA drivers), repository setup with backports support and gaming tools.
Debianito is a user-friendly post-installation automation script for Debian 11 (Bullseye), Debian 12 (Bookworm) and Debian 13 (Trixie). It streamlines system configuration, driver installation (including NVIDIA drivers), repository setup with backports support, gaming tools integration, and more with an interactive menu-driven interface.
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-red.svg?style=for-the-badge&logo=gnu&logoColor=white)](https://github.com/stornic56/debianito-post-install/blob/main/LICENSE)
![Script](media/gift/script.gif)
@@ -36,116 +36,199 @@ chmod +x debianito.sh && ./debianito.sh
---
## Command-Line Usage
Running `./debianito.sh` with no arguments starts the interactive menu. Three flags change that behavior:
| Flag | Effect |
| ---- | ------ |
| `--yes` | Auto-confirms prompts (unattended mode). Menus still display and still require interaction. |
| `--allow-destructive` | Allows destructive operations, such as branch migration, without the extra safety gate. |
| `--help`, `-h` | Show usage and exit. |
`--yes` alone still blocks branch migration, which is the one operation that can leave the system unbootable. When that happens the script stops and prints `Branch migration is destructive. Re-run with --allow-destructive, or drop --yes to confirm interactively.` to stderr. Combine both flags to run everything unattended:
```bash
./debianito.sh --yes --allow-destructive
```
An unknown option exits with status 2. For boot profiling, set `DEBIANITO_PROFILE=1` to stamp each boot step to stderr with a timestamp:
```bash
DEBIANITO_PROFILE=1 ./debianito.sh
```
---
## Usage
## Usage
After running the script:
1. **Select Option:** Use arrow keys or type 1-14.
1. **Select Option:** Use arrow keys or type 1-10.
2. **Navigation:** Use Arrow Keys (Up↑/Down↓) to move between list options and ENTER key to confirm selection.
3. **Confirm Actions:** Installation prompts use whiptail for TUI confirmations. Before a batch of packages is applied, the script shows what it is about to install (up to 15 package names plus a count of the rest) and asks you to proceed.
3. **Confirm Actions:** Installation prompts use whiptail for TUI confirmations.
4. **Review System Info:** Header displays detected Debian version and hardware summary before each action.
5. **Repeat as Needed:** Return to main menu at any time or exit when done.
| Option | Description | What it does |
| -------- | ------------- | -------------- |
|--------|-------------|--------------|
| **1** | [System Info](/docs/system_info.md) | Show detected OS, CPU, RAM, GPU and hardware details |
| **2** | [User Privileges & Feedback](/docs/user_priv_feed.md) | Configure sudo group membership, enable passwordless sudo for frequent tasks, repair home directory ownership issues, and toggle visual password feedback (asterisks) in terminal |
| **3** | [System Preferences](/docs/system_prefs.md) | Configure date/time & timezone, language/locales & keyboard layout, and audio stack (PipeWire, ALSA, PulseAudio) |
| **4** | [Configure Repositories](/docs/repos_config.md) | Setup official repos with non-free/contrib options, optional Backports support and Deb822/classic format injection |
| **5** | [Firmware, Wireless & Bluetooth](/docs/firmware.md) | Install essential firmware for GPUs and wireless; configure Bluetooth stack (bluez, bluedevil/blueman) |
| **6** | [Graphics Drivers & Mesa Stack](/docs/gpu.md) | Configure AMD/Intel/NVIDIA drivers and Mesa graphics stack + monitoring tools |
| **7** | [Kernel](/docs/kernel.md) | Install kernel variants: Stable, RT, Cloud, or Backports (Debian 13) |
| **8** | [Gaming Setup](/docs/gaming.md) | Steam, Heroic Games Launcher, [RetroArch](/docs/retroarch.md), GameMode, MangoHud, OpenRGB, Java JRE (Temurin 8/17/21/25) |
| **9** | [ZRAM](/docs/zram.md) | Configure compressed RAM for memory optimization |
| **10** | [Swap Management](/docs/swap.md) | Manage swap file or partition size and enable/disable swap space for system stability |
| **11** | Install Programs and Software | Browse and install packages by category (Development, Themes, System Tools, etc.) using APT |
| **12** | [Boot Rescue + GRUB](/docs/boot.md) | Config and fix GRUB bootloader issues, chroot repair, or restore system boot configuration |
| **13** | [Desktop & Display](/docs/desktops_display.md) | Install and configure desktops (XFCE, LXDE, GNOME, KDE Plasma) and display managers (LightDM, GDM3, SDDM, greetd) |
| **14** | Exit | Return to terminal |
| **3** | [Configure Repositories](/docs/repos_config.md) | Setup official repos with non-free/contrib options, optional Backports support and Deb822/classic format injection.|
| **4** | [Firmware, Wireless & Bluetooth](/docs/firmware.md) | Install essential firmware for GPUs and wireless|
| **5** | [Graphics Drivers & Mesa Stack](/docs/gpu.md) | Configure AMD/Intel/NVIDIA drivers and Mesa graphics stack + monitoring tools |
| **6** | [Backports Kernel](/docs/kernel.md) | Install latest kernel from Debian backports|
| **7** | [Gaming Setup ](/docs/gaming.md)| Steam, Heroic Games Launcher, [RetroArch](/docs/retroarch.md) GameMode, MangoHud, OpenRGB, Java JRE (Temurin 8/17/21) |
| **8** | [ZRAM](/docs/zram.md)| Configure compressed RAM for memory optimization|
| **9** | [Swap Management](/docs/swap.md)| Manage swap file or partition size and enable/disable swap space for system stability|
| **10** | Install Programs and Software | Browse and install packages by category (Development, Themes, System Tools, etc.) using APT |
| **11** | [Boot Rescue + GRUB](/docs/boot.md)| Config and fix GRUB bootloader issues, chroot repair, or restore system boot configuration|
| **12** | [Desktop & Display](/docs/desktops_display.md)| Install and configure desktops and display managers|
| **13** | Exit | Return to terminal |
### Install Programs and Software (Option 11)
### Install Programs and Software (Option 10)
The submenu offers these categories:
The submenu offers the next categories:
| Option | Category Title | Description |
| -------- | ------------------------------- | ------------- |
| **0** | Essential Pack | Quick install of common tools (compression, system info, VLC, MS fonts) |
|--------|-------------------------------|-------------|
| **0** | Essential Pack | Quick install of common tools (compression, system info, VLC, MS fonts)|
| **1** | Customization System | Desktop themes, icon themes, cursor themes, and fonts |
| **2** | Download & Network | Downloaders (aria2, ytdlp, FileZilla) + Torrent clients (qBittorrent, Deluge, Transmission) |
| **3** | Internet (Browsers, Email Clients, VPN) | Web browsers (Firefox, LibreWolf, Floorp, Chromium, Tor), email client (Thunderbird), and VPN tools (RiseUp, Proton, Mullvad) |
| **4** | Communication | Signal, Telegram, HexChat: chat and messaging clients (GUI only) |
| **5** | Media Players | Multimedia playback with VLC media player and MPV for advanced video/audio support |
| **6** | Multimedia & Design | Image editing (GIMP), video editing (Kdenlive, HandBrake), 3D modeling (Blender), audio recording (Audacity), and graphics design (Inkscape) |
| **7** | Code Editors & IDEs | vim, vim-gtk3, Neovim, Helix, nano, Emacs, Kate, Mousepad, Gedit, Geany, GNOME Text Editor, and VSCodium (VS Code open-source) |
| **8** | Servers & Dev Tools | Web servers (Nginx/Apache), databases (PostgreSQL/MariaDB), Java Development Kit (Temurin 17/21/25 JDK), Docker, Python, SSH tools, Jellyfin Server and essential utilities |
| **9** | Security & Networking | Wireshark, tcpdump, Zenmap, ClamAV, UFW, fail2ban |
| **10** | Software Center & Flatpak | GNOME Software / KDE Discover and Flatpak support |
| **11** | Office & Productivity | Office suites (LibreOffice), document tools, productivity apps |
| **12** | System Tools | htop/btop, ncdu, Timeshift, tmux/screen, nvme-cli, extension repository manager and qemu/virtmanager |
| **13** | Fetch / System Info | fastfetch/neofetch, hyfetch, Linux logo and screenfetch |
| **14** | Back to Main Menu | Return directly to the main Debianito menu (exit submenu) |
| **3** | Internet (Browsers, Email Clients, VPN) | Web browsers (Firefox, LibreWolf, Floorp, Chromium, Tor), email client (Thunderbird), and VPN tools (RiseUp, Proton, Mullvad)|
| **4** | Media Players | Multimedia playback with VLC media player and MPV for advanced video/audio support |
| **5** | Multimedia & Design | image editing (GIMP), video editing (Kdenlive, HandBrake), 3D modeling (Blender), audio recording (Audacity), and graphics design (Inkscape) |
| **6** | Code Editors & IDEs | vim, vim-gtk3, Neovim, Helix, nano, Emacs, Kate, Mousepad, Gedit, Geany, GNOME Text Editor, and VSCodium (VS Code open-source) |
| **7** | Servers & Dev Tools | Web servers (Nginx/Apache), databases (PostgreSQL/MariaDB), Java Development Kit (Temurin 17/21/25 JDK), Docker, Python, SSH tools, Jellyfin Server and essential utilities |
| **8** | Security & Networking | Wireshark, tcpdump, Zenmap, ClamAV, UFW, fail2ban |
| **9** | Software Centers | Choose a software store to install. |
| **10** | Office & Productivity | Choose a software store to install. |
| **12** | System Tools | htop/btop, ncdu, Timeshift, tmux/screen, nvme-cli, Flatpak support, extension repository manager and qemu/virtmanager |
| **12** | Fetch / System Info | fastfetch/neofetch, hyfetch, Linux logo and screenfetch |
| **13** | Back to Main Menu | Return directly to the main Debianito menu (exit submenu) |
---
## Quick Start Guide
## Quick Start — Recommended Setup Order
For a full setup in ~15-20 minutes, refer to the [Quick Start Visual Guide](/docs/quickstart.md). It provides:
This section shows the recommended order to configure a fresh Debian installation for maximum performance and usability. Follow these steps in order — each step builds on the previous one.
- Step-by-step recommended order for running Debianito options (Steps 1–13)
- What each option does and when to enable it
- Screenshots of key dialogs (whiptail menus, confirmations, hardware detection)
- Troubleshooting tips for common issues (WiFi after firmware install, NVIDIA + Wayland, GRUB boot menu hidden, etc.)
---
### Step 1 — Know Your System
**Menu:** `1 System Information`
Check your hardware before installing anything. The script detects your CPU, RAM, GPU vendor and generation, network adapters, and display server. Write down what you see — especially the GPU line (Intel/AMD/NVIDIA) and any WiFi chipset. You'll need this info for steps 4 and 5.
> 💡 If no GPU is detected, you're likely on a VM or headless server — skip step 5.
---
### Step 2 — Enable Password Feedback
**Menu:** `2 User Privileges & Feedback` → select option for Sudo Password Feedback
Enable asterisks (`****`) when typing your sudo password. This prevents typos during the many installation steps that follow. Toggle it on now, disable it later if you prefer hidden input.
---
### Step 3 — Configure Repositories
**Menu:** `3 Configure Repositories`
This is the foundation — all subsequent installations depend on correct repos.
1. **Enable Contrib & Non-Free Components** (required for firmware, drivers, Steam)
2. **Setup/Update Backports repositories** (recommended — gives you newer kernels, Mesa drivers, and firmware for modern hardware)
> ⚠️ Without non-free enabled, steps 4 and 5 will fail. Do not skip this.
---
### Step 4 — Install Firmware & Wireless Drivers
**Menu:** `4 Firmware, Wireless & Bluetooth`
The script scans ALL network hardware (PCI + USB) and installs exactly the firmware your system needs. Accept the plan it shows — it's based on your actual hardware.
This step also sets up Bluetooth if your system has Bluetooth hardware (KDE gets bluedevil, XFCE gets blueman, GNOME uses built-in).
> ⚠️ You need internet for this step. If WiFi isn't working after firmware install, reboot first — the driver needs a fresh load.
---
### Step 5 — Install Graphics Drivers
**Menu:** `5 Graphics Drivers & Mesa Stack`
The script auto-detects your GPU and installs the correct driver:
- **NVIDIA:** The script chooses the best path automatically (CUDA repo, backports, or stable) based on your GPU generation and kernel. Just confirm the plan.
- **AMD/Intel:** Installs firmware + Mesa stack + Vulkan drivers. Also installs 32-bit libraries if gaming is planned.
- **Hybrid (laptop):** Installs Intel/AMD firmware first, then NVIDIA driver.
> 💡 After installation, reboot before testing. NVIDIA drivers need a fresh kernel load to activate.
---
### Step 6 — Kernel Configuration
**Menu:** `6 Kernel`
If you enabled backports in step 3, you can install a newer kernel (e.g., 7.x) for better hardware support. The script offers stable vs backports:
| Choice | Best for |
|--------|----------|
| Stable (default) | Maximum stability, older hardware |
| Backports | Modern CPUs (Intel Arrow Lake, AMD Zen 5), new GPUs, better power management |
> ⚠️ If you installed NVIDIA drivers in step 5, the backports kernel may require DKMS recompilation. The script warns about this — it's usually fine.
---
### Step 7 — Gaming Setup (Optional)
**Menu:** `7 Gaming Setup`
If you plan to game on Debian:
1. **Enable i386 architecture** when prompted — this is REQUIRED for Steam/Proton and 32-bit game libraries
2. Select your launchers: Steam, Heroic Games Launcher, Lutris
3. Select performance tools: GameMode, MangoHud, GOverlay
4. Select RetroArch if you want retro emulation (pre-installed with GBA, SNES, NES, GB cores)
> ⚠️ Steam requires the contrib repository (step 3) and i386 architecture. The script warns if either is missing.
---
### Step 8 — ZRAM Compressed Swap (Optional)
**Menu:** `8 ZRAM`
ZRAM compresses memory pages in RAM instead of writing to disk. This is especially useful on systems with limited RAM (8GB or less). The script automatically sets ZRAM to ~50% of your physical RAM with priority 100 (used before disk swap).
Choose **LZ4** for gaming (fastest compression) or **ZSTD** for general use (better compression ratio).
> 💡 If you also use disk-based swap (Option 9), ZRAM is used first. Disk swap only kicks in after ZRAM fills up.
---
### Step 9 — Essential Software
**Menu:** `10 Install Programs and Software` → option `0 Essential Pack`
One-click install of the most common tools:
| Package | What you get |
|---------|--------------|
| htop, inxi | System monitoring |
| neofetch / fastfetch | System info display |
| vlc | Media player |
| ufw | Firewall|
| zip, unrar, p7zip | Compression tools |
After this, browse the other categories (browsers, editors, dev tools) as needed.
---
### Complete Flow Summary
1. System Information ← Know your hardware
2. User Privileges & Feedback ← Enable password asterisks
3. Configure Repositories ← Enable contrib + non-free + backports
4. Firmware, Wireless & Bluetooth ← Install WiFi/BT firmware
5. Graphics Drivers & Mesa Stack ← Install GPU drivers
6. Kernel ← Optional: newer kernel (Backports)
7. Gaming Setup ← Optional: Steam + tools
8. ZRAM ← Optional: compressed swap
9. Install Programs → 0 Essential ← Common tools
**Estimated time:** 15-20 minutes for a full setup (steps 1-5 + 9). Steps 6-8 are optional and depend on your use case.
**Tip:** If you're unsure where to start, follow Steps 1–6 from the Quick Start guide. They cover the essentials: system info, permissions, repositories, firmware, and graphics drivers.
---
## File Structure
| Directory/File | Description |
| ---------------- | ------------- |
|----------------|-------------|
| `debianito.sh` | Main entry point; handles menu navigation and system detection. |
| `docs/` | Documentation directory containing Markdown files for each module. |
| `modules/` | Core modular scripts organized by category: repos, gpu, gaming, kernel, firmware, zram, etc. |
| `modules/bullseye/` | Legacy Debian 11 (Bullseye) specific modules: `extras.sh`, `legacy.sh`, `repos.sh`. |
| `modules/system/` | System preferences: `system_prefs.sh` (timezone, locale, keyboard) and `audio.sh` (PipeWire, ALSA). |
| `modules/extras/` | Software installer sub-modules split by category (themes, downloaders, internet, dev tools, etc.). |
| `modules/gaming/` | Gaming launcher and optimization scripts: Steam, Heroic, Lutris, performance tools. |
| `modules/gpu/` | GPU driver installation scripts for AMD and NVIDIA with architecture detection. |
| `modules/desktops/` | Desktop environment installers: `desktop_xfce.sh`, `desktop_lxde.sh`, `desktop_gnome.sh`, `desktop_kde.sh`. |
| `modules/hw_detect.sh` | Shared hardware detection (PCI/USB network, WiFi and Bluetooth devices); sourced early by `debianito.sh` and consumed by firmware and Bluetooth modules. |
| `modules/repos/` | Repository management scripts: migration tool (`migrate.sh`) and format detection (`repo_detect.sh`). |
| `tests/` | Offline BATS suite: `utils.bats`, `repo_detect.bats`, `desktops.bats` plus shared setup in `test_helper.bash`. |
| `.github/workflows/ci.yml` | CI pipeline: ShellCheck at warning severity and the BATS suite. |
```bash
├── .github
│ └── workflows
│ └── ci.yml
├── .gitignore
├── debianito.sh
├── docs
│   ├── boot.md
@@ -154,48 +237,23 @@ For a full setup in ~15-20 minutes, refer to the [Quick Start Visual Guide](/doc
│   ├── gaming.md
│   ├── gpu.md
│   ├── kernel.md
│   ├── quickstart.md
│   ├── repos_config.md
│   ├── retroarch.md
│   ├── swap.md
│   ├── system_info.md
│   ├── system_prefs.md
│   ├── user_priv_feed.md
│   └── zram.md
├── media
│   ├── gift
│   │   └── script.gif
│   └── screenshots
│   ├── 01-system-info.png
│   ├── 02b-pwfeedback.png
│   ├── 02-user-privileges.png
│   ├── 03-system-prefs.png
│   ├── 04b-backports.png
│   ├── 04-repos.png
│   ├── 05-firmware-plan.png
│   ├── 06-gpu-choice.png
│   ├── 07-kernel.png
│   ├── 08-gaming.png
│   ├── 09b-zram-status.png
│   ├── 09-zram-algo.png
│   ├── 10b-swap-status.png
│   ├── 10-swap.png
│   ├── 11b-essential-pack.png
│   └── 11-programs.gif
│   └── gift
│   └── script.gif
├── modules
│   ├── bluetooth.sh
│   ├── bullseye
│   │   ├── extras.sh
│   │   ├── legacy.sh
│   │   └── repos.sh
│ ├── desktop_display.sh
│ ├── desktops
│ │ ├── desktop_gnome.sh
│ │ ├── desktop_kde.sh
│ │ ├── desktop_lxde.sh
│ │ └── desktop_xfce.sh
│ ├── extras
│   │   ├── audio
│   ├── desktop_display.sh
│   ├── extras
│   │   ├── communication
│   │   │   └── communication.sh
│   │   ├── design
@@ -247,11 +305,9 @@ For a full setup in ~15-20 minutes, refer to the [Quick Start Visual Guide](/doc
│   ├── gpu
│   │   ├── amd_intel.sh
│   │   ├── _helpers.sh
│   │   ├── nvidia_manage.sh
│   │   └── nvidia.sh
│ ├── gpu.sh
│ ├── hw_detect.sh
│ ├── kernel.sh
│   ├── gpu.sh
│   ├── kernel.sh
│   ├── repos
│   │   ├── migrate.sh
│   │   └── repo_detect.sh
@@ -260,19 +316,10 @@ For a full setup in ~15-20 minutes, refer to the [Quick Start Visual Guide](/doc
│   ├── sudo_config.sh
│   ├── swap.sh
│   ├── sysinfo.sh
│   ├── system
│   │   ├── audio.sh
│   │   └── system_prefs.sh
│ ├── utils.sh
│ └── zram.sh
├── tests
│ ├── desktops.bats
│ ├── repo_detect.bats
│ ├── test_helper.bash
│ └── utils.bats
│   ├── utils.sh
│   └── zram.sh
└── README.md
```
---
> 🤖 **AI-Assisted Development Note**
Executable → Regular
+103 -227
View File
@@ -2,14 +2,10 @@
# Debianito — simple configurator script
set -euo pipefail
# Colour palette shared with every lazily-loaded module:
# exported so shellcheck sees them as cross-file globals
# (SC2034) instead of unused locals.
export RED='\033[0;31m'
export GREEN='\033[0;32m'
export YELLOW='\033[1;33m'
export CYAN='\033[0;36m'
export NC='\033[0m'
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# TUI dimensions — fixed centered size for whiptail dialogs
TUI_ALTO=20
@@ -19,102 +15,39 @@ TUI_ALTO_LISTA=10
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
MODULES_DIR="${SCRIPT_DIR}/modules"
# ── CLI flags (unattended mode) ──
# --yes: auto-confirm every prompt. Menus still
# display; only _confirm-style prompts auto-accept.
# --allow-destructive: lift the safety gate on
# destructive operations (e.g. branch migration).
# CLI flags consumed by the TUI helpers in modules/utils.sh
# (_confirm auto-accepts) and by destructive-operation gates.
# Exported because they are cross-file globals (SC2034).
export AUTO_YES=0
export ALLOW_DESTRUCTIVE=0
_usage() {
cat <<'EOF'
Usage: debianito.sh [OPTIONS]
Debian post-installation configurator (Debian 11/12/13).
Options:
--yes Auto-confirm all prompts (unattended mode).
Menus still require interaction.
--allow-destructive Allow destructive operations (e.g. branch
migration) without an extra safety gate.
--help Show this help and exit.
Examples:
./debianito.sh --yes
./debianito.sh --yes --allow-destructive
EOF
}
while [ $# -gt 0 ]; do
case "$1" in
--yes) AUTO_YES=1 ;;
--allow-destructive) ALLOW_DESTRUCTIVE=1 ;;
--help|-h) _usage; exit 0 ;;
*)
echo "Unknown option: $1 (run with --help for usage)" >&2
exit 2
;;
esac
shift
done
# Boot profiling: DEBIANITO_PROFILE=1 stamps each boot
# step to stderr for before/after comparison.
_boot_mark() {
[ "${DEBIANITO_PROFILE:-0}" = "1" ] || return 0
printf '[%s] %s\n' "$(date +%s%N)" "$*" >&2
}
_boot_mark "boot: start (before module sources)"
# Eager core only: utils.sh holds every boot function
# and TUI helper; hw_detect.sh declares the global
# device arrays (PCI_NET_DEVS, ...) that lazily-loaded
# modules read under `set -u`. All other modules are
# sourced on demand via _load_module() in main_menu().
source "${MODULES_DIR}/utils.sh"
source "${MODULES_DIR}/hw_detect.sh"
[ -f "${MODULES_DIR}/sysinfo.sh" ] && source "${MODULES_DIR}/sysinfo.sh"
source "${MODULES_DIR}/sudo_config.sh"
source "${MODULES_DIR}/repos/repo_detect.sh"
source "${MODULES_DIR}/repos.sh"
[ -f "${MODULES_DIR}/firmware.sh" ] && source "${MODULES_DIR}/firmware.sh"
[ -f "${MODULES_DIR}/bluetooth.sh" ] && source "${MODULES_DIR}/bluetooth.sh"
[ -f "${MODULES_DIR}/gpu.sh" ] && source "${MODULES_DIR}/gpu.sh"
[ -f "${MODULES_DIR}/kernel.sh" ] && source "${MODULES_DIR}/kernel.sh"
[ -f "${MODULES_DIR}/gaming.sh" ] && source "${MODULES_DIR}/gaming.sh"
[ -f "${MODULES_DIR}/extras.sh" ] && source "${MODULES_DIR}/extras.sh"
[ -f "${MODULES_DIR}/zram.sh" ] && source "${MODULES_DIR}/zram.sh"
[ -f "${MODULES_DIR}/extras/java.sh" ] && source "${MODULES_DIR}/extras/java.sh"
[ -f "${MODULES_DIR}/rescue.sh" ] && source "${MODULES_DIR}/rescue.sh"
[ -f "${MODULES_DIR}/swap.sh" ] && source "${MODULES_DIR}/swap.sh"
[ -f "${MODULES_DIR}/desktop_display.sh" ] && source "${MODULES_DIR}/desktop_display.sh"
# ── Bullseye-specific modules (loaded only on Debian 11) ──
# Eager-conditional: the menu dispatch branches on
# `type install_*_bullseye`, so these must be defined
# before main_menu() runs.
if [ -d "${MODULES_DIR}/bullseye" ]; then
[ -f "${MODULES_DIR}/bullseye/legacy.sh" ] && source "${MODULES_DIR}/bullseye/legacy.sh"
[ -f "${MODULES_DIR}/bullseye/repos.sh" ] && source "${MODULES_DIR}/bullseye/repos.sh"
[ -f "${MODULES_DIR}/bullseye/repos.sh" ] && source "${MODULES_DIR}/bullseye/repos.sh"
[ -f "${MODULES_DIR}/bullseye/extras.sh" ] && source "${MODULES_DIR}/bullseye/extras.sh"
fi
_boot_mark "boot: core modules loaded"
# ── Interrupt safety: restore repository state on Ctrl+C / TERM ──
_on_interrupt() {
echo -e "${RED}[!] Interrupted. Restoring repository state if possible...${NC}"
if type restore_previous_repos &>/dev/null; then
restore_previous_repos 2>/dev/null || true
fi
# Clean up temporary files created during execution
rm -rf /tmp/debianito.* 2>/dev/null || true
echo -e "${YELLOW}[!] Temp files cleaned.${NC}"
exit 130
}
trap _on_interrupt INT TERM
REPOS_CONFIGURED=false
DEBIAN_VERSION=""
export DEBIAN_CODENAME=""
DEBIAN_CODENAME=""
main_menu() {
# Auto-adjust TUI dimensions for small terminals
if [ "${LINES:-24}" -lt $((TUI_ALTO + 6)) ] || [ "${COLUMNS:-80}" -lt $((TUI_ANCHO + 6)) ]; then
TUI_ALTO=$((${LINES:-24} - 4 > 8 ? ${LINES:-24} - 4 : 8))
TUI_ANCHO=$((${COLUMNS:-80} - 4 > 50 ? ${COLUMNS:-80} - 4 : 50))
TUI_ALTO=$(( ${LINES:-24} - 4 > 8 ? ${LINES:-24} - 4 : 8))
TUI_ANCHO=$(( ${COLUMNS:-80} - 4 > 50 ? ${COLUMNS:-80} - 4 : 50))
TUI_ALTO_LISTA=$((TUI_ALTO - 10 > 4 ? TUI_ALTO - 10 : 4))
fi
@@ -126,127 +59,72 @@ main_menu() {
$TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
"1" "System Information" \
"2" "User Privileges & Feedback" \
"3" "System Preferences" \
"4" "Configure Repositories" \
"5" "Firmware, Wireless & Bluetooth" \
"6" "Graphics Drivers & Mesa Stack" \
"7" "Kernel" \
"8" "Gaming Setup" \
"9" "ZRAM" \
"10" "Swap Management" \
"11" "Install Programs and Software" \
"12" "Boot Rescue + GRUB" \
"13" "Desktop & Display" \
"14" "Exit")
"3" "Configure Repositories" \
"4" "Firmware, Wireless & Bluetooth" \
"5" "Graphics Drivers & Mesa Stack" \
"6" "Kernel" \
"7" "Gaming Setup" \
"8" "ZRAM" \
"9" "Swap Management" \
"10" "Install Programs and Software" \
"11" "Boot Rescue + GRUB" \
"12" "Desktop & Display" \
"13" "Exit")
clear
case "$choice" in
1)
_load_module sysinfo || continue
_show_sysinfo
;;
2)
_load_module sudo_config || continue
config_sudo || true
;;
3)
_load_module system_prefs || continue
_system_preferences_menu || true
STATE_REFRESHED=true
;;
4)
if [ "$DEBIAN_VERSION" = "11" ] && type configure_repos_bullseye &>/dev/null; then
configure_repos_bullseye || true
else
_load_module repos || continue
configure_repos || true
fi
STATE_REFRESHED=true
;;
5)
if [ "$DEBIAN_VERSION" = "11" ] && type install_firmware_bullseye &>/dev/null; then
# Bullseye flow calls _handle_wireless
# from firmware.sh: load the module
# (cascades repo_detect, bluetooth, repos).
_load_module firmware || continue
install_firmware_bullseye || true
else
_load_module firmware || continue
install_firmware || true
fi
STATE_REFRESHED=true
;;
6)
_load_module gpu || continue
local gpu_sub
gpu_sub=$(_menu "Graphics Drivers" "" 12 50 2 \
"1" "Radeon/Intel Mesa" \
"2" "NVIDIA Drivers")
[ -z "$gpu_sub" ] && continue
clear
case $gpu_sub in
1) _install_amd_intel_stack || true ;;
2) _install_nvidia_stack || true ;;
esac
STATE_REFRESHED=true
;;
7)
_load_module kernel || continue
show_kernel_menu || true
STATE_REFRESHED=true
;;
8)
if [ "$DEBIAN_VERSION" = "11" ] && type install_gaming_bullseye &>/dev/null; then
# Bullseye flow calls the gaming bundle
# (ensure_contrib_repo, install_steam,
# install_mangohud, ...) and java.sh:
# load the bundle (cascades repo_detect,
# java, repos).
_load_module gaming || continue
install_gaming_bullseye || true
else
_load_module gaming || continue
install_gaming || true
fi
STATE_REFRESHED=true
;;
9)
_load_module zram || continue
zram_menu || true
STATE_REFRESHED=true
;;
10)
_load_module swap || continue
manage_swap || true
STATE_REFRESHED=true
;;
11)
if [ "$DEBIAN_VERSION" = "11" ] && type install_extras_bullseye &>/dev/null; then
# Bullseye flow calls _install_dev_java
# from extras/java.sh: load it explicitly.
_load_module java || continue
install_extras_bullseye || true
else
_load_module extras || continue
install_extras || true
fi
STATE_REFRESHED=true
;;
12)
_load_module rescue || continue
rescue_boot || true
STATE_REFRESHED=true
;;
13)
_load_module desktop_display || continue
manage_desktop_display || true
STATE_REFRESHED=true
;;
14)
echo "Exiting."
exit 0
;;
1) _show_sysinfo ;;
2) config_sudo || true ;;
3)
if [ "$DEBIAN_VERSION" = "11" ] && type configure_repos_bullseye &>/dev/null; then
configure_repos_bullseye || true
else
configure_repos || true
fi
STATE_REFRESHED=true
;;
4)
if [ "$DEBIAN_VERSION" = "11" ] && type install_firmware_bullseye &>/dev/null; then
install_firmware_bullseye || true
else
install_firmware || true
fi
;;
5)
local gpu_sub
gpu_sub=$(_menu "Graphics Drivers" "" 12 50 2 \
"1" "Radeon/Intel Mesa" \
"2" "NVIDIA Drivers")
[ -z "$gpu_sub" ] && continue
clear
case $gpu_sub in
1) _install_amd_intel_stack || true ;;
2) _install_nvidia_stack || true ;;
esac
STATE_REFRESHED=true
;;
6) show_kernel_menu || true ;;
7)
if [ "$DEBIAN_VERSION" = "11" ] && type install_gaming_bullseye &>/dev/null; then
install_gaming_bullseye || true
else
install_gaming || true
fi
STATE_REFRESHED=true
;;
8) zram_menu || true ;;
9) manage_swap || true; STATE_REFRESHED=true ;;
10)
if [ "$DEBIAN_VERSION" = "11" ] && type install_extras_bullseye &>/dev/null; then
install_extras_bullseye || true
else
install_extras || true
fi
;;
11) rescue_boot || true ;;
12) manage_desktop_display || true ;;
13) echo "Exiting."; exit 0 ;;
esac
if $STATE_REFRESHED; then
refresh_system_state
@@ -254,34 +132,32 @@ main_menu() {
done
}
_boot_mark "boot: pre-flight checks"
check_root
check_sudo
if ! command -v whiptail >/dev/null 2>&1; then
echo -e "${YELLOW}[+] whiptail not found. Installing required TUI dependencies...${NC}"
if _ensure_apt_updated && sudo apt-get install -y whiptail; then
echo -e "${GREEN}[+] whiptail installed.${NC}"
else
echo -e "${RED}[-] Could not install whiptail (no network?).${NC}" >&2
echo -e "${RED} The TUI menu requires it; install manually and re-run.${NC}" >&2
fi
if ! _check_network; then
echo -e "${YELLOW}──────────────────────────────────────────${NC}"
echo -e "${YELLOW} No internet connectivity detected.${NC}"
echo -e "${YELLOW} Package installation will fail without network.${NC}"
echo -e "${YELLOW} You can use: System Info, User Privileges, and${NC}"
echo -e "${YELLOW} other offline features.${NC}"
echo -e "${YELLOW}──────────────────────────────────────────${NC}"
fi
check_system_time
# Hardware detection is deferred on demand via
# _ensure_state_detected(); only the OS version is
# eager because the menu dispatch branches on it.
_boot_mark "boot: os detection"
detect_debian_version
detect_cpu_ram
detect_kernel
detect_gpu
detect_network
detect_displayserver
detect_storage
detect_desktop_environment
_configure_lightdm
detect_audio_server
# ── Bullseye-specific init (archive phase) ──
if [ "$DEBIAN_VERSION" = "11" ] && type check_bullseye_archive_phase &>/dev/null; then
check_bullseye_archive_phase
fi
if ! command -v whiptail >/dev/null 2>&1; then
echo -e "${RED}[-] whiptail is required for the TUI menu. Aborting.${NC}" >&2
exit 1
fi
_boot_mark "boot: entering main menu"
main_menu
+12 -19
View File
@@ -1,13 +1,11 @@
# Option 12: Boot Rescue + GRUB
## Option 11: Boot Rescue & Repair
### 1. What does this component do?
This component diagnoses and fixes boot-related problems on Debian: GRUB configuration, UEFI Secure Boot integrity and initrd image validity. It performs three operations: configuring the GRUB boot menu (timeout and visibility settings), repairing UEFI Secure Boot by reinstalling signed bootloader packages after kernel or driver changes, and regenerating initramfs images to fix missing driver errors. Every modification starts with a timestamped backup, and a failed operation rolls back to the original state.
This component serves as a comprehensive rescue toolkit designed to diagnose and fix boot-related issues on Debian systems, specifically targeting GRUB configuration, UEFI Secure Boot integrity, and initrd image validity. It provides three primary operations: configuring the GRUB boot menu behavior (including timeout adjustments and visibility settings), repairing UEFI Secure Boot by reinstalling signed bootloader packages after kernel or driver changes, and regenerating initramfs images to resolve missing driver errors. All modifications are protected by automatic backup creation before system files are altered, with built-in rollback mechanisms that restore the original state if any operation fails during execution.
### 2. Logical Execution Flow
**Block 1: GRUB Boot Menu Settings**
**Block 1 — GRUB Boot Menu Settings**
- **Menu Options:** Users select from four presets or a custom option to control boot behavior:
1. Hide GRUB menu entirely (Fastest boot, requires holding ESC during power-on).
2. Show 3-second countdown (Faster boot).
@@ -17,8 +15,7 @@ This component diagnoses and fixes boot-related problems on Debian: GRUB configu
- **Backup & Safety:** Before applying changes, the script creates a timestamped backup of `/etc/default/grub`. It writes an override configuration file at `/etc/default/grub.d/99_script_override.cfg` to ensure settings persist across updates.
- **Execution & Rollback:** The system runs `update-grub` to apply changes. If this command fails, the script automatically restores the timestamped backup and removes the override file to prevent boot issues.
**Block 2: UEFI Secure Boot Repair**
**Block 2 — UEFI Secure Boot Repair**
- **Pre-checks:** Before proceeding, the script verifies two conditions:
1. The system is running in UEFI mode (checks for existence of `/sys/firmware/efi`).
2. Secure Boot is currently enabled (uses `mokutil --sb-state` to confirm status).
@@ -28,15 +25,14 @@ This component diagnoses and fixes boot-related problems on Debian: GRUB configu
2. Reinstalls GRUB to the EFI partition using `grub-install` with the appropriate target and directory detection (preferring `/boot/efi` over `/boot`).
3. Regenerates the GRUB configuration via `update-grub`.
**Block 3: Initramfs Regeneration**
**Block 3 — Initramfs Regeneration**
- **Confirmation:** The script prompts for user confirmation before proceeding to avoid unintended rebuilds.
- **Execution:** Upon approval, it runs `update-initramfs -u -k all || true` to regenerate initrd images for all installed kernels.
- **Execution:** Upon approval, it runs `update-initramfs -u -k all` to regenerate initrd images for all installed kernels.
- **Purpose:** This fixes boot issues caused by missing drivers or corrupted initrd images that prevent the system from loading properly.
### 3. Safeguards and Automation
### 3. Intelligent Automation
- **EFI Directory Detection:** The script detects the EFI partition, checking `/boot/efi` first and falling back to `/boot` if necessary.
- **EFI Directory Detection:** The script intelligently detects the EFI partition, checking `/boot/efi` first and falling back to `/boot` if necessary.
- **Secure Boot State Verification:** Instead of assuming Secure Boot is active, it uses `mokutil --sb-state` to verify the actual state before attempting repairs on non-UEFI or disabled systems.
- **GRUB Backup Strategy:** A timestamped backup file (e.g., `/etc/default/grub.backup.YYYYMMDD_HHMMSS`) is created prior to any modification, ensuring a safe point for rollback.
- **Rollback Mechanism:** If `update-grub` fails during the GRUB settings or Secure Boot repair process, the script automatically restores the backup and cleans up temporary override files.
@@ -44,29 +40,26 @@ This component diagnoses and fixes boot-related problems on Debian: GRUB configu
- **Input Validation:** Custom timeout inputs are validated using regex matching for integers (e.g., `^-?[0-9]+$`), allowing negative numbers like -1 while rejecting invalid text.
- **Variable Handling Logic:** The `_set_grub_var()` function handles three distinct cases: updating an existing variable, uncommenting a commented variable, or appending a new variable if it does not exist in the configuration file.
### 4. Managed Packages and Resources
### 4. Paquetes y Recursos Gestionados
**Boot Packages Reinstalled (Secure Boot Repair):**
| Package | Purpose |
| --- | --- |
|---|---|
| shim-signed | UEFI Secure Boot shim (first-stage bootloader) |
| grub-efi-amd64-signed | Signed GRUB for UEFI |
| linux-image-amd64 | Kernel image (re-signed) |
**GRUB Settings Modified:**
| Variable | Option 1 | Option 2 | Option 3 | Custom |
| --- | --- | --- | --- | --- |
|---|---|---|---|---|
| GRUB_TIMEOUT | 0 | 3 | 5 | User value |
| GRUB_TIMEOUT_STYLE | hidden | menu | menu | menu |
| GRUB_RECORDFAIL_TIMEOUT | 0 | 3 | 5 | User value |
| GRUB_DISABLE_OS_PROBER | true | — | — | — |
**System Files Modified:**
| Package | Purpose |
| --- | --- |
|---|---|
| /etc/default/grub | Main GRUB configuration |
| /etc/default/grub.d/99_script_override.cfg | Persistent override (survives grub updates) |
| /etc/default/grub.backup.* | Timestamped backup (auto-created) |
+14 -50
View File
@@ -1,25 +1,14 @@
# Option 13: Desktop & Display
## Option 12: Desktop & Display
### 1. What does this component do?
This component manages both desktop environments and display managers. Desktop Environment installs **GNOME, KDE Plasma, XFCE, or LXDE**; Display Manager handles LightDM, GDM3, SDDM, and greetd (login screens). It handles autologin setup, user list visibility toggles, and a specific Wayland override for NVIDIA GPUs. Desktop Environment management supports **XFCE** (full / minimal / Wayland `labwc` on Trixie / custom), **LXDE** (full / core), **GNOME** (full / core) and **KDE Plasma** (full / minimal), with polkit rules for suspend/backlight where applicable.
**Modular layout:** the menu, the shared helpers (`_existing_display_manager`, `_offer_pipewire_after_desktop`) and the display-manager configuration routines live in `modules/desktop_display.sh`; every desktop installer lives in its own module under `modules/desktops/` (`desktop_xfce.sh`, `desktop_lxde.sh`, `desktop_gnome.sh`, `desktop_kde.sh`), lazily loaded on demand via `_load_module`.
This component manages display managers (the login screen you see before entering your desktop). It allows users to install and configure LightDM, GDM3, SDDM, or greetd. It handles autologin setup, user list visibility toggles, and a specific Wayland override for NVIDIA GPUs. Currently, Desktop Environment management is marked as "Coming Soon".
### 2. Logical Flow of Execution
The execution logic is divided into five functional blocks:
**Block 1: Desktop Environment Selection & Installation**
- The main menu (`manage_desktop_display` in `desktop_display.sh`) offers two primary options: `1 Desktop Environment` and `2 Display Manager` (plus `3 Back to main menu`).
- **Desktop Environment** opens `desktop_environment_menu` with four environments:
1. **XFCE**: full / minimal / Wayland (`labwc`, Debian 13 only) / custom package checklist.
2. **LXDE**: full / core.
3. **GNOME**: full (`gnome`) / core (`gnome-core`), with GDM3.
4. **KDE Plasma**: full (`kde-standard`) / minimal (`kde-plasma-desktop`), with SDDM.
- Each environment lives in its own module under `modules/desktops/` and is lazy-loaded from the menu dispatch with `_load_module xfce|lxde|gnome|kde` (dependencies: `desktop_display` + `audio`).
- **Display Manager** opens `display_manager_menu`: LightDM, GDM3, SDDM, and greetd (12/13 only).
**Block 1 — Display Manager Selection**
- The main menu offers two primary options within Option 12: "Desktop Environment" (currently unavailable) and "Display Manager".
- Selecting "Display Manager" opens a submenu listing available display managers based on the Debian version.
- **Available Managers:**
- LightDM (available on all Debian versions).
- GDM3 (available on all Debian versions).
@@ -27,16 +16,14 @@ The execution logic is divided into five functional blocks:
- greetd (available only on Debian 12 and 13).
- Each selected manager opens its own dedicated configuration submenu.
**Block 2: LightDM Configuration**
**Block 2 — LightDM Configuration**
- A checklist menu presents three options:
1. Install LightDM + GTK Greeter (auto-selects the display manager via debconf-set-selections).
2. Enable user list at login screen (writes `greeter-hide-users=false` to a configuration file in `.conf.d`).
3. Enable autologin for current user (uncomments `autologin-user` and sets timeout to 0 in the main config).
- **Idempotency:** The script checks if `lightdm` and `lightdm-gtk-greeter-settings` are already installed before attempting installation, skipping redundant steps.
**Block 3: GDM3 Configuration**
**Block 3 — GDM3 Configuration**
- A checklist menu presents up to four options:
1. Install/Reinstall gdm3 (auto-selects via debconf).
2. Toggle user list visibility (toggles `disable-user-list` in greeter.dconf-defaults).
@@ -47,8 +34,7 @@ The execution logic is divided into five functional blocks:
- **Risk Warning:** Displays a warning that this may break graphics or cause instability.
- **Toggle Behavior:** If the symlink exists, removing it reverts to default behavior; if not present, creating it enables the override.
**Block 4: SDDM Configuration**
**Block 4 — SDDM Configuration**
- A submenu offers two options:
1. Install SDDM (auto-selects via debconf).
2. Enable Autologin:
@@ -56,8 +42,7 @@ The execution logic is divided into five functional blocks:
- **Configuration:** Writes autologin settings to `/etc/sddm.conf.d/autologin.conf`.
- **Fallback:** If no session is found, it enables autologin but warns that SDDM will use the default session.
**Block 5: greetd Configuration**
**Block 5 — greetd Configuration**
- greetd is a minimal, modern display manager designed for Wayland environments.
- Submenu options vary by Debian version:
- **All Versions:** Install base `greetd`, Install `greetd` + `tuigreet` (recommended TUI greeter).
@@ -65,27 +50,20 @@ The execution logic is divided into five functional blocks:
- **Backports Handling:** On Bookworm, `tuigreet` is installed from backports if not enabled.
- **Important Warning:** greetd is installed but NOT configured by the script. The user must manually create `/etc/greetd/config.toml` to be able to log in. The script displays a warning with references to man pages for configuration details.
### 3. Automations
The component automates several steps:
### 3. Smart Automations
The component utilizes several intelligent automation features:
- **debconf-set-selections:** Pre-selects the display manager as default before `apt install`, preventing the interactive "Configuring shared/default-x-display-manager" dialog that would otherwise pause the script execution.
- **Session Auto-Detection (SDDM):** Checks `.desktop` files in priority order (Wayland first, then X11) to set the correct session type for autologin configuration.
- **Wayland Toggle:** Creates or removes a udev rule symlink to `/dev/null`. This is a known workaround that disables the GDM rule blocking NVIDIA Wayland support.
- **greetd Backports:** On Bookworm, `tuigreet` is installed from backports (newer version) if not already enabled in the system.
- **Manual Config Warning:** The script explicitly informs users that greetd requires manual configuration and points to man pages for further details.
- **User Detection:** Uses `SUDO_USER` (the real user who ran sudo) instead of `$USER` (which is root during script execution). This ensures the correct user is set for automatic login across all display managers.
- **Pre-apply Summary:** every desktop installer shows a confirmation list of the packages (`_confirm_install_list`) before any debconf or apt transaction starts; declining aborts safely.
- **Existing DM Preservation:** the GNOME and KDE installers detect the current default display manager (`_existing_display_manager` reads `/etc/X11/default-display-manager`, falling back to an enabled DM systemd unit), preseed that manager instead of the new one, and skip `systemctl enable` so an already-configured login screen is never overwritten.
- **PipeWire Offer:** after any desktop install (XFCE, LXDE, GNOME, KDE), `_offer_pipewire_after_desktop` checks for `pipewire-audio` (`pipewire` on Bullseye) and, when missing, asks once before running `_install_pipewire_standard`. Declining is a choice, not an error: the helper always returns 0.
- **Stale-lists Guard:** `_ensure_apt_updated` runs before the GNOME/KDE `apt install` transactions, refreshing the package lists at most once per session.
### 4. Packages and Resources Managed
**Display Manager Packages:**
| DM | Packages | Debian Versions |
| --- | --- | --- |
|---|---|---|
| LightDM | lightdm, lightdm-gtk-greeter, lightdm-gtk-greeter-settings | All |
| GDM3 | gdm3 | All |
| SDDM | sddm | All |
@@ -95,22 +73,9 @@ The component automates several steps:
| greetd + nwg-hello | greetd, nwg-hello | 13 only |
| greetd + wlgreet | greetd, wlgreet | 13 only |
**Desktop Environment Packages:**
| Environment | Variant | Packages | Display Manager |
| --- | --- | --- | --- |
| XFCE | Full | xfce4, xfce4-goodies, xfce4-power-manager | not managed by the installer |
| XFCE | Minimal | xfce4 | not managed by the installer |
| XFCE | Wayland (13 only) | xfce4, labwc | not managed by the installer |
| XFCE | Custom | user-selected checklist | not managed by the installer |
| LXDE | Full / Core | lxde / lxde-core | lightdm (always) |
| GNOME | Full / Core | gnome / gnome-core | gdm3 (installed; existing DM kept as default) |
| KDE Plasma | Full / Minimal | kde-standard / kde-plasma-desktop | sddm (installed; existing DM kept as default) |
**Configuration Files Modified:**
| DM | File Path | Configuration Purpose |
| --- | --- | --- |
|---|---|---|
| LightDM | /etc/lightdm/lightdm.conf.d/50-debianito-userlist.conf | User list visibility toggle |
| LightDM | /etc/lightdm/lightdm.conf | Autologin user + timeout settings |
| GDM3 | /etc/gdm3/greeter.dconf-defaults | User list toggle (disable-user-list) |
@@ -119,9 +84,8 @@ The component automates several steps:
| SDDM | /etc/sddm.conf.d/autologin.conf | Autologin user + session type |
**Session Detection Priority (SDDM Autologin):**
| Priority | Session File | Session Type |
| --- | --- | --- |
|---|---|---|
| 1 | /usr/share/wayland-sessions/plasmawayland.desktop | KDE Wayland |
| 2 | /usr/share/wayland-sessions/lxqt-wayland.desktop | LXQt Wayland |
| 3 | /usr/share/xsessions/plasma.desktop | KDE X11 |
+39 -84
View File
@@ -1,103 +1,58 @@
# Option 5: Firmware, Wireless & Bluetooth
# Option 4: Advanced Firmware & Wireless Architecture
## 1. What the Debian Package Misses
## 1. The Hidden Debian "Problem"
In standard Debian packaging, the meta-package `firmware-linux-nonfree` serves as a generic aggregator for hardware blobs. However, due to internal dependency resolution rules and conservative versioning strategies within the repository, this package often fails to automatically include vendor-specific firmware drivers for newer or niche network controllers (specifically Realtek Wi-Fi/Bluetooth and Intel Ethernet/Wi-Fi variants).
In standard Debian packaging, the meta-package `firmware-linux-nonfree` collects hardware blobs from many sources. Its dependency rules and conservative versioning mean it often misses vendor-specific firmware for newer or niche network controllers (Realtek Wi-Fi/Bluetooth and Intel Ethernet/Wi-Fi variants in particular).
This script fills that gap. It scans the hardware itself instead of trusting the meta-package's `Recommends` field: detection is separate from installation, so a specific blob (e.g., `firmware-iwlwifi`, `firmware-realtek`) is pulled in only when its hardware ID is present, even if the base package is already installed. The result is no network interface that appears but cannot initialize because its firmware is missing.
This script acts as an intelligent injector to bridge that gap. It does not rely solely on the meta-package's `Recommends` field; instead, it actively scans the hardware topology to identify missing dependencies. By decoupling the detection from the installation logic, the architecture ensures that even if the base package is installed, specific vendor blobs (e.g., `firmware-iwlwifi`, `firmware-realtek`) are explicitly pulled in only when their corresponding hardware IDs are confirmed present on the system. This prevents "half-baked" network connectivity where the interface exists but lacks the necessary firmware to initialize.
## 2. Dual Scan Engine (PCI & USB)
To ensure comprehensive hardware detection while minimizing false positives, the script utilizes a dual-scan engine that interrogates both PCI and USB buses with strict filtering logic:
The script scans both the PCI and USB buses with strict filters, which catches more hardware while keeping false positives low:
* **PCI Bus Scanning (`lspci`):** The engine parses `lspci -nn` output specifically targeting lines containing "network controller" or "ethernet controller". This captures both wireless adapters (e.g., Intel AX200) and wired NICs (e.g., Realtek RTL8125 2.5GbE), so Ethernet firmware needs are covered too.
* **USB Bus Scanning (`lsusb`):** The USB scan applies keyword filtering to ignore peripherals unrelated to networking, such as audio devices or card readers. It specifically looks for terms like "wireless", "wifi", "802.11", and "wlan". Bluetooth devices are also detected separately through `PCI_BT_DEVS` and `USB_BT_DEVS`.
* **Deduplication:** The collected device lists are merged into a single array (`dev_list`) so the same hardware is not processed twice across bus categories and the plan stays clean.
* **PCI Bus Scanning (`lspci`):** The engine parses `lspci -nn` output specifically targeting lines containing "network controller" or "ethernet controller". This captures both wireless adapters (e.g., Intel AX200) and wired NICs (e.g., Realtek RTL8125 2.5GbE), ensuring that Ethernet firmware requirements are also met during the process.
* **USB Bus Scanning (`lsusb`):** The USB scan applies keyword filtering to ignore peripherals unrelated to networking, such as audio devices or card readers. It specifically looks for terms like "wireless", "wifi", "802.11", and "wlan". Bluetooth devices are also detected separately through `PCI_BT_DEVS` and `USB_BT_DEVS`.
* **Deduplication:** The collected device lists are merged into a single array (`dev_list`) to prevent duplicate processing of the same hardware instance across different bus categories, ensuring a clean plan generation.
## 3. Dynamic Hardware Mapping Matrix
The script employs an associative mapping strategy in `_detect_firmware_needs` to translate raw vendor strings from `lspci`/`lsusb` into specific Debian package names. This matrix is critical for handling the "Intel Split" and other vendor-specific requirements:
The script employs an associative mapping strategy in `_detect_firmware_needs` to translate raw vendor strings from `lspci`/`lsusb` into specific Debian package names. This mapping handles the "Intel Split" and other vendor-specific requirements:
* **Vendor Filtering:** The engine first filters out generic or unsupported vendors (e.g., non-Realtek, non-Intel, non-Atheros) to avoid unnecessary package pulls.
* **Package Assignment Logic:**
* **Intel Wi-Fi Hardware** ➔ `firmware-iwlwifi` (Specific driver for wireless chips).
* **Intel Ethernet Hardware** ➔ `firmware-intel-misc` (Often pulled via `Recommends` of the base package, but explicitly tracked here).
* **Realtek Hardware** ➔ `firmware-realtek`.
* **MediaTek / Ralink Hardware** ➔ `firmware-mediatek`.
* **Atheros / Qualcomm Hardware** ➔ `firmware-atheros`.
* **Vendor Filtering:** The engine first filters out generic or unsupported vendors (e.g., non-Realtek, non-Intel, non-Atheros) to avoid unnecessary package pulls.
* **Package Assignment Logic:**
* **Intel Wi-Fi Hardware** ➔ `firmware-iwlwifi` (Specific driver for wireless chips).
* **Intel Ethernet Hardware** ➔ `firmware-intel-misc` (Often pulled via `Recommends` of the base package, but explicitly tracked here).
* **Realtek Hardware** ➔ `firmware-realtek`.
* **MediaTek / Ralink Hardware** ➔ `firmware-mediatek`.
* **Atheros / Qualcomm Hardware** ➔ `firmware-atheros`.
So a system with an Intel Wi-Fi 6 card gets `firmware-iwlwifi` queued explicitly, whether or not the base meta-package claims to cover it. The output is rendered as a deduplicated plan with visual indicators (e.g., `[+] package ← hardware`) for user clarity.
This mapping ensures that if a system contains an Intel Wi-Fi 6 card, the script explicitly queues `firmware-iwlwifi` regardless of whether the base meta-package claims to cover it. The output is rendered as a deduplicated plan with visual indicators (e.g., `[+] package ← hardware`) for user clarity.
## 4. Installation Execution Flow (Atomic Pipeline)
The installation process follows a strict atomic pipeline defined in `install_firmware`, ensuring system stability and version consistency:
The installation runs through the atomic pipeline in `install_firmware`:
1. **Repository Validation:** The script first verifies that `/etc/apt/sources.list` or `.d/` contains the `non-free` component. If absent, it halts to prevent installation failures.
2. **Plan Rendering & Confirmation:** A diagnostic tree is generated showing detected controllers and planned packages. The user must explicitly confirm ("Apply the network & firmware plan?") before proceeding.
3. **Base Meta-Package Selection (Backports vs. Stable):**
* If `firmware-linux-nonfree` is already installed, the script checks for a newer version in backports (`${DEBIAN_CODENAME}-backports`). It prompts to upgrade if available, as backports often contain firmware for very recent hardware not yet in stable.
* If not installed, it presents a choice between Stable (Ultra-tested) and Backports (Recommended for modern hardware).
4. **Sequential Injection:** After the base package is secured, the script iterates through `_DETECTED_FW_PKGS`. It uses `apt-cache policy` to validate availability before installing specific vendor packages (`firmware-realtek`, etc.), skipping those already present or unavailable in repositories.
1. **Repository Validation:** The script first verifies that `/etc/apt/sources.list` or `.d/` contains the `non-free` component. If absent, it halts to prevent installation failures.
2. **Plan Rendering & Confirmation:** A diagnostic tree is generated showing detected controllers and planned packages. The user must explicitly confirm ("Apply the network & firmware plan?") before proceeding.
3. **Base Meta-Package Selection (Backports vs. Stable):**
* If `firmware-linux-nonfree` is already installed, the script checks for a newer version in backports (`${DEBIAN_CODENAME}-backports`). It prompts to upgrade if available, as backports often contain firmware for very recent hardware not yet in stable.
* If not installed, it presents a choice between Stable (thoroughly tested) and Backports (Recommended for modern hardware).
4. **Sequential Injection:** After the base package is secured, the script iterates through `_DETECTED_FW_PKGS`. It uses `apt-cache policy` to validate availability before installing specific vendor packages (`firmware-realtek`, etc.), skipping those already present or unavailable in repositories.
## 5. Broadcom Redundancy System (3-Tier Support)
Broadcom chipsets require complex handling due to their mix of open-source and proprietary driver support. The `_handle_wireless` function implements a three-tier logic to maximize compatibility without breaking the kernel:
## 5. Broadcom Wireless Support (DKMS Single-Path)
* **Tier 1 (Open/Non-Free Direct):** For supported chips, it attempts to install `firmware-brcm80211`. This is preferred as it uses standard DKMS modules provided by Debian.
* **Tier 2 (Firmware Emulation):** If Tier 1 fails or the chipset requires firmware emulation (e.g., older B43 chips), the script installs `firmware-b43-installer` or `firmware-b43legacy-installer`. This is a fallback for hardware that cannot be driven by standard kernel modules.
* **Tier 3 (Proprietary DKMS):** For unsupported chipsets where open-source drivers are insufficient, the system falls back to compiling proprietary drivers (`broadcom-sta-dkms`). The script explicitly checks for `linux-headers` availability before attempting this compilation, as it requires kernel headers matching the running version. It warns the user that a reboot may be required after installation.
Broadcom chipsets require proprietary handling because no open-source driver covers most `14e4:*` devices on modern kernels. The current implementation in `firmware.sh:_handle_wireless` uses a **single-path DKMS flow** (`broadcom-sta-dkms` + `wl` module) instead of a 3-tier fallback:
1. **Device detection**: Iterates `PCI_NET_DEVS` (parsed from `lspci -nn` by the on-demand detection battery) and extracts the Broadcom ID `14e4:XXXX`. Non-Broadcom devices are skipped.
2. **Dependency guard**: Verifies `linux-headers-amd64` and `dkms` are available via `apt-cache show`. If missing, shows: `"linux-headers-amd64 or dkms are not available in your repositories."`
3. **User confirmation**: `whiptail --yesno "Install broadcom-sta-dkms, dkms, and wireless-tools?"`
4. **Step-by-step install** (allows partial failure without aborting the whole module):
```bash
_run_cmd "Broadcom Dependencies" "sudo DEBIAN_FRONTEND=noninteractive apt install -y dkms wireless-tools linux-headers-amd64" || true
_run_cmd "Broadcom Driver" "sudo DEBIAN_FRONTEND=noninteractive apt install -y broadcom-sta-dkms" || true
```
5. **Blacklist persistence**: Writes `/etc/modprobe.d/blacklist-broadcom.conf`:
```
blacklist b43
blacklist b43legacy
blacklist brcmsmac
blacklist bcma
blacklist ssb
```
6. **SSH Warning + Module Switch**: Before unloading current WiFi modules, the script warns about SSH disconnection. Then removes conflicting modules and loads the Broadcom driver:
```bash
_msg "Network Warning" "The script is about to unload current WiFi kernel modules to load the Broadcom driver.
If you are connected via SSH over WiFi, YOUR CONNECTION WILL DROP. Please reconnect after a few seconds."
_run_cmd "Modprobe" "sudo modprobe -r b43 b43legacy b44 bcma brcmsmac brcmfmac ssb wl 2>/dev/null || true" || true
_run_cmd "Modprobe" "sudo modprobe wl" || true
```
7. **Initramfs**: Rebuilds the initramfs to include the new `wl` module:
```bash
_run_cmd "Initramfs" "sudo update-initramfs -u" || true
```
1. **Combo BT handling**: If a Broadcom Bluetooth device is also present (`PCI_BT_DEVS` grep `broadcom`), writes `softdep wl post: btusb` to `/etc/modprobe.d/broadcom-combo.conf`
2. **Post-DKMS verification**: Checks for `/lib/modules/$(uname -r)/updates/dkms/wl.ko*`. If missing, shows the last 20 lines of `dmesg` and offers `dpkg-reconfigure broadcom-sta-dkms`. On success, verifies `lsmod | grep ^wl` and reports `Broadcom WiFi activated (wl module loaded)` or `driver installed but wl module did not load — reboot required`.
USB Broadcom (`0a5c` vendor, `lsusb`) is detected but **not auto-installed**: the script shows a message that Linux lacks native drivers for most USB Broadcom WiFi chips and that `ndiswrapper` may be needed.
> **Historical note:** Earlier drafts of this document described a 3-tier system (`firmware-brcm80211` → `firmware-b43-installer` → `broadcom-sta-dkms`). The current script has consolidated to the DKMS path only. The other firmware packages are still available via the base `firmware-linux-nonfree` metapackage and vendor-specific packages (`firmware-realtek`, etc.).
This tiered approach ensures that even if one method fails (e.g., proprietary driver compilation errors), the system attempts other supported methods to restore network functionality.
## 6. Bluetooth Stack Integration
Bluetooth support is handled through a dedicated module (`bluetooth.sh`) that integrates seamlessly with the firmware detection process:
Bluetooth support is handled through a dedicated module (`bluetooth.sh`) that reuses the firmware module's device detection:
* **Hardware Detection:** The script identifies both PCI and USB Bluetooth controllers using `PCI_BT_DEVS` and `USB_BT_DEVS` arrays, ensuring comprehensive coverage of all Bluetooth hardware types.
* **Base Stack Installation:** When Bluetooth hardware is detected, the system installs the core stack (`bluez`, `bluez-utils`, `bluez-obexd`) if not already present.
* **Desktop Environment Optimization:** Based on the detected desktop environment:
* **KDE:** Installs `bluedevil` and optionally `pipewire-pulse` + `wireplumber` for Pipewire audio server integration.
* **GNOME:** Uses built-in GNOME Bluetooth support in `gnome-control-center`.
* **XFCE/Other:** Installs `blueman` as the GTK Bluetooth manager.
* **Service Management:** The script ensures the Bluetooth service is enabled and started automatically on boot, with session restart or reboot recommendation for desktop applets to load properly.
* **Hardware Detection:** The script identifies both PCI and USB Bluetooth controllers via the `PCI_BT_DEVS` and `USB_BT_DEVS` arrays, covering all Bluetooth hardware types.
* **Base Stack Installation:** When Bluetooth hardware is detected, the system installs the core stack (`bluez`, `bluez-utils`, `bluez-obexd`) if not already present.
* **Desktop Environment Optimization:** Based on the detected desktop environment:
* **KDE:** Installs `bluedevil` and optionally `pipewire-pulse` + `wireplumber` for Pipewire audio server integration.
* **GNOME:** Uses built-in GNOME Bluetooth support in `gnome-control-center`.
* **XFCE/Other:** Installs `blueman` as the GTK Bluetooth manager.
* **Service Management:** The script enables and starts the Bluetooth service on boot, and recommends a session restart or reboot so desktop applets load properly.
Bluetooth handling stays separate from network firmware; both share the same device detection arrays.
This modular approach keeps Bluetooth handling separate from network firmware while maintaining tight integration through shared device detection arrays and coordinated installation flow.
+26 -26
View File
@@ -1,10 +1,10 @@
# Option 8: Gaming Ecosystem, Performance Tweaks & Runtimes
## Option 7: Gaming Ecosystem, Performance Tweaks & Runtimes
### 1. Philosophy of the Gaming Environment in Debian
The Debianito gaming module turns a plain Debian installation into a gaming station. It isolates the gaming dependencies from the base system and configures the compatibility layers without putting system stability at risk. The design follows three principles:
The Debianito gaming module transforms a server-grade Debian installation into a dedicated gaming station by carefully isolating dependencies and configuring compatibility layers without compromising system stability. The core philosophy follows three principles:
**Dependency Isolation:** All gaming-specific packages (Steam, Heroic, Mesa 32-bit libraries) are installed in user-space where possible, preventing conflicts with base system packages. The `steam-installer` package from Debian's contrib section is used as the bootstrap mechanism rather than Valve's official repository for stability reasons. This ensures compatibility with Debian's package management and reduces update complexity.
**Dependency Isolation:** All gaming-specific packages (Steam, Heroic, Mesa 32-bit libraries) are installed in user-space where possible, preventing conflicts with base system packages. The `steam-installer` package from Debian's contrib section is used as the bootstrap mechanism rather than Valve's official repository for stability reasons—this ensures compatibility with Debian's package management and reduces update complexity.
**Compatibility Layer Configuration:** Proton/Wine support is enabled through automatic installation of required 32-bit libraries (`libgl1-mesa-dri:i386`, `mesa-vulkan-drivers:i386`) that are mandatory for running Windows-only games on Linux. The module detects GPU architecture and installs appropriate drivers (NVIDIA proprietary, AMD open-source, Intel built-in) before attempting any launcher installation.
@@ -16,16 +16,15 @@ The module architecture separates concerns into distinct scripts (`steam.sh`, `h
### 2. Launchers and Compatibility Layers (Steam & Heroic)
**Steam Installation Logic:** The `install_steam()` function in `steam.sh` uses Debian's native package management through the `apt install -y steam-installer` command. This approach differs from Valve's official repository for several reasons:
**Steam Installation Logic:** The `install_steam()` function in `steam.sh` leverages Debian's native package management through the `apt install -y steam-installer` command. This approach differs from Valve's official repository for several reasons:
1. **32-bit Architecture Requirement:** Steam requires 32-bit libraries to run modern Windows games via Proton. The script explicitly prompts users to enable Multi-Arch support (`dpkg --add-architecture i386`) and install the complete Mesa stack for both amd64 and i386 architectures:
```bash
apt install mesa-vulkan-drivers libglx-mesa0:i386 mesa-vulkan-drivers:i386 \
libgl1-mesa-dri:i386 libegl-mesa0:i386 mesa-va-drivers:i386
```
2. **Runtime Dependency Chain:** The `steam-installer` package depends on several components:
2. **Runtime Dependency Chain:** The `steam-installer` package depends on several critical components:
- `debconf` and `cdebconf` for configuration management
- `default-dbus-session-bus` for inter-process communication
- `lsof` for file listing utilities
@@ -52,7 +51,7 @@ This approach ensures users always get the latest stable release while maintaini
### 3. Optimization Engine and Telemetry (GameMode & MangoHud)
**Feral GameMode Daemon:** When a game launches with `gamemoderun`, the Feral GameMode daemon performs several optimizations at the kernel level:
**Feral GameMode Daemon:** When a game launches with `gamemoderun`, the Feral GameMode daemon performs several critical optimizations at the kernel level:
1. **CPU Scheduler Priority:** Adjusts scheduling policies to prioritize gaming processes over background tasks, reducing latency spikes during gameplay
2. **I/O Priority Management:** Increases I/O priority for game processes while deprioritizing non-essential system operations (updates, backups)
@@ -60,7 +59,6 @@ This approach ensures users always get the latest stable release while maintaini
4. **Screensaver Inhibition:** Prevents screensavers from activating during gaming sessions
The `install_gamemode()` function in `tools.sh` ensures the daemon is available system-wide:
```bash
_run_install gamemode
```
@@ -75,7 +73,6 @@ This allows users to wrap game launch commands with `gamemoderun %command%` for
4. **Native Rendering:** Uses Vulkan/OpenGL hooks for efficient overlay rendering without impacting game performance
The `install_mangohud()` function handles both 64-bit and 32-bit installations:
```bash
if dpkg --print-foreign-architectures | grep -q i386; then
echo "Installing 32-bit MangoHud..."
@@ -83,7 +80,7 @@ if dpkg --print-foreign-architectures | grep -q i386; then
fi
```
The `goverlay` component extends this functionality by integrating with Wayland compositors for smoother overlay behavior on modern desktop environments. The two tools need no game-specific configuration.
The `goverlay` component extends this functionality by integrating with Wayland compositors for smoother overlay behavior on modern desktop environments. Both tools work together to provide comprehensive performance visibility without requiring game-specific configuration.
---
@@ -97,7 +94,6 @@ The `goverlay` component extends this functionality by integrating with Wayland
4. **Capability Assignment:** Sets raw I/O capabilities on the binary using `setcap cap_sys_rawio=ep /usr/bin/openrgb`, allowing direct hardware communication while maintaining user-space execution
The script includes version-specific download URLs for Debian Bookworm (12) and Trixie (13), ensuring compatibility with different kernel versions:
```bash
if [ "$DEBIAN_VERSION" = "12" ]; then
url="https://codeberg.org/OpenRGB/OpenRGB/releases/download/release_candidate_1.0rc2/openrgb_1.0rc2_amd64_bookworm_0fca93e.deb"
@@ -110,32 +106,32 @@ This approach eliminates the security risk of running OpenRGB as root while main
---
### 5. Java Runtimes (Eclipse Temurin 8 / 17 / 21 / 25)
### 5. Java Runtimes (Eclipse Temurin 8 / 17 / 21)
**Multi-Version Support:** The gaming module provides four specific Eclipse Temurin versions to accommodate different game requirements:
**Multi-Version Support:** The gaming module provides three specific Eclipse Temurin versions to accommodate different game requirements:
| Version | Use Case | Justification |
| --------- | ---------- | --------------- |
|---------|----------|---------------|
| **Temurin 8** | Legacy Minecraft mods, older Java games | Maintains compatibility with mods written for Java 8 (2014-2019 era) |
| **Temurin 17** | Modern Minecraft servers, newer game clients | Balances performance and compatibility for post-1.16+ game versions |
| **Temurin 21** | Latest game engines, cutting-edge mods | Provides best performance for modern Java applications |
| **Temurin 25** | Minecraft 26+ and latest game engines | Best performance for the newest Java applications |
**Repository Management via extrepo:** The script uses the `extrepo` utility to manage external repositories instead of injecting repository URLs into system files by hand. This approach offers several advantages:
**Repository Management via extrepo:** The script leverages the `extrepo` utility to manage external repositories cleanly rather than manually injecting repository URLs into system files. This approach offers several advantages:
1. **Automated Keyring Handling:** `extrepo` manages GPG keys and source file configurations automatically, eliminating manual intervention with `/etc/apt/sources.list.d/`.
2. **Dependency Resolution:** The utility checks for its own installation and handles the enabling of the Adoptium Temurin repository before proceeding with package installation.
3. **Maintenance Safety:** Updates to the upstream repository are reflected through `extrepo` without requiring direct edits to system configuration files, reducing the risk of breakage during OS updates.
1. **Automated Keyring Handling:** `extrepo` manages GPG keys and source file configurations automatically, eliminating manual intervention with `/etc/apt/sources.list.d/`.
2. **Dependency Resolution:** The utility checks for its own installation and handles the enabling of the Adoptium Temurin repository before proceeding with package installation.
3. **Maintenance Safety:** Updates to the upstream repository are reflected through `extrepo` without requiring direct edits to system configuration files, reducing the risk of breakage during OS updates.
**Version Selection Logic:** Users can choose which Temurin version to install based on their specific game requirements via a TUI menu (`install_minecraft_java()`). The module offers all four versions because:
**Version Selection Logic:** Users can choose which Temurin version to install based on their specific game requirements via a TUI menu (`install_minecraft_java()`). The module justifies offering all three versions because:
1. **Backward Compatibility:** Java 8 remains in use by many Minecraft mods and older game clients that haven't been updated for newer JVMs
2. **Performance Optimization:** Java 21 provides the best performance characteristics for modern games with heavy multithreading requirements
3. **Security Updates:** All Temurin versions receive regular security patches from the Eclipse Foundation community
1. **Backward Compatibility:** Java 8 remains in use by many Minecraft mods and older game clients that haven't been updated for newer JVMs
2. **Performance Optimization:** Java 21 provides the best performance characteristics for modern games with heavy multithreading requirements
3. **Security Updates:** All Temurin versions receive regular security patches from the Eclipse Foundation community
The install avoids multiple conflicting JRE installations. It installs `extrepo` if it is missing, enables the Adoptium source and then installs the chosen packages (e.g., `temurin-8-jre`, `temurin-17-jre`, `temurin-25-jre`).
The installation process ensures clean repository management without polluting the system with multiple conflicting JRE installations, maintaining Debian's package integrity while providing flexibility for different gaming scenarios. It automatically installs `extrepo` if not present and enables the Adoptium source before proceeding with version-specific packages (e.g., `temurin-8-jre`, `temurin-17-jre`).
### References
### References:
- [https://wiki.debian.org/Steam](https://wiki.debian.org/Steam)
- [wiki.archlinux.org/title/Steam](wiki.archlinux.org/title/Steam)
@@ -147,4 +143,8 @@ The install avoids multiple conflicting JRE installations. It installs `extrepo`
- [https://github.com/flightlessmango/Mangohud](https://github.com/flightlessmango/Mangohud)
- [https://github.com/feralinteractive/gamemode](https://github.com/feralinteractive/gamemode)
- [https://gitlab.com/CalcProgrammer1/OpenRGB](https://gitlab.com/CalcProgrammer1/OpenRGB)
- [https://adoptium.net/en/installation/linux](https://adoptium.net/en/installation/linux)
- [https://adoptium.net/es/installation/linux](https://adoptium.net/es/installation/linux)
+92 -131
View File
@@ -1,29 +1,27 @@
# Option 6: Graphics Drivers, Mesa Stack & Display Architecture
## Option 5: Graphics Drivers, Mesa Stack & Display Architecture
### 1. Philosophy of the Graphics Stack (Open-Source vs. Proprietary)
The `debianito` script prefers the open-source drivers that ship with the Linux kernel and injects proprietary ones only where performance or legacy support requires it. The work is split across three layers:
The `debianito` script adopts a **hybrid-first architecture philosophy**. It prioritizes the stability and security of the Linux kernel's native open-source drivers while maintaining the capability to inject proprietary solutions where necessary for performance or legacy support. This approach is implemented through three distinct layers:
1. **DRM/KMS & Mesa (Open-Source Core)**: For Intel and AMD hardware, the script relies on the `i915`/`amdgpu` kernel drivers (KMS) paired with the `Mesa` user-space stack. This ensures that graphics acceleration is handled by the mainline Linux kernel without requiring third-party blobs or external repositories for basic functionality. The script explicitly installs the necessary Gallium3D drivers (`radeonsi`, `iris`) and Vulkan implementations (`RADV`).
2. **Proprietary Injection (NVIDIA)**: For NVIDIA hardware, the open-source Nouveau driver is often insufficient for gaming or compute workloads. The script manages the installation of proprietary `.run` or DKMS modules via official NVIDIA repositories. This requires careful handling to ensure compatibility with the running kernel version, especially when using backports kernels.
3. **Firmware & Microcode**: A prerequisite layer handled by `firmware.sh`. Before any driver can load, the correct firmware blobs (e.g., `iwlwifi`, `amdgpu`, `nvidia`) must be present in `/lib/firmware`. The script scans hardware via `lspci` and `lsusb` to populate a dynamic installation plan for these non-free components.
1. **DRM/KMS & Mesa (Open-Source Core)**: For Intel and AMD hardware, the script relies on the `i915`/`amdgpu` kernel drivers (KMS) paired with the `Mesa` user-space stack. This ensures that graphics acceleration is handled by the mainline Linux kernel without requiring third-party blobs or external repositories for basic functionality. The script explicitly installs the necessary Gallium3D drivers (`radeonsi`, `iris`) and Vulkan implementations (`RADV`).
2. **Proprietary Injection (NVIDIA)**: For NVIDIA hardware, the open-source Nouveau driver is often insufficient for gaming or compute workloads. The script manages the installation of proprietary `.run` or DKMS modules via official NVIDIA repositories. This requires careful handling to ensure compatibility with the running kernel version, especially when using backports kernels.
3. **Firmware & Microcode**: A critical prerequisite layer handled by `firmware.sh`. Before any driver can load, the correct firmware blobs (e.g., `iwlwifi`, `amdgpu`, `nvidia`) must be present in `/lib/firmware`. The script scans hardware via `lspci` and `lsusb` to populate a dynamic installation plan for these non-free components.
Users on Debian Stable get compatibility by default, while advanced users can opt into backports kernels or the NVIDIA enterprise repository without breaking the base system.
This philosophy ensures that users on standard Debian Stable releases get maximum compatibility, while advanced users can opt into backports kernels or enterprise NVIDIA repositories without breaking the base system integrity.
---
### 2. The Automatic GPU Detection Pipeline
The script runs a pre-flight detection sequence defined in `utils.sh` and executed in `gpu.sh`. It categorizes the hardware before showing installation options, so the user answers fewer questions.
The script utilizes a robust pre-flight detection sequence defined in `utils.sh` and executed within `gpu.sh`. This pipeline minimizes user interaction by automatically categorizing hardware before presenting installation options.
**Detection Flow:**
1. **Hardware Scanning**: The function `detect_gpu()` executes `lspci -nn | grep -E "VGA|3D"`. It parses the output using `sed` and `grep` to identify vendor IDs (e.g., `8086` for Intel, `10de` for NVIDIA).
2. **Variable State**: Global variables are populated immediately:
* `GPU_TYPE`: Set to `"intel"`, `"amd"`, or `"nvidia"`. If no GPU is found, it defaults to `"unknown"` (common in VMs or headless servers).
* `INTEL_GPU_DEVICE_ID` / `NVIDIA_GPU_DEVICE_ID`: Hexadecimal device IDs extracted for precise generation matching.
3. **Logic Branching**: Inside `install_gpu_drivers()`, the script checks these variables:
1. **Hardware Scanning**: The function `detect_gpu()` executes `lspci -nn | grep -E "VGA|3D"`. It parses the output using `sed` and `grep` to identify vendor IDs (e.g., `8086` for Intel, `10de` for NVIDIA).
2. **Variable State**: Global variables are populated immediately:
* `GPU_TYPE`: Set to `"intel"`, `"amd"`, or `"nvidia"`. If no GPU is found, it defaults to `"unknown"` (common in VMs or headless servers).
* `INTEL_GPU_DEVICE_ID` / `NVIDIA_GPU_DEVICE_ID`: Hexadecimal device IDs extracted for precise generation matching.
3. **Logic Branching**: Inside `install_gpu_drivers()`, the script checks these variables:
```bash
if [ "$GPU_TYPE" = "unknown" ]; then
# Install generic Mesa stack (Safe fallback)
@@ -35,12 +33,11 @@ The script runs a pre-flight detection sequence defined in `utils.sh` and execut
# Route to AMD-specific logic (amdgpu/radeonsi)
install_amd_firmware && offer_amd_tools
```
4. **Hybrid Support**: For laptops with hybrid graphics (e.g., Intel iGPU + NVIDIA dGPU), the script detects both `HAS_INTEL=true` and `HAS_NVIDIA=true`. It executes a sequential plan:
* Install Intel firmware/drivers first to ensure the base display server works.
* Install NVIDIA drivers second, configuring them for PRIME offloading if detected.
4. **Hybrid Support**: For laptops with hybrid graphics (e.g., Intel iGPU + NVIDIA dGPU), the script detects both `HAS_INTEL=true` and `HAS_NVIDIA=true`. It executes a sequential plan:
* Install Intel firmware/drivers first to ensure the base display server works.
* Install NVIDIA drivers second, configuring them for PRIME offloading if detected.
This detect-then-deploy order keeps users from installing unnecessary drivers (e.g., `i965` on an RTX 4090) and puts the firmware in place before drivers are installed.
This "detect-then-deploy" model prevents users from installing unnecessary drivers (e.g., `i965` on an RTX 4090) and ensures that critical firmware is present before the driver installation phase begins.
---
@@ -51,9 +48,9 @@ This detect-then-deploy order keeps users from installing unnecessary drivers (e
| **Gen4** (Broadwater) | 65nm | GMA X4500, GMA X4500HD | `i915` | `i915` | Not supported | Predecessor to HD Graphics. Very limited support. The i915 DRI driver is the original, now obsolete. |
| **Gen5** (Ironlake) | 32nm | HD Graphics (Westmere/Arrandale) | `i915` | `crocus` | Not supported | First generation "HD Graphics". OpenGL up to 3.3 only. Legacy driver `i965` was **removed** in Mesa 24.1, so `crocus` is now the only option. |
| **Gen6** (Sandy Bridge) | 32nm | HD Graphics 2000/3000 | `i915` | `crocus` | Not supported | Significant performance improvement. Maximum OpenGL 3.3. |
| **Gen7** (Ivy Bridge) | 22nm | HD Graphics 2500/4000 | `i915` | `crocus` | `ANV/HASVK` [(incomplete/broken)](https://www.reddit.com/r/vulkan/s/Hnf5zU8WZY) | First Gen at 22nm. `crocus` is the recommended OpenGL driver. Vulkan is exposed but completely non-conformant (0.0.0.0 ), lacks basic features (e.g., texture swizzle), and is [unusable for real-world tasks](https://www.phoronix.com/news/Intel-HasVK-Drop-Dead-Code). |
| **Gen7** (Ivy Bridge) | 22nm | HD Graphics 2500/4000 | `i915` | `crocus` | `ANV/HASVK` [(incomplete/broken)](https://www.reddit.com/r/vulkan/s/Hnf5zU8WZY) | First Gen at 22nm. `crocus` is the recommended OpenGL driver. Vulkan is exposed but completely non-conformant (0.0.0.0 ), lacks basic features (e.g., texture swizzle), and is [unusable for real-world tasks](https://www.phoronix.com/news/Intel-HasVK-Drop-Dead-Code).|
| **Gen7** (Bay Trail) | 22nm | HD Graphics (Bay Trail) | `i915` | `crocus` | `ANV`[(incomplete)](https://lists.debian.org/debian-user/2023/07/msg00550.html) | `conformanceVersion = 0.0.0.0` Support is experimental up to Vulkan 1.2, lacks basic extensions, and may cause instability. The separate HASVK driver exists but is not used on this system. |
| **Gen7.5** (Haswell) | 22nm | HD Graphics 4600, Iris Pro 5200 | `i915` | `crocus` | `hasvk` | Vulkan support via community driver `hasvk` (Vulkan 1.3). |
| **Gen7.5** (Haswell) | 22nm | HD Graphics 4600, Iris Pro 5200 | `i915` | `crocus` | `hasvk` | Vulkan support via community driver `hasvk` (Vulkan 1.3). |
| **Gen8** (Broadwell) | 14nm | HD Graphics 5300, Iris Pro 6200, Iris 6100 | `i915` | `iris` | `hasvk` | First generation at 14nm, `iris` becomes the main OpenGL driver. |
| **Gen9** (Skylake) | 14nm | HD Graphics 530, Iris 540/550 | `i915` | `iris` | `anv` | Mature architecture with strong Linux support. Major performance boost for iGPU. |
| **Gen9.5** (Kaby Lake, Coffee Lake, Comet Lake) | 14nm+ / 14nm++ | UHD Graphics 620/630, UHD 610/630 | `i915` | `iris` | `anv` | Process node optimization for 14nm. "UHD" replaces "HD" in naming convention. |
@@ -67,19 +64,19 @@ This detect-then-deploy order keeps users from installing unnecessary drivers (e
---
#### Intel Details
#### Intel Details:
1. **Kernel Driver Transition (`i915` to `xe`)**: The [`i915`](https://www.kernel.org/doc/html/v4.9/gpu/i915.html) driver is reaching its scalability limits. [Xe](https://www.kernel.org/doc/html/v6.8/gpu/rfc/xe.html) is the path for modern hardware, though it still requires forcing and is under development, it already shows significant improvements in various areas.
2. **Mesa Drivers (User Space)**:
* **OpenGL**:
* **Legacy Hardware (Gen5-Gen8)**: The classic `i965` driver was **officially removed from Mesa in version 24.1**. **[`crocus`](https://www.phoronix.com/news/Intel-Crocus-Default-Gallium3D)** (Gallium3D) is the only active driver for this legacy hardware.
* **Modern Hardware (Gen9 and Xe)**: **`iris`** is the standard driver. It works excellently on both iGPUs and Arc dGPUs (Alchemist/Battlemage).
* **Vulkan**:
* **Old Hardware about Ivy Bridge and Bay Trail (Gen7)**: Although the ANV/HASVK drivers expose these GPUs as Vulkan devices (reporting API versions as high as 1.2 or 1.3), their state is completely non-compliant (conformanceVersion = 0.0.0.0). The support is purely theoretical, it lacks basic hardware features (e.g., texture swizzle on Ivy Bridge) and is unstable or unusable for real-world applications. Because of this, in Mesa 22.3, the Gen7/Gen8 Vulkan code was separated from the main driver (ANV) and moved to the legacy HASVK driver to avoid hindering the development of modern hardware. You can read the technical details of this decision [here](https://www.phoronix.com/news/Intel-HASVK-Old-Vulkan-Gen7-8).
* **Legacy Hardware (Gen7.5 - Gen8)**: Uses **[`hasvk`](https://www.phoronix.com/news/Intel-ANV-HASVK-Split-Merged)**, a community-maintained driver (not directly by Intel engineers), offering Vulkan 1.2? on 2013-era hardware.
1. **Kernel Driver Transition (`i915` to `xe`)**: The [`i915`](https://www.kernel.org/doc/html/v4.9/gpu/i915.html) driver is reaching its scalability limits. [Xe](https://www.kernel.org/doc/html/v6.8/gpu/rfc/xe.html) is the path for modern hardware, though it still requires forcing and is under development, it already shows significant improvements in various areas.
2. **Mesa Drivers (User Space)**:
* **OpenGL**:
* **Legacy Hardware (Gen5-Gen8)**: The classic `i965` driver was **officially removed from Mesa in version 24.1**. **[`crocus`](https://www.phoronix.com/news/Intel-Crocus-Default-Gallium3D)** (Gallium3D) is the only active driver for this legacy hardware.
* **Modern Hardware (Gen9 and Xe)**: **`iris`** is the standard driver. It works excellently on both iGPUs and Arc dGPUs (Alchemist/Battlemage).
* **Vulkan**:
* **Old Hardware about Ivy Bridge and Bay Trail (Gen7)**: Although the ANV/HASVK drivers expose these GPUs as Vulkan devices (reporting API versions as high as 1.2 or 1.3), their state is completely non-compliant (conformanceVersion = 0.0.0.0). The support is purely theoretical, it lacks basic hardware features (e.g., texture swizzle on Ivy Bridge) and is unstable or unusable for real-world applications. Because of this, in Mesa 22.3, the Gen7/Gen8 Vulkan code was separated from the main driver (ANV) and moved to the legacy HASVK driver to avoid hindering the development of modern hardware. You can read the technical details of this decision [here](https://www.phoronix.com/news/Intel-HASVK-Old-Vulkan-Gen7-8).
* **Legacy Hardware (Gen7.5 - Gen8)**: Uses **[`hasvk`](https://www.phoronix.com/news/Intel-ANV-HASVK-Split-Merged)**, a community-maintained driver (not directly by Intel engineers), offering Vulkan 1.2? on 2013-era hardware.
*Additionally, in early 2024, the compiler code shared between `iris` and `anv` for [Gen8](https://www.phoronix.com/news/Intel-Mesa-Splitting-Gen8) was also isolated, following the same principle: to enable faster development for modern hardware without breaking Broadwell support*.
* **Modern Hardware (Gen9+)**: **[`anv`](https://docs.mesa3d.org/drivers/anv.html)** is Intel's official driver. On recent hardware (Gen12+, Arc) it reaches the **Vulkan 1.4 standard**.
3. **New Hardware Support Status**: Support for very recent iGPUs (such as Lunar Lake and Panther Lake) often requires very recent versions of the Linux kernel (6.8/6.11 branch or higher) and Mesa library (24.2+), plus updated firmware (`linux-firmware`).
* **Modern Hardware (Gen9+)**: **[`anv`](https://docs.mesa3d.org/drivers/anv.html)** is Intel's official driver. On recent hardware (Gen12+, Arc) it reaches the **Vulkan 1.4 standard**.
3. **New Hardware Support Status**: Support for very recent iGPUs (such as Lunar Lake and Panther Lake) often requires very recent versions of the Linux kernel (6.8/6.11 branch or higher) and Mesa library (24.2+), plus updated firmware (`linux-firmware`).
---
@@ -92,53 +89,31 @@ AMD's open-source support is divided by architecture families, each mapped to a
| **TeraScale 1**<br>*(R600/R700)* | Radeon HD 2000, HD 3000, HD 4000 | `radeon` | `r600` | **Not Applicable** | Starting point of the `r600` driver in Mesa. Supports up to OpenGL 3.3. Architecture is completely obsolete, only useful for very basic 2D/3D desktop graphics. |
| **TeraScale 2**<br>*(Evergreen)* | Radeon HD 5000, HD 6000<br>*(and some low-end HD 7000)* | `radeon` | `r600` | **Not Applicable** | Last evolution of TeraScale. OpenGL support stalled at version 3.3. **No Vulkan support exists or will exist** due to hardware architecture limitations. |
| **TeraScale 3**<br>*(Northern Islands)* | Radeon HD 6000, HD 7000 (low-end) | `radeon` | `r600` | **Not Applicable** | Intermediate architecture between TeraScale 2 and GCN. OpenGL support remains at 3.3. **No Vulkan support**. Last generation before the jump to GCN. |
| **GCN 1.0 / 1.1**<br>*(gfx6 / gfx7)* | Radeon HD 7700, R7 200, R9 200/300 | `radeon` (default)<br>`amdgpu` (forced) | `radeonsi` | `RADV` (Vulkan 1.3) | First generation 28nm processor with Vulkan support. The kernel loads the `radeon` module by default. The script injects the system parameters to use `RADV`. |
| **GCN 1.0 / 1.1**<br>*(gfx6 / gfx7)* | Radeon HD 7700, R7 200, R9 200/300 | `radeon` (default)<br>`amdgpu` (forced) | `radeonsi` | `RADV` (Vulkan 1.3) | **⚠️ Requires manual configuration:** The kernel loads `radeon` by default. To use `radeonsi`/`RADV`, pass to the kernel: `amdgpu.si_support=1 amdgpu.cik_support=1 radeon.si_support=0 radeon.cik_support=0`. |
| **GCN 3.0**<br>*(gfx8 / gfx8.1)* | Radeon R9 285, R9 Fury X, R9 Nano | `amdgpu` | `radeonsi` | `RADV` (Vulkan 1.4) | Third generation of GCN, direct predecessor to Polaris. Introduces efficiency improvements and initial support for Vulkan 1.3. |
| **GCN 4.0**<br>*(Polaris, gfx8.0)* | Radeon RX 400, RX 500, Radeon Pro WX | `amdgpu` | `radeonsi` | `RADV` (Vulkan 1.4) | First generation to use the `amdgpu` KMD natively and by default without tricks. Sweet spot for stability of older hardware in current Linux. No Ray Tracing hardware support. |
| **GCN 5.0**<br>*(Vega, gfx9)* | Radeon RX Vega, Radeon VII, APUs Raven Ridge | `amdgpu` | `radeonsi` | `RADV` (Vulkan 1.4) | Last GCN generation. Significant improvements to the `radeonsi` driver for this hardware. On Vega, using "Override" in RADV sometimes improves performance over default shader cache. |
| **RDNA 1**<br>*(gfx10)* | Radeon RX 5000 | `amdgpu` | `radeonsi` | `RADV` (Vulkan 1.4) | Architectural jump. Introduces Variable Rate Shading (VRS) support. Mesa drivers quickly achieved performance parity with the proprietary Windows driver on this generation. |
| **RDNA 2**<br>*(gfx10.3)* | Radeon RX 6000, Steam Deck (Van Gogh) | `amdgpu` | `radeonsi` | `RADV` (Vulkan 1.4) | First generation with hardware **Ray Tracing** in AMD. In Mesa, this is handled through the `VK_KHR_ray_tracing_pipeline` extension. This architecture is in the Steam Deck, which massively accelerated RADV development. |
| **RDNA 3**<br>*(gfx11)* | Radeon RX 7000 | `amdgpu` | `radeonsi` | `RADV` (Vulkan 1.4) | Introduces **Mesh Shaders** in AMD hardware ([`VK_EXT_mesh_shader`](https://github.com/KhronosGroup/Vulkan-Docs/blob/main/proposals/VK_EXT_mesh_shader.adoc) extension). Requires a relatively recent Linux kernel (6.1+) for complete and stable graphics controller support. |
| **RDNA 3.5**<br>*(gfx11.5)* | APUs Strix Point/Halo, Krackan Point, Gorgon Halo | `amdgpu` | `radeonsi` | `RADV` (Vulkan 1.4) | Intermediate update to RDNA 3. Shares many features with RDNA 3 (gfx11). Support in drivers (kernel, Mesa, LLVM) is integrated as part of the GFX11 family. |
| **RDNA 4**<br>*(gfx12)* | Radeon RX 9000 | `amdgpu` | `radeonsi` | `RADV` (Vulkan 1.4) | Latest generation to date. RADV jumps to full support for **Vulkan 1.4** across the entire GFX8+ line (GCN 3 onwards). **Current context:** AMD has officially discontinued their other open Vulkan driver (AMDVLK), leaving
| **RDNA 3.5**<br>*(gfx11.5)* | APUs Strix Point/Halo, Krackan Point, Gorgon Halo| `amdgpu` | `radeonsi` | `RADV` (Vulkan 1.4) | Intermediate update to RDNA 3. Shares many features with RDNA 3 (gfx11). Support in drivers (kernel, Mesa, LLVM) is integrated as part of the GFX11 family. |
| **RDNA 4**<br>*(gfx12)* | Radeon RX 9000 | `amdgpu` | `radeonsi` | `RADV` (Vulkan 1.4) | Latest generation to date. RADV jumps to full support for **Vulkan 1.4** across the entire GFX8+ line (GCN 3 onwards). **Current context:** AMD has officially discontinued their other open Vulkan driver (AMDVLK), leaving
#### AMD GCN Migration via GRUB Parameters
#### Additional notes & carifications
For older AMD GCN 1.0/1.1 GPUs (`gfx6`/`gfx7`), the script forces the modern `amdgpu` driver via kernel parameters injected into `GRUB_CMDLINE_LINUX_DEFAULT`. The migration is handled in `gpu.sh:_force_amdgpu()` with a **duplicate guard** to prevent repeated parameter injection:
```bash
local params="radeon.si_support=0 radeon.cik_support=0 amdgpu.si_support=1 amdgpu.cik_support=1"
# Duplicate guard — skip if already present
if grep -q "amdgpu.si_support=1" "$file" 2>/dev/null; then
_msg "AMD GCN" "amdgpu parameters already present in GRUB.\nNo changes made." 8 50
return
fi
# Insert params at the beginning of GRUB_CMDLINE_LINUX_DEFAULT
if ! sudo grep -q "amdgpu.si_support=1" "$file"; then
sudo sed -i -E "/^GRUB_CMDLINE_LINUX_DEFAULT=/ s/\"([^\"]*)\"/\"${params} \1\"/" "$file"
fi
```
The guard checks both `grep -q` (read-only) and `sudo grep -q` (with elevated permissions) to handle systems where `/etc/default/grub` requires root access. If the parameters are already present, the function exits early with a message rather than duplicating the entry.
#### Additional notes & clarifications
1. **Regarding GCN Nomenclature**: Names like "GCN 1.0", "1.1", "1.2" were created by the press as a convenient abbreviation, since AMD only started officially numbering their GCN revisions (gen 1 to 4) later. The table now uses more standard terminology.
2. **Relationship Between Drivers and Architectures**:
* The `radeonsi` driver (OpenGL) and `RADV` (Vulkan) are siblings within the Mesa 3D project. Both depend on the [`amdgpu` kernel](https://docs.kernel.org/gpu/amdgpu/index.html).
* The old [`radeon`](https://wiki.freedesktop.org/xorg/radeon/) driver (for TeraScale) is incompatible with modern Mesa drivers (`radeonsi`/`RADV`).
* **Important milestone**: Starting from Linux kernel 6.19, the `amdgpu` driver will include support for older generations of [AMD GPUs](https://wiki.gentoo.org/wiki/AMDGPU) (such as TeraScale and early GCN) that were previously only supported by the `radeon` driver, unifying support.
3. **Vulkan Support in RADV**: Mesa documentation indicates that [RADV supports Vulkan 1.4 for all GFX8 GPUs (GCN 3 onwards) and newer](https://docs.mesa3d.org/drivers/radv.html#supported-hardware). This covers RDNA 4 as well as RDNA 3 and RDNA 3.5 architectures.
4. **RDNA 3.5 Status**: It is an intermediate update that shares the architectural base of RDNA 3 (gfx11). The identifiers `gfx1150` and `gfx1151` correspond to this generation. Support in Mesa drivers and the kernel has been integrated progressively.
1. **Regarding GCN Nomenclature**: Names like "GCN 1.0", "1.1", "1.2" were created by the press as a convenient abbreviation, since AMD only started officially numbering their GCN revisions (gen 1 to 4) later. The table now uses more standard terminology.
2. **Relationship Between Drivers and Architectures**:
* The `radeonsi` driver (OpenGL) and `RADV` (Vulkan) are siblings within the Mesa 3D project. Both depend on the [`amdgpu` kernel](https://docs.kernel.org/gpu/amdgpu/index.html).
* The old [`radeon`](https://wiki.freedesktop.org/xorg/radeon/) driver (for TeraScale) is incompatible with modern Mesa drivers (`radeonsi`/`RADV`).
* **Important milestone**: Starting from Linux kernel 6.19, the `amdgpu` driver will include support for older generations of [AMD GPUs](https://wiki.gentoo.org/wiki/AMDGPU) (such as TeraScale and early GCN) that were previously only supported by the `radeon` driver, unifying support.
3. **Vulkan Support in RADV**: Mesa documentation indicates that [RADV supports Vulkan 1.4 for all GFX8 GPUs (GCN 3 onwards) and newer](https://docs.mesa3d.org/drivers/radv.html#supported-hardware). This includes RDNA 3 and RDNA 3.5 architectures, not just RDNA 4.
4. **RDNA 3.5 Status**: It is an intermediate update that shares the architectural base of RDNA 3 (gfx11). The identifiers `gfx1150` and `gfx1151` correspond to this generation. Support in Mesa drivers and the kernel has been integrated progressively.
---
### 5. **NVIDIA Hardware & Driver Support**
#### Nvidia Legacy (Fermi to Pascal)
#### Nvidia Legacy (Fermi to Pascal):
These generations depend **exclusively** on the proprietary driver and closed stack. There is no support for the open kernel module nor NVK.
| Architecture | Last Driver with Support | Kernel Module (KMD) | Proprietary Vulkan Support | NVK (Mesa) Support | Max CUDA Version | Notes and Particularities |
@@ -150,8 +125,7 @@ These generations depend **exclusively** on the proprietary driver and closed st
---
#### Nvidia: modern era and "Open Source" (Turing to Blackwell)
#### Nvidia: modern era and """Open Source""" (Turing to Blackwell):
Starting with Turing, NVIDIA introduced the **open kernel module**. From driver 525, this module is the default. Additionally, it's the range where community driver **NVK** (in Mesa) shines.
| Architecture | Compatible Active Drivers | Kernel Module (KMD) | Proprietary Vulkan Support | NVK (Mesa) Support | Max CUDA Version | Notes and Particularities |
@@ -161,11 +135,10 @@ Starting with Turing, NVIDIA introduced the **open kernel module**. From driver
| **Ada Lovelace**<br>*(AD100/102/103/104)* | 525.xx to 610+ | `nvidia` (**Open Module**) | **Vulkan 1.3** | **Yes** (Vulkan 1.3) | **CUDA 12.8+** | NVK added full Ada support recently. Proprietary driver still required if hardware Ray Tracing or DLSS 3 (Frame Generation) is needed, as NVK does not yet implement these proprietary extensions. |
| **Blackwell**<br>*(GB100/102/202)* | [570.xx](https://docs.nvidia.com/datacenter/tesla/tesla-release-notes-570-211-01/index.html) to 610+ | `nvidia` (**Open Module**) | **Vulkan 1.3** | **Yes** (In development) | **CUDA 12.8+** | Latest generation architecture (RTX 5090/5080). NVK support is landing in the most recent kernel versions (6.12+) and Mesa (24.3+). Requires very updated `linux-firmware`. |
#### Quick context glossary
* **Open Module:** Starting with driver 515, [NVIDIA releases code that interacts directly with the Linux kernel under MIT/GPL license](https://developer.nvidia.com/blog/nvidia-releases-open-source-gpu-kernel-modules/). However, the GPU still requires loading a proprietary closed microcode called **GSP (GPU System Processor)** to boot.
* **NVK:** It is the open-source Vulkan driver developed by Red Hat and the community, integrated into the [Mesa project](https://docs.mesa3d.org/drivers/nvk.html). It's the 100% free alternative to `libGLX_nvidia.so`. Does not require NVIDIA proprietary driver installed to function (only kernel firmware).
* **CUDA Drop:** When NVIDIA removes an architecture from new drivers (e.g., Pascal in 555), the CUDA version used by that GPU freezes forever (in this case, CUDA 12.0). Cannot run applications compiled for CUDA 12.1 or higher.
#### 💡 Quick context glossary for documentation:
* **Open Module:** Starting with driver 515, [NVIDIA releases code that interacts directly with the Linux kernel under MIT/GPL license](https://developer.nvidia.com/blog/nvidia-releases-open-source-gpu-kernel-modules/). However, the GPU still requires loading a proprietary closed microcode called **GSP (GPU System Processor)** to boot.
* **NVK:** It is the open-source Vulkan driver developed by Red Hat and the community, integrated into the [Mesa project](https://docs.mesa3d.org/drivers/nvk.html). It's the 100% free alternative to `libGLX_nvidia.so`. Does not require NVIDIA proprietary driver installed to function (only kernel firmware).
* **CUDA Drop:** When NVIDIA removes an architecture from new drivers (e.g., Pascal in 555), the CUDA version used by that GPU freezes forever (in this case, CUDA 12.0). Cannot run applications compiled for CUDA 12.1 or higher.
---
@@ -174,64 +147,56 @@ Starting with Turing, NVIDIA introduced the **open kernel module**. From driver
When the script detects Intel or AMD hardware, it triggers a specific installation sequence designed to maximize [API support](https://mesamatrix.net/) (OpenGL/Vulkan/VA-API).
**Core Components Installed:**
* **`libgl1-mesa-dri`**: Provides the core OpenGL implementation for 64-bit systems. The script ensures `libgl1-mesa-dri:i386` is included if Wine or legacy applications are required, preventing architecture mismatches.
* **`mesa-vulkan-drivers`**: Installs `RADV` (AMD) and `anv` (Intel). This enables Vulkan 1.2/1.3 support on modern hardware.
* **`va-driver-all` / `vdpau-va-driver`**: Ensures video decoding acceleration is available for media players like VLC or MPV.
* **`libgl1-mesa-dri`**: Provides the core OpenGL implementation for 64-bit systems. The script ensures `libgl1-mesa-dri:i386` is included if Wine or legacy applications are required, preventing architecture mismatches.
* **`mesa-vulkan-drivers`**: Installs `RADV` (AMD) and `anv` (Intel). This enables Vulkan 1.2/1.3 support on modern hardware.
* **`va-driver-all` / `vdpau-va-driver`**: Ensures video decoding acceleration is available for media players like VLC or MPV.
**Vendor-Specific Logic:**
| Vendor | Kernel Driver (KMD) | Mesa User-Space Driver | VAAPI Backend Strategy |
| :--- | :--- | :--- | :--- |
| **Intel Gen < 8** | `i915` | `crocus` / `iris` | Installs [`i965-va-driver-shaders`](https://github.com/intel/intel-vaapi-driver/). Legacy path for older CPUs. |
| **Intel Gen 8+** | `i915` | `iris` / `anv` | Installs [`intel-media-va-driver-non-free`](https://github.com/intel/media-driver). Modern, preferred backend for Broadwell+. |
| **Intel Gen 8+** | `i915` | `iris` / `anv` | Installs [`intel-media-va-driver-non-free](https://github.com/intel/media-driver). Modern, preferred backend for Broadwell+. |
| **AMD GCN/RDNA** | `amdgpu` | `radeonsi` (GL) + `RADV` (VK) | Uses standard `va-driver-all`. Requires kernel param tuning for older GCN. |
**Consistency Check:**
**Critical Consistency Check:**
The script enforces the installation of 32-bit Mesa libraries (`libgl1-mesa-dri:i386`) alongside the 64-bit packages. This is a mandatory requirement for running Proton (Steam) and Wine applications, which often rely on 32-bit OpenGL contexts even when running on a 64-bit OS.
---
### 7.**NVIDIA Driver Management & Kernel Compatibility**
The NVIDIA driver installation involves proprietary components, kernel version constraints, and DKMS (Dynamic Kernel Module Support) compilation. This section covers the choice between stable and backports kernels, Blackwell GPUs via the CUDA repository (production branches v590 / v595), and the warnings the script shows for compatibility issues.
The NVIDIA driver installation process is inherently complex due to proprietary components, kernel version constraints, and DKMS (Dynamic Kernel Module Support) module compilation. This section outlines how the script navigates these challenges by distinguishing between stable and backports kernels, handling Blackwell architecture GPUs via CUDA v590, and providing appropriate warnings for potential compatibility issues.
#### **Kernel Compatibility**
- **Stable Kernels**: Use `linux-image-amd64`. Compatible with standard NVIDIA `.deb` packages (e.g., `nvidia-driver-535`) and DKMS modules.
- **Backports Kernels**: Detected via `is_backports_kernel()`. Newer kernels may cause DKMS compilation failures due to driver version lag. The script warns users or suggests using the NVIDIA enterprise repository or manual header compilation (`linux-headers-$(uname -r)`).
* **Stable Kernels**: Use `linux-image-amd64`. Compatible with standard NVIDIA `.deb` packages (e.g., `nvidia-driver-535`) and DKMS modules.
* **Backports Kernels**: Detected via `is_backports_kernel()`. Newer kernels may cause DKMS compilation failures due to driver version lag. The script warns users or suggests using the NVIDIA enterprise repository or manual header compilation (`linux-headers-$(uname -r)`).
#### **Blackwell Architecture & CUDA v590/v595 (Production Branches)**
* **Detection**: `_helpers.sh` function `is_nvidia_blackwell()` identifies GPUs via PCI IDs `10de:24xx`, `0x2900–0x29BF`, and `0x2B80–0x31FF`. The same check gates both CUDA branches: Blackwell is unsupported by Debian stable regardless of the selected CUDA version.
* **Reason for v590/v595**: Debian 13 (Trixie) stable drivers only support up to v550, which lacks Blackwell (GB20x) architecture. The NVIDIA CUDA repository provides two production branches with unified driver packages: **v590**(current long-lived production, [590.48.01](https://download.nvidia.com/XFree86/Linux-x86_64/590.48.01/README/supportedchips.html)) and **v595** ([595.91.07](https://download.nvidia.com/XFree86/Linux-x86_64/595.91.07/README/supportedchips.html)newer production branch, same GB20x support with more recent fixes/features). Both branches expose the same unified metapackage and replace the need for a distro-packaged driver on Blackwell hardware.
* **CUDA Repository Enablement**: On Debian 13 (Trixie), the only release that exposes `590` and `595` (`_show_nvidia_version_menu` gates them behind `DEBIAN_VERSION == 13`), the script enables the `nvidia-cuda` repository via `extrepo` (`sudo apt install -y extrepo` if missing, then `sudo extrepo enable nvidia-cuda`). After enabling, the script always runs an explicit `apt update`, then installs `nvidia-driver-pinning-<ver>` if available (official NVIDIA pinning, optional and version-specific for 590 vs 595), and finally installs the unified metapackage `nvidia-open` plus `firmware-nvidia-gsp` (pulled as a transitive dependency). No manual pinning file is written; if `apt install` fails, the real `apt` error is shown and the installation aborts.
#### **Blackwell Architecture & CUDA v590**
- **Detection**: `_helpers.sh` function `is_nvidia_blackwell()` identifies GPUs via PCI IDs `10de:24xx`, `0x2900–0x29BF`, and `0x2B80–0x31FF`.
- **Reason for v590**: Debian 13 (Trixie) stable drivers only support up to v550, which lacks Blackwell (GB20x) architecture. The NVIDIA CUDA repository provides production branch v590 with unified driver packages. Specifically, the goal is to install the latest version of the nvidia-driver from the 590 branch, which would be [590.48.01](https://download.nvidia.com/XFree86/Linux-x86_64/590.48.01/README/supportedchips.html).
- **Extrepo Mechanism**: Enables `nvidia-cuda` repository via `extrepo`, creates APT pinning in `/etc/apt/preferences.d/block-nvidia` to lock to version `590.*`, and installs `nvidia-driver-pinning-590`, `nvidia-driver`, and `firmware-nvidia-gsp`.
#### **Installation Flow**
The NVIDIA flow is driven by `_install_nvidia_stack` (dispatcher in `modules/gpu.sh`):
1. **Plan**: `_msg` with detected GPUs, then `_pause`.
2. **Manage menu** (`_nvidia_manage_menu`): "Install / Change NVIDIA Driver Version" or "Remove NVIDIA Driver and Restore Nouveau".
3. **Version menu** (`_show_nvidia_version_menu`): Debian 12 → v535 (Recommended) / v470 (Kepler); Debian 13 → v550 (Recommended) / v590 / v595 (NVIDIA CUDA Repo).
4. **Dispatch by hardware**: Kepler → `nvidia-tesla-470-driver`; Fermi → veto with message; Turing+ → `_install_nvidia_standard` (Debian stable); Blackwell on Trixie → CUDA repo v590.
5. **CUDA repo (590/595)**: two separate APT transactions: `nvidia-driver-pinning-<ver>` (version lock, mandatory) then `nvidia-open` (driver + open kernel modules, firmware via transitive dependency).
6. **Secure Boot warning**: If Secure Boot is active (`mokutil --sb-state` reports `SecureBoot enabled`), the script displays a warning that the NVIDIA DKMS module is compiled but **NOT signed**. The user must sign the module with MOK or disable Secure Boot in BIOS before rebooting:
```
WARNING: Secure Boot is enabled. The NVIDIA DKMS module is compiled but NOT signed.
You MUST sign the module with MOK or disable Secure Boot in BIOS before rebooting.
```
Installation continues: Secure Boot does not block DKMS compilation, but the module will fail to load until signed.
7. **Post-install**: `/etc/modprobe.d/nvidia-wayland.conf` (written with `|| return 1` protection) + DKMS verification.
> Note: Debian 12 (Bookworm) backports reached EOL (2026-08-09); the NVIDIA flow on Bookworm only uses the stable repository.
```bash
if [ "$HAS_NVIDIA" = true ]; then
if [ "$(is_backports_kernel)" == "true" ] && \
{ [ "$DEBIAN_CODENAME" != "trixie" ] || ! is_nvidia_blackwell; }; then
# Warn about DKMS compatibility or use enterprise repo
offer_nvidia_enterprise_repo
fi
if _confirm "NVIDIA Driver"; then
install_nvidia_driver # Installs latest stable (535/550) or v590 for Blackwell
fi
fi
```
---
### 9.**NVIDIA Driver Compatibility Matrix**
### 8.**NVIDIA Driver Management & Kernel Compatibility**
Depending on your GPU generation and your Debian ecosystem, you must select the appropriate legacy or current driver series. The table below lists the verified compatibility across Debian versions and GPU generations:
Depending on your GPU generation and your Debian ecosystem, you must select the appropriate legacy or current driver series. The following table details the verified compatibility matrix across different Debian versions and hardware architectures:
| Driver NVIDIA | Debian Version | Supported Architectures (Generations) | Notes |
| :--- | :--- | :--- | :--- |
@@ -240,17 +205,14 @@ Depending on your GPU generation and your Debian ecosystem, you must select the
| **[535.247.01](https://us.download.nvidia.com/XFree86/Linux-x86_64/535.247.01/README/supportedchips.html)** | **Debian 12** (Bookworm) | Maxwell to Ada Lovelace | Kepler support dropped completely. Standard for RTX 3000/4000 series. |
| **[550.163.01](https://us.download.nvidia.com/XFree86/Linux-x86_64/550.163.01/README/supportedchips.html)** | **Debian 13** (Trixie) | Maxwell to Ada Lovelace | Current stable standard. Blackwell not officially supported yet. |
#### **Hardware Notes:**
* **Kepler (GeForce vs. Quadro)**: The last driver supporting true Kepler architecture is version **470**. If a user has a GTX 680 or similar, they must stay on Debian 11 or use the 470 driver branch in Bookworm/Trixie manually.
* **Fermi (GTX 400/500)**: Support ended with driver 390. These GPUs are incompatible with modern kernels and drivers beyond Debian 11.
* **Volta (Titan V / V100)**: Excellent longevity, supported from 390 through 550+.
* **Blackwell (RTX 5000)**: Not officially supported by standard Debian drivers yet. The script provides a path to the Enterprise Repo for users who need this hardware to function immediately.
#### **Critical Hardware Notes:**
* **Kepler (GeForce vs. Quadro)**: The last driver supporting true Kepler architecture is version **470**. If a user has a GTX 680 or similar, they must stay on Debian 11 or use the 470 driver branch in Bookworm/Trixie manually.
* **Fermi (GTX 400/500)**: Support ended with driver 390. These GPUs are incompatible with modern kernels and drivers beyond Debian 11.
* **Volta (Titan V / V100)**: Excellent longevity, supported from 390 through 550+.
* **Blackwell (RTX 5000)**: Not officially supported by standard Debian drivers yet. The script provides a path to the Enterprise Repo for users who need this hardware to function immediately.
#### **Kepler Interception in Bookworm:**
When Kepler is detected on Bookworm, `nvidia.sh` forces installation of `nvidia-tesla-470-driver` (the modern v535/v550 branches do not support Kepler):
When Kepler is detected on Bookworm, `nvidia.sh` bypasses `nvidia-detect` (which might recommend v535) and forces installation of `nvidia-tesla-470-driver`:
```bash
if [ "$is_kepler" = "true" ] && [ "$DEBIAN_CODENAME" = "bookworm" ]; then
nv_pkg="nvidia-tesla-470-driver"
@@ -260,22 +222,21 @@ fi
---
### 10. Performance Monitoring & Telemetry Tools
### 9. Performance Monitoring & Telemetry Tools
To check that the graphics stack works, `gpu.sh` offers an optional installation of telemetry tools. These allow users to verify GPU utilization, memory usage, and codec support post-installation.
To ensure the graphics stack is functioning correctly, `gpu.sh` offers an optional installation of telemetry tools. These allow users to verify GPU utilization, memory usage, and codec support post-installation.
* **Universal ([`nvtop`](https://github.com/Syllo/nvtop))**:
* A cross-platform tool that displays real-time metrics for NVIDIA, AMD, and Intel GPUs in a terminal interface (similar to `htop`).
* **Debian 11 Constraint**: In Debian 11 Bullseye, `nvtop` support is limited primarily to NVIDIA GPUs. The script warns users of this limitation on older releases.
* **AMD Specific ([`radeontop`](https://github.com/clbr/radeontop))**:
* Provides detailed metrics for AMD GPUs (GPU usage, memory utilization, power consumption). Essential for verifying that the `amdgpu` driver is active and not falling back to software rendering.
* **Intel Specific ([`intel-gpu-tools`](https://github.com/ChrisCummins/intel-gpu-tools))**:
* Only installed if the detected Intel hardware supports it (Gen 6+). Provides information on GPU usage via `inotify` or `/sys/class/drm`.
* **Codec Verification ([`vainfo`](https://github.com/intel/libva-utils))**:
* The script runs `vainfo` to verify that VAAPI is correctly configured. This confirms whether the system can use hardware acceleration for video decoding (e.g., H.264, HEVC) via Intel QuickSync or AMD Video Core Plus.
* **Universal ([`nvtop`](https://github.com/Syllo/nvtop))**:
* A cross-platform tool that displays real-time metrics for NVIDIA, AMD, and Intel GPUs in a terminal interface (similar to `htop`).
* **Debian 11 Constraint**: In Debian 11 Bullseye, `nvtop` support is limited primarily to NVIDIA GPUs. The script warns users of this limitation on older releases.
* **AMD Specific ([`radeontop`](https://github.com/clbr/radeontop))**:
* Provides detailed metrics for AMD GPUs (GPU usage, memory utilization, power consumption). Essential for verifying that the `amdgpu` driver is active and not falling back to software rendering.
* **Intel Specific ([`intel-gpu-tools`](https://github.com/ChrisCummins/intel-gpu-tools))**:
* Only installed if the detected Intel hardware supports it (Gen 6+). Provides information on GPU usage via `inotify` or `/sys/class/drm`.
* **Codec Verification ([`vainfo`](https://github.com/intel/libva-utils))**:
* The script runs `vainfo` to verify that VAAPI is correctly configured. This confirms whether the system can utilize hardware acceleration for video decoding (e.g., H.264, HEVC) via Intel QuickSync or AMD Video Core Plus.
**Installation Command Logic:**
```bash
if _confirm "Install Telemetry Tools"; then
case "$GPU_TYPE" in
@@ -287,4 +248,4 @@ if _confirm "Install Telemetry Tools"; then
fi
```
Users can run these tools right after `debianito` to confirm that the graphics stack works.
This modular approach ensures that users can verify their installation immediately after running `debianito`, providing confidence in the performance of their graphics stack.
+51 -126
View File
@@ -1,152 +1,77 @@
# Option 7: Kernel Variants (Stable, Backports, RT, Cloud)
## Option 6: Debian Backports Kernel Integration
## 1. What Does This Component Do?
### 1. Why a Backports Kernel?
The **Kernel** module (`modules/kernel.sh`) manages which Linux kernel your system boots. Unlike a simple `apt install`, it enforces **atomic installation of image + headers** and validates repository state before touching the boot chain. The menu (`show_kernel_menu`) offers four distinct kernel flavours plus a Backports path that is only available on Debian 13 (Trixie):
The decision to integrate the Debian Backports kernel into `debianito.sh` is driven by the fundamental architectural conflict between **Stability** and **Hardware Enablement**.
| Key | Variant | Package | Use Case |
| ----- | --------- | --------- | ---------- |
| `stable` | Stable | `linux-image-amd64` | Default Debian kernel (6.12 LTS on Trixie). Maximum stability. |
| `backports` | Backports | `linux-image-amd64` from `trixie-backports` | Newer kernel (e.g., 7.x) for recent hardware. **Trixie only**, requires backports enabled. |
| `rt` | Real-Time | `linux-image-rt-amd64` | Preempt-RT kernel for low-latency / audio production. Warns on NVIDIA. |
| `cloud` | Cloud | `linux-image-cloud-amd64` | Minimal kernel for VMs, containers, and cloud images. |
Debian Stable (including Debian 13 "Trixie") prioritizes long-term reliability. As a result, its kernel version is frozen at a Long Term Support (LTS) release—in this case, Linux 6.12 LTS. While 6.12 is robust and secure, it represents a snapshot of the upstream kernel from late 2024/early 2025. It does not include the rapid stream of hardware enablement, scheduler refinements, or power management optimizations that occur in subsequent releases (e.g., Linux 7.0+).
All variants install the matching **headers** package (`linux-headers-amd64`, `linux-headers-rt-amd64`, `linux-headers-cloud-amd64`) in the same transaction, which matters for DKMS modules (NVIDIA, VirtualBox, ZFS).
For users with modern hardware released between 2025 and 2026, this freeze creates a compatibility gap:
* **New Architectures:** CPUs like Intel Arrow Lake/Panther Lake or AMD Zen 5 require specific microcode, scheduler hints (e.g., "slow workload hints"), and CXL support that are absent in the frozen 6.12 LTS branch.
* **Graphics Performance:** New GPUs (Intel Battlemage) may lack optimized power states (like D3cold enablement) or improved driver integration found in newer kernels.
* **Filesystem Integrity:** Advanced features like XFS self-healing or Btrfs remap-tree improvements are exclusive to newer kernel versions.
> **Position in menu:** This is Option 7 in the current `debianito.sh` main menu. Previous documentation listed it as Option 6 (Backports only). The module was expanded to support RT and Cloud kernels after the initial release.
The `kernel.sh` module leverages the Debian Backports repository (`trixie-backports`) as a "best-effort" bridge. This allows users to opt-in to Kernel 7.0+ without abandoning the Stable base entirely. The script ensures that this upgrade is treated as an exception, providing access to modern enablement while maintaining the safety net of the Stable ecosystem for core system packages.
---
### 2. Synchronized Installation Pipeline (Kernel + Headers)
## 2. Why Offer Backports at All?
A critical engineering principle in kernel management is **Atomicity**. Installing a new kernel image without its corresponding headers breaks the build chain for third-party modules (such as NVIDIA DKMS, VirtualBox, or ZFS). The `install_kernel_backports` function enforces this by ensuring the installation command targets both components.
Debian Stable freezes its kernel at an LTS release (6.12 LTS on Trixie). This is intentional for reliability, but creates a hardware enablement gap for machines released in 2025-2026:
- **New CPUs** (Intel Arrow Lake / Panther Lake, AMD Zen 5) need scheduler hints, CXL, and microcode not in 6.12
- **New GPUs** (Intel Battlemage D3cold, NVIDIA Blackwell) need power-state and firmware support added after 6.12
- **Filesystem fixes** (XFS self-healing, Btrfs remap-tree) land only in newer kernels
The `trixie-backports` repository provides a **best-effort newer kernel** without moving the rest of the system to Testing. Debian backports kernels receive security updates but are not LTS themselves.
This is why the menu shows `backports` only when `DEBIAN_VERSION == 13` and greys it out otherwise. Bookworm backports is intentionally not offered: its EOL was 2026-08-09 and the NVIDIA path on Bookworm no longer uses backports.
---
## 3. The Installation Pipeline: Image + Headers Atomically
### Function: `_install_kernel_package` (`kernel.sh`)
**The Installation Command Logic:**
The script utilizes `apt` with a specific target release flag to pull packages from the backports suite:
```bash
_install_kernel_package "linux-image-amd64" "Backports" "-t trixie-backports"
_install_kernel_package "linux-image-rt-amd64" "RT" ""
sudo apt install -y -t ${DEBIAN_CODENAME}-backports linux-image-amd64
```
Execution steps:
While Debian's dependency resolver often pulls headers automatically when installing `linux-image`, explicit documentation and engineering best practices dictate that the system must be configured to ensure both are present. The pipeline operates as follows:
1. **Availability check**: `apt-cache show <pkg>` must succeed. If the package does not exist for the current Debian version, a whiptail message `Kernel not available` is shown and the function returns.
2. **Hardware warnings** (flavour-specific):
- **Backports + NVIDIA** (`GPU_TYPE == "nvidia"`): `_confirm "Kernel" "WARNING: Backports kernel changes the kernel version. Your NVIDIA driver will need recompilation (DKMS)."`. Users can still proceed: DKMS will rebuild on next boot if headers match.
- **RT + NVIDIA**: `_msg "Kernel — RT" "Ensure your NVIDIA driver supports the RT kernel. Some proprietary drivers may not work correctly."`. Not a blocker, but informs that some closed drivers fail with PREEMPT_RT.
3. **Version resolution** for the confirmation dialog:
1. **Target Specification (`-t`):** The flag `-t ${DEBIAN_CODENAME}-backports` explicitly directs APT to ignore the Stable repository for this specific transaction, ensuring the latest backported version is selected rather than a cached Stable package.
2. **Image Package:** `linux-image-amd64` contains the bootable kernel binary and associated modules.
3. **Headers Dependency:** Although often implicit, the documentation mandates that `linux-headers-amd64` must be present for DKMS drivers to recompile successfully after a reboot. If these are missing, external drivers may fail to load until manually rebuilt against the new headers.
```bash
# Backports path
ver=$(apt-cache madison linux-image-amd64 | grep trixie-backports | awk '{print $3}' | head -1)
headers_ver=$(apt-cache madison linux-headers-amd64 | grep trixie-backports | awk '{print $3}' | head -1)
# Stable/RT/Cloud path
ver=$(apt-cache show linux-image-amd64 | sed -n 's/^Version: //p' | grep -v '~bpo' | head -1)
```
This synchronized approach ensures that when the system boots into the new kernel, all dependent modules have access to the correct symbol tables and build environment provided by the matching headers.
The dialog shows `Image: linux-image-amd64 (6.12.22-1)` + `Headers: linux-headers-amd64 (6.12.22-1)` + `From: Trixie-backports` when a backports flag is present.
4. **User confirmation**: `whiptail --yesno "Install Backports kernel? Image: ... Headers: ..."` with the version string. Declining aborts with `Skipping.`.
5. **Atomic install**:
### 3. Safety Mechanisms and Atomic Operation
```bash
sudo apt install -y [-t trixie-backports] linux-image-amd64 linux-headers-amd64
sudo apt install -y linux-image-rt-amd64 linux-headers-rt-amd64
sudo apt install -y linux-image-cloud-amd64 linux-headers-cloud-amd64
```
To prevent boot loops or system instability, `kernel.sh` implements several safety checks before executing any installation commands:
Both image and headers are passed in a **single `apt` transaction**. This keeps the symbol tables matched so DKMS can rebuild. The helper `_run_cmd "Kernel"` prints the command, captures the exit code, and pauses for the user to review output.
6. **Post-install**: prints `Backports kernel installed. Reboot to use it.` and pauses. The new kernel is added to `/boot` alongside the old one; GRUB will show both at next boot. The script does not remove the old kernel; rollback is simply rebooting into the previous entry.
* **Pre-flight Repository Validation:**
The function begins with a strict check using `is_backports_enabled()`. If the backports repository is not active in `/etc/apt/sources.list`, the script halts and instructs the user to enable it via Option 3. This prevents accidental dependency conflicts or installation failures due to missing sources.
---
* **Hardware Compatibility Warnings:**
The script detects if an NVIDIA GPU is present (`GPU_TYPE == "nvidia"`). In this scenario, a warning is displayed: *"WARNING: may break NVIDIA driver."*. This alerts the user that proprietary drivers might require DKMS recompilation against the new headers.
## 4. Menu Logic: `show_kernel_menu`
* **Bootloader Update (GRUB):**
Although not explicitly shown in the minimal `kernel.sh` snippet provided, standard kernel engineering practice dictates that after a successful installation, the bootloader must be updated to register the new entry:
```bash
sudo update-grub
```
This ensures the new kernel appears in the GRUB menu and can be set as the default.
```bash
show_kernel_menu() {
while true; do
items=("stable" "Install linux-image-amd64")
[ "$DEBIAN_VERSION" = "13" ] && items+=("backports" "Install from backports")
items+=("rt" "Install linux-image-rt-amd64 (Preempt-RT)")
items+=("cloud" "Install linux-image-cloud-amd64")
items+=("back" "Return to main menu")
choice=$(whiptail --menu "Kernel Installation" "Select kernel variant:" 16 65 5 "${items[@]}")
* **Fallback Preservation:**
The script does not remove the previous kernel. Debian's package manager retains older kernels, preserving them in `/boot`. If the new backports kernel fails to boot (e.g., due to a hardware incompatibility), the user can simply select the previous stable version from the GRUB menu during startup. This "Rollback Safety" is inherent to the Debian Stable model and is reinforced by the script's non-destructive installation approach.
case "$choice" in
stable) _install_kernel_package "linux-image-amd64" "Stable" "" ;;
backports)
if [ "$(is_backports_enabled)" != "true" ]; then
whiptail --msgbox "Backports repository is not enabled.\nUse option 4 (Configure repositories) to enable backports before installing."
else
_install_kernel_package "linux-image-amd64" "Backports" "-t ${DEBIAN_CODENAME}-backports"
fi ;;
rt) _install_kernel_package "linux-image-rt-amd64" "RT" "" ;;
cloud) _install_kernel_package "linux-image-cloud-amd64" "Cloud" "" ;;
back) break ;;
esac
done
}
```
### 4. Critical Interconnection with Other Modules (Script Ecosystem)
Key details:
The Backports Kernel module does not operate in isolation; it relies on a tightly coupled ecosystem within `debianito.sh` to ensure full functionality:
- **Backports visibility**: The `backports` entry is only appended when `DEBIAN_VERSION == 13`. On Bullseye/Bookworm it does not appear.
- **Backports guard**: Selecting `backports` without backports enabled shows a message pointing to **Option 4 (Configure Repositories)**. No install is attempted.
- **Loop**: The menu is a `while true` loop; the user can install multiple variants sequentially (e.g., Stable + RT) before returning with `back` or `ESC`.
* **Option 4: Firmware & Wireless Drivers:**
New kernels often introduce support for new hardware IDs, but they require corresponding firmware blobs (e.g., `firmware-misc-nonfree`). If the user installs Kernel 7.0+ without updating their firmware repository, wireless cards or specific storage controllers may remain unfunctional. The script ensures that Option 4 is logically dependent on a compatible kernel state.
---
* **Option 5: Graphics Drivers (NVIDIA DKMS):**
For systems with NVIDIA hardware, the installation of a new kernel triggers a dependency chain for `nvidia-dkms`. If the user has proprietary drivers installed, they must be recompiled against the new headers provided by the backports kernel. The script's detection logic (`HAS_NVIDIA`) allows it to warn users or trigger DKMS rebuilds automatically if integrated into a larger workflow.
## 5. Safety Mechanisms
* **Bullseye-Specific Logic:**
As seen in `debianito.sh`, the Backports Kernel module is conditionally loaded based on the Debian version:
```bash
if [ "$DEBIAN_VERSION" = "11" ]; then
_msg "Not Available" ...
else
install_kernel_backports || true
fi
```
This ensures that legacy systems (Debian 11 Bullseye) do not attempt to use a backports workflow that may not be supported or stable in older architectures, while newer versions (Bookworm/Trixie) utilize the full feature set.
| Mechanism | How It Works |
| ----------- | -------------- |
| **Pre-flight `is_backports_enabled`** | `utils.sh:is_backports_enabled` greps `/etc/apt/sources.list` and `/etc/apt/sources.list.d/*.sources` / `*.list` for `trixie-backports`. Prevents `apt -t` from failing with `E: Release not found`. |
| **NVIDIA RT warning** | `utils.sh:GPU_TYPE` is set on demand by `detect_gpu` (lspci, via `_ensure_state_detected`). RT kernels change scheduling semantics; some `nvidia.ko` builds reject `PREEMPT_RT`. |
| **Atomic image+headers** | Both packages in one `apt` call. If headers are missing, `dkms` will fail at boot, so they are never installed separately. |
| **Fallback preservation** | `apt` never removes the running kernel. `/boot` retains `vmlinuz-*` and `initrd.img-*` for both. GRUB keeps both entries; if the new kernel panics, select the old one. |
| **Version-aware messaging** | The confirmation dialog always shows the exact version string (`apt-cache madison` / `apt-cache show`) so users know they are not reinstalling the same package. |
GRUB update is **not** explicitly called: `linux-image-*` postinst triggers `update-grub` (or `kernel-install` on systemd-boot) automatically. If GRUB is broken, use **Option 12 (Boot Rescue + GRUB)** to rebuild it.
---
## 6. Interconnection with Other Modules
- **Option 4: Repositories**: Must enable `trixie-backports` before `Kernel → backports` is usable. The kernel menu explicitly checks `is_backports_enabled` and directs the user there if missing.
- **Option 5: Firmware**: Newer kernels expose new hardware IDs (e.g., `8086:7e40` for Panther Lake). Without updated `firmware-linux-nonfree` or `firmware-iwlwifi`, the new kernel will show the device but fail to load firmware. Run Firmware after a kernel upgrade if WiFi/storage is not recognized.
- **Option 6: Graphics Drivers**: NVIDIA `dkms` needs the exact `linux-headers-*` version. The atomic install ensures headers match. On Bookworm the NVIDIA path deliberately avoids backports kernels; on Trixie a backports kernel + Maxwell/Pascal GPU is forced to the stable NVIDIA driver (`v550`) to avoid `v590` incompatibility.
- **Gaming (`gaming.sh`)**: The backports kernel is sometimes recommended for gaming (scheduler latency, `SCHED_EXT`), but not required. Benchmarks show <3% difference for most titles; enable it only for newer hardware that needs it.
---
## 7. Verification After Installation
```bash
uname -r # Should show the new kernel after reboot
dpkg -l linux-image-amd64 | grep ^ii
dpkg -l linux-headers-amd64 | grep ^ii
ls -l /boot/vmlinuz-* # Both old and new kernels present
grep -E 'menuentry' /boot/grub/grub.cfg | head -5 # GRUB entries
```
If the new kernel fails to boot, hold `ESC` (or `Shift` on BIOS) at power-on, select **Advanced options for Debian** → previous kernel version.
---
## References
- [Debian Backports: backports.debian.org](https://backports.debian.org/)
- [Debian Kernel Handbook](https://kernel-handbook.alioth.debian.org/)
- [PREEMPT_RT: wiki.debian.org/RealTime](https://wiki.debian.org/RealTime)
- [Installing a new kernel: wiki.debian.org/DebianKernel](https://wiki.debian.org/DebianKernel)
* **Gaming & Extras:**
The Backports kernel is often recommended for gaming due to improved scheduler performance and low-latency networking features found in Linux 7.0+. By linking Option 6 with `gaming.sh`, users can ensure their hardware is tuned correctly before launching high-performance applications.
-330
View File
@@ -1,330 +0,0 @@
# Debianito: Quick Start Visual Guide
> **Scope:** Fresh Debian 11 / 12 / 13 installation → fully configured desktop in ~15-20 minutes.
> **Prerequisites:** Normal user with `sudo` access. Do **not** run the script as root.
> **Related:** [README Overview](../README.md) · [System Info](system_info.md) · [Repositories](repos_config.md) · [Firmware](firmware.md) · [GPU](gpu.md) · [Kernel](kernel.md) · [Gaming](gaming.md)
---
## Before You Start
```bash
git clone https://github.com/stornic56/debianito-post-install
cd debianito-post-install
chmod +x debianito.sh && ./debianito.sh
```
The script automatically checks for `whiptail` and `lsb-release`, installs them if missing, verifies `sudo` access, checks network connectivity, and synchronizes the system clock (`systemd-timesyncd` + `tzdata`).
> ![Script](/media/gift/script.gif)
With no flags the script runs interactively. Three flags change that (full table in the [README](../README.md#command-line-usage)):
```bash
./debianito.sh --yes # auto-confirm prompts; menus still display
./debianito.sh --yes --allow-destructive # also unblock branch migration
./debianito.sh --help # show usage and exit
```
`--yes` auto-confirms prompts, but the menus still display and still need arrow keys and ENTER. By itself it does not reach branch migration (menu `4 [ADVANCED] Upgrade system branch`): that operation can leave the system unbootable, so the script reports that `--allow-destructive` is required. `DEBIANITO_PROFILE=1 ./debianito.sh` stamps each boot step to stderr with a timestamp, which makes before/after comparisons possible. An unknown option exits with status 2.
Package installs are batched behind a single confirmation: the script lists what it is about to install (the first 15 package names, plus a count of the rest) and asks you to proceed. With `--yes` these confirmations are accepted automatically.
---
## Recommended Order
Each step builds on the previous one. Steps 1-6 are the essential path; steps 7-10 are optional.
### Step 1: Know Your System
**Menu:** `1 System Information`
Opens a whiptail message box populated by `utils.sh` detection functions (`detect_cpu_ram`, `detect_gpu`, `detect_network`, `detect_storage`, `detect_displayserver`, `detect_desktop_environment`).
You will see:
- Debian version and codename
- CPU model and RAM size
- GPU vendor(s) and device IDs (e.g., `10de:2684` for NVIDIA)
- Network adapters (Ethernet + WiFi chipset) and their state/IP
- Storage topology (NVMe / SSD / HDD / USB-SD)
- Display server (Wayland / X11 / tty) and desktop environment
Remember the **WiFi chipset** for the firmware mapping in Step 5, and the **GPU line** (Intel / AMD / NVIDIA) for the drivers in Step 6.
> ![Script](/media/screenshots/01-system-info.png) System Information dialog with hardware summary.
### Step 2: Fix Permissions Early (Optional but Recommended)
**Menu:** `2 User Privileges & Feedback`
A sub-menu with four toggles:
| Option | What It Does | When to Enable |
| -------- | -------------- | ---------------- |
| Sudo group membership | `usermod -aG sudo $USER` | Fresh install where the first user is not in `sudo` |
| Passwordless sudo | Creates `/etc/sudoers.d/$USER-nopasswd` with `NOPASSWD` for `apt`, `systemctl`, etc. | Lab / personal machine; skip on shared systems |
| Repair home ownership | `chown -R $USER:$USER $HOME` | Home files owned by root after a mishandled `sudo` |
| Sudo password feedback | Writes `Defaults pwfeedback` to `/etc/sudoers.d/pwfeedback` (typing then shows `****`) | **Enable now** to avoid typos during the many installs that follow |
> ![Script](/media/screenshots/02-user-privileges.png) User Privileges & Feedback menu.
> ![Script](/media/screenshots/02b-pwfeedback.png) Confirmation dialog for "Sudo Password Feedback" with asterisk preview.
### Step 3: System Preferences (Optional)
**Menu:** `3 System Preferences`
```
1 Date, Time & Timezone
2 Language, Locales & Keyboard
3 Audio & Sound Stack
4 Back to main menu
```
- **Date, Time & Timezone**: Runs `dpkg-reconfigure tzdata` and then `_ensure_time_synced` (NTP + `systemd-timesyncd`). Fix timezone before `apt update`: a wrong clock breaks GPG verification.
- **Language, Locales & Keyboard**: Runs `dpkg-reconfigure locales` and `keyboard-configuration`. Requires re-login to apply `LANG`.
- **Audio & Sound Stack**: Checklist with `pipewire-audio` (or `pipewire` on Bullseye), `alsa-utils`, `pavucontrol` (hidden on TTY), `pulsemixer`, `playerctl`. PipeWire on Trixie offers a Backports vs Stable choice and installs Bluetooth Hi-Res codecs (`LDAC`, `aptX`, `AAC`). See [System Preferences](system_prefs.md).
> ![Script](/media/screenshots/03-system-prefs.png) System Preferences menu.
### Step 4: Configure Repositories (Required)
**Menu:** `4 Configure Repositories`
```
1 Enable Contrib & Non-Free Components
2 Migrate traditional sources.list to DEB822 format
3 Setup/Update Backports repositories
4 [ADVANCED] Upgrade system branch (Testing / SID)
5 Back to main menu
```
1. Choose **1. Enable Contrib & Non-Free**: adds `contrib`, `non-free`, `non-free-firmware` (Bookworm/Trixie). Required for firmware, NVIDIA drivers, and Steam (`contrib`). The script handles both `/etc/apt/sources.list` (classic) and `/etc/apt/sources.list.d/debian.sources` (DEB822) and validates via `apt update` with automatic rollback.
2. Choose **3. Setup/Update Backports**: answers `Yes` to enable `trixie-backports` / `bookworm-backports` in a separate file (`debian-backports.sources` or `.list`). Backports gives newer kernels, Mesa, and firmware. See [Configure Repositories](repos_config.md).
⚠️ Without `non-free` and `contrib`, Steps 5 and 8 will fail. Do not skip.
> ![Script](/media/screenshots/04-repos.png) Repositories menu.
> ![Script](/media/screenshots/04b-backports.png) Backports confirmation dialog.
### Step 5: Install Firmware & Wireless Drivers
**Menu:** `5 Firmware, Wireless & Bluetooth`
The script:
- Scans **all** network hardware (PCI `lspci -nn` + USB `lsusb`, filtered for Realtek/Intel/Mediatek/Atheros/Qualcomm) and Bluetooth controllers
- Maps vendors to packages: `firmware-iwlwifi`, `firmware-realtek`, `firmware-mediatek`, `firmware-atheros`, `firmware-intel-misc`, plus `firmware-linux-nonfree` as the base meta-package
- Builds a plan showing detected controllers and planned packages, including Bluetooth handling (KDE → `bluedevil`, XFCE → `blueman`, GNOME → built-in, plus `pipewire-pulse`/`wireplumber` if PipeWire is active)
- Asks for confirmation before installing; offers Stable vs Backports for the base firmware
Accept the plan it shows: it is based on your actual hardware. Broadcom wireless (if present) is handled via `broadcom-sta-dkms` with DKMS build checks and `update-initramfs` + `modprobe wl`.
⚠️ You need internet for this step. If WiFi does not work after firmware install, **reboot** first: the driver needs a fresh load.
> ![Script](/media/screenshots/05-firmware-plan.png) Firmware plan dialog with hardware + package list.
### Step 6: Install Graphics Drivers
**Menu:** `6 Graphics Drivers & Mesa Stack`
Whiptail radiolist with two options:
```
1 Radeon/Intel Mesa
2 NVIDIA Drivers
```
#### Path A: Radeon/Intel Mesa
Shows a plan with detected GPUs (`Intel firmware + intel-media-va-driver-non-free` or `i965-va-driver-shaders` for Gen7-, `firmware-amd-graphics` for AMD, plus `Mesa Vulkan/OpenGL/VA-API`). Offers legacy AMD GCN 1.0/1.1 migration to `amdgpu` via GRUB params (`radeon.si_support=0 ... amdgpu.si_support=1`). Installs Mesa from Backports or Stable (`_install_mesa_backports`), verifies `mesa-vulkan-drivers`, and offers vendor-specific telemetry (`radeontop`, `intel-gpu-tools`, `vainfo`).
#### Path B: NVIDIA Drivers
Shows detected NVIDIA GPUs and a two-stage menu:
1. **Manage menu** (if a driver is already installed): *Install / Change Driver Version* vs *Remove Driver and Restore Nouveau*.
2. **Version menu** (depends on Debian version):
- Bookworm: `v535` (Recommended) or `v470` (Kepler legacy via `nvidia-tesla-470-driver`)
- Trixie: `v550` (Debian stable, Recommended), `v590` or `v595` (NVIDIA CUDA Repo with `cuda-keyring` + `nvidia-open` + `firmware-nvidia-gsp`)
The script auto-detects the GPU architecture (`kepler` / `fermi` / `maxwell` / `pascal` / `turing` / `ampere` / `ada` / `blackwell`) and enforces compatibility. For example, Blackwell requires `v590+` on Trixie, Maxwell/Pascal on Trixie + backports kernel is forced to `v550` stable, Fermi is vetoed on Bookworm/Trixie with an explanatory message.
> ![Script](/media/screenshots/06-gpu-choice.png) GPU type selector (Radeon/Intel vs NVIDIA).
After installation, **reboot** before testing. NVIDIA drivers need a fresh kernel load.
### Step 7: Kernel Configuration (Optional)
**Menu:** `7 Kernel`
```
stable Install linux-image-amd64 (Stable)
backports Install from backports (Trixie only)
rt Install linux-image-rt-amd64 (Preempt-RT)
cloud Install linux-image-cloud-amd64
back Return to main menu
```
- **Stable**: default Debian kernel (6.12 LTS on Trixie).
- **Backports**: only on Trixie; requires `trixie-backports` enabled in Step 4. Newer kernel (e.g., 7.x) for Intel Arrow Lake / AMD Zen 5, Battlemage D3cold, etc.
- **RT**: Preempt-RT low-latency kernel. Warns if `GPU_TYPE == nvidia` (proprietary drivers may not support RT).
- **Cloud**: minimal kernel for VMs/containers (`linux-image-cloud-amd64`).
Each variant installs the matching `linux-headers-*` package atomically (`sudo apt install -y [-t trixie-backports] linux-image-* linux-headers-*`). The script warns about DKMS recompilation if NVIDIA is present.
> ![Script](/media/screenshots/07-kernel.png) Kernel menu with four variants.
> See [kernel](kernel.md) for backports rationale and bootloader details.
### Step 8: Gaming Setup (Optional)
**Menu:** `8 Gaming Setup`
A single checklist with all options:
```
[*] i386 Enable 32-bit (i386) architecture
[*] steam Steam (requires 32-bit support)
[*] mangohud Performance overlay (Vulkan/OpenGL)
[ ] gamemode Game performance optimization
[*] goverlay MangoHud config GUI
[ ] heroic Heroic Launcher (Epic/GOG)
[ ] java Minecraft Java Runtime
[ ] openrgb OpenRGB (RGB lighting control)
[ ] lutris Lutris + Wine (requires 32-bit support)
[ ] retroarch RetroArch Emulator Frontend
```
- If `steam`, `lutris`, or `i386` is checked, the script runs `dpkg --add-architecture i386` + `apt update`, then installs 32-bit graphics libraries (`_install_nvidia_32bit` or `_install_mesa_32bit`).
- `steam` checks `contrib` (`ensure_contrib_repo`) and installs `steam-installer`.
- `heroic` fetches the latest `.deb` from GitHub releases (`api.github.com`).
- `openrgb` is Bookworm/Trixie only; handles `i2c-dev`, udev, `i2c` group, and `setcap`.
- `java` offers Temurin 8 / 17 / 21 / 25 via `extrepo adoptium`.
- Requires GUI for some installers (skipped on headless).
> ![Script](/media/screenshots/08-gaming.png) Gaming checklist with i386 + Steam + MangoHud checked.
> See [gaming](gaming.md) and [retroarch](retroarch.md).
### Step 9: ZRAM Compressed Swap (Optional)
**Menu:** `9 ZRAM`
```
1 View ZRAM status
2 Create / Reconfigure ZRAM
3 Remove ZRAM
4 Back to main menu
```
- **View**: Shows `/etc/default/zramswap` (`ALGO`, `SIZE`, `PRIORITY`) and `zramctl` output.
- **Create / Reconfigure**: Choice of `lz4` (fastest, gaming) vs `zstd` (better ratio). Recommended size is 50% of RAM (≤8 GB) or 4096 MB fixed (>8 GB), configurable. Writes to `/etc/default/zramswap` (`ALGO`, `SIZE`, `PRIORITY=100`) and `systemctl restart zramswap || true`. Priority 100 ensures ZRAM is used before any disk swap (priority 10).
- **Remove**: `systemctl stop zramswap`, `swapoff /dev/zram0`, `modprobe -r zram`, `apt purge zram-tools`, removes `/etc/default/zramswap`.
> ![Script](/media/screenshots/09-zram-algo.png) Algorithm choice (lz4 vs zstd).
> ![Script](/media/screenshots/09b-zram-status.png) `zramctl` status output in whiptail.
> See [zram](zram.md).
### Step 10: Swap Management (Optional)
**Menu:** `10 Swap Management`
```
1 Show current swap & swappiness
2 Create / resize swapfile
3 Remove swapfile
4 Change swappiness
5 Back to main menu
```
- Uses `/swapfile` with `pri=10` (below ZRAM's 100) and tag `# debianito-managed-swap` in `/etc/fstab`.
- Btrfs: warns about `nodatacow` (`chattr +C`) and hibernation limitations; uses `dd` instead of `fallocate`.
- Fstab is written via a temp file and validated with `findmnt --verify` before replacing `/etc/fstab`.
- Concurrency is guarded by `flock /run/lock/debianito-swap.lock`.
> ![Script](/media/screenshots/10-swap.png) Swap Management menu.
> ![Script](/media/screenshots/10b-swap-status.png) Swap status.
> See [swap](swap.md).
### Step 11: Install Programs and Software
**Menu:** `11 Install Programs and Software` → `0 Essential Pack`
One-click install of `htop`, `inxi`, `neofetch`/`fastfetch`, `vlc`, `ufw`, `zip`, `unrar`, `p7zip`, plus `lsb-release` fixes.
After this, browse the other categories as needed:
| Category | Example Packages |
| ---------- | ------------------ |
| Customization System | Desktop themes, icons, cursors, fonts |
| Download & Network | aria2, ytdlp, qBittorrent, Deluge |
| Internet | Firefox, LibreWolf, Chromium, Tor, Thunderbird, RiseUp/Mullvad VPN |
| Communication | Signal, Telegram, HexChat |
| Media Players | VLC, MPV |
| Multimedia & Design | GIMP, Kdenlive, Blender, Audacity, Inkscape |
| Code Editors & IDEs | Neovim, Helix, Emacs, VSCodium, Geany |
| Servers & Dev Tools | Nginx, PostgreSQL, Docker, Temurin JDK, Jellyfin |
| Security & Networking | Wireshark, ClamAV, UFW, fail2ban |
| Software Center & Flatpak | GNOME Software, KDE Discover, Flatpak |
| Office & Productivity | LibreOffice, document tools |
| System Tools | htop/btop, Timeshift, extension-manager, virt-manager |
| Fetch / System Info | fastfetch, neofetch, hyfetch, screenfetch |
> ![Script](/media/screenshots/11-programs.gif) Category menu with 0-14 options.
> ![Script](/media/screenshots/11b-essential-pack.png) Essential Pack confirmation.
### Step 12: Boot Rescue
**Menu:** `12 Boot Rescue + GRUB`
- **GRUB boot menu settings**: 4 presets (hidden 0s / 3s / 5s / custom) writing `GRUB_TIMEOUT`, `GRUB_TIMEOUT_STYLE`, `GRUB_RECORDFAIL_TIMEOUT`, `GRUB_DISABLE_OS_PROBER` to `/etc/default/grub.d/99_script_override.cfg` with `update-grub` + backup/rollback.
- **UEFI Secure Boot repair**: reinstalls `shim-signed`, `grub-efi-amd64-signed`, `linux-image-amd64`, runs `grub-install` + `update-grub` (UEFI only, checked via `/sys/firmware/efi` + `mokutil`).
- **Initramfs regeneration**: `update-initramfs -u -k all || true`.
> See [boot](boot.md).
### Step 13: Desktop & Display
**Menu:** `13 Desktop & Display`
```
1 Desktop Environment
2 Display Manager
3 Back to main menu
```
- **Desktop Environment**: pick one of **XFCE** (full / minimal / Wayland `labwc` on Trixie / custom checklist), **LXDE** (full / core), **GNOME** (full / core) or **KDE Plasma** (full / minimal). Each environment installs from its own module in `modules/desktops/` (lazy-loaded). XFCE installs polkit rules (`85-suspend.rules`, `89-backlight.rules`) + `backlight` group; GNOME/KDE preseed the display manager while keeping any already-configured one as default. After the install, the script offers the PipeWire audio stack if none is present.
- **Display Manager**: LightDM (GTK greeter, user list, autologin), GDM3 (user list, autologin, NVIDIA Wayland override via `61-gdm.rules → /dev/null`), SDDM (autologin with session auto-detection `plasmawayland → lxqt-wayland → plasma → lxqt`), greetd (base / tuigreet / gtkgreet / nwg-hello / wlgreet; manual `/etc/greetd/config.toml` required).
> See [Desktop & Display](desktops_display.md).
---
## After the Script
1. **Reboot** if you installed firmware, GPU drivers, or a new kernel.
2. Verify:
```bash
sudo zramctl # ZRAM active?
sudo swapon --show # Swap with correct priorities?
vainfo # VA-API acceleration?
nvidia-smi # NVIDIA driver loaded?
systemctl status bluetooth # Bluetooth active?
```
3. For gaming, the script adds the `i386` architecture when you check `steam`, `lutris` or `i386`; verify with `dpkg --print-foreign-architectures | grep i386`.
---
## Troubleshooting Quick Answers
| Symptom | Fix |
| --------- | ----- |
| **WiFi not working after firmware** | Reboot. Check `lspci -nn \| grep Network` and verify the package (`firmware-iwlwifi` etc.) is `ii` via `dpkg -l`. Ensure `non-free` + `non-free-firmware` were enabled in Step 4. |
| **Black screen after NVIDIA + Wayland (GNOME/GDM3)** | At login, select *GNOME on Xorg* (gear icon) or disable Wayland: `sudo nano /etc/gdm3/daemon.conf` → `WaylandEnable=false`. The script only warns about Debian bug #1109409, it does not force X11. |
| **GRUB menu hidden and cannot enter** | Hold `ESC` immediately after power-on. Or boot a live USB, `chroot`, and run `12 Boot Rescue → GRUB boot menu settings → Show 5 seconds`. |
| **Steam fails to start** | Verify `i386` is enabled: `dpkg --print-foreign-architectures`. Check `contrib` is in `/etc/apt/sources.list`. Re-run `8 Gaming Setup` and ensure the checklist had `i386` + `steam` checked. |
| **greetd installed but cannot log in** | This is expected: you must create `/etc/greetd/config.toml` manually. See `man greetd` and `man 5 greetd-sessions`. |
| **Bluetooth tray icon missing** | Reboot or `systemctl restart bluetooth`. Ensure `bluez`, `bluedevil` (KDE) or `blueman` (XFCE) is installed. For PipeWire, check `systemctl --user status pipewire pipewire-pulse wireplumber`. |
| **PipeWire crackling / no Bluetooth Hi-Res codec** | Re-run `3 System Preferences → Audio & Sound → PipeWire Audio Stack`. Verify `libldacbt-*`, `libopenaptx0`, `libfdk-aac2t64` are installed (`dpkg -l \| grep -E 'ldac\|aptx\|fdk'`). |
+20 -39
View File
@@ -1,43 +1,38 @@
# Option 4: Repository Configuration
## Option 3: Advanced Repository Configuration
### 1. What Does This Component Do?
The repository configuration module sets up and maintains the APT sources of your Debian system. Each change is written atomically, backed up beforehand and rolled back automatically if the following `apt update` fails.
It:
The repository configuration module is the foundational engine of Debianito that establishes and maintains a secure, up-to-date package management environment for your Debian system. It performs **idempotent, atomic operations** to configure APT sources with precision while protecting against corruption through automatic rollback mechanisms.
At its core, this component:
- Detects your current repository format (Classic `.list` vs modern DEB822 `.sources`)
- Backs up existing configurations before any modifications
- Enables the non-free components required for hardware drivers and proprietary software
- Enables critical non-free components required for hardware drivers and proprietary software
- Integrates Debian Backports to access newer kernels and firmware packages
- Validates changes through `apt update` with automatic restoration on failure
Every other configuration option (GPU drivers, kernel upgrades, gaming setup) needs this module to work first.
This is not just about "adding repositories"—it's about **system integrity assurance** that enables all other configuration options (GPU drivers, kernel upgrades, gaming setup) to function correctly.
---
### 2. Supported Injection Formats
The script adapts to your Debian version and existing repository structure:
The script intelligently adapts to your Debian version and existing repository structure:
#### Classic Format (`/etc/apt/sources.list`)
- **Structure**: Human-readable text with `deb` lines
- **Use Case**: Debian 11 (Bullseye) through Debian 12 (Bookworm) default
- **Characteristics**: Linear, comment-friendly, widely understood by all APT tools
- **Example**:
```bash
deb https://deb.debian.org/debian bookworm main contrib non-free non-free-firmware
```
#### Modern DEB822 Format (`/etc/apt/sources.list.d/debian.sources`)
- **Structure**: Declarative RFC-822 style format with `Types`, `URIs`, and `Suites` blocks
- **Structure**: Declarative YAML-like format with `Types`, `URIs`, and `Suites` blocks
- **Use Case**: Debian 13 (Trixie) default, future-proofing for newer releases
- **Characteristics**: Machine-parseable, structured, supports complex repository hierarchies
- **Example**:
```yaml
Types: deb
URIs: https://deb.debian.org/debian
@@ -46,11 +41,9 @@ Components: main contrib non-free non-free-firmware
```
#### Migration Logic
The script automatically detects your current format and offers migration options:
- On Debian 13 (Trixie): Prompts to migrate TO DEB822 or stay with Classic
- Format changes are atomic: a backup is created before any modification
- Format changes are atomic—backup is created before any modification
- Old files are renamed with `.disabled` extension rather than deleted
---
@@ -59,6 +52,7 @@ The script automatically detects your current format and offers migration option
The `configure_repos()` function in `repos.sh` executes the following sequence:
```bash
┌─────────────────────────────────────────────────────────────┐
│ INITIAL DETECTION PHASE │
@@ -117,11 +111,6 @@ The `configure_repos()` function in `repos.sh` executes the following sequence:
│ ├── Creates appropriate file(s) │
│ └── Includes main + backports if enabled │
│ │
│ 8b. Branch Migration (if applicable) │
│ ├── _write_branch_sources() → writes new branch sources │
│ ├── If failure → _restore_backup() || true │
│ └── return 1 │
│ │
│ 9. Update Package Lists │
│ └── sudo apt update │
└─────────────────────────────────────────────────────────────┘
@@ -130,6 +119,7 @@ The `configure_repos()` function in `repos.sh` executes the following sequence:
│ POST-EXECUTION PHASE │
├─────────────────────────────────────────────────────────────┤
│ 10. Success Path │
│ ├── REPOS_CONFIGURED=true │
│ ├── Cleanup disabled files │
│ └── Optional: Upgrade system if packages available │
│ │
@@ -140,11 +130,9 @@ The `configure_repos()` function in `repos.sh` executes the following sequence:
```
**Key Safety Mechanisms:**
- **Atomic Operations**: Backup created before any write operation
- **Idempotency Check**: `content_differs()` prevents unnecessary modifications
- **Rollback on Failure**: If `apt update` fails, original configuration is restored via `restore_previous_repos()`
- **Branch Migration Rollback**: `_write_branch_sources()` failure triggers `_restore_backup() || true`
- **Rollback on Failure**: If `apt update` fails, original configuration is restored
- **Disabled File Extension**: Old formats renamed with `.disabled` rather than deleted
---
@@ -154,13 +142,13 @@ The `configure_repos()` function in `repos.sh` executes the following sequence:
The script enables specific APT component branches that are essential for hardware functionality and software availability:
| Component | Purpose | Critical For | Debian Version Notes |
| ----------- | --------- | -------------- | --------------------- |
|-----------|---------|--------------|---------------------|
| **main** | Free, open-source software (Debian official) | All packages | Always enabled |
| **contrib** | Free software that uses non-free components | Proprietary codecs, drivers | Enabled in all versions |
| **non-free** | Non-free firmware and proprietary software | NVIDIA GPU drivers, Wi-Fi firmware | Required for hardware support |
| **non-free-firmware** | Firmware blobs (Wi-Fi, Bluetooth, etc.) | Wireless adapters, embedded chips | **Critical from Debian 12+** |
#### Why `non-free-firmware` Is Required (Debian 12+)
#### Why `non-free-firmware` is Vital (Debian 12+)
Starting with Debian Bookworm (12.0), the `non-free-firmware` component was separated into its own repository branch:
@@ -173,14 +161,12 @@ deb https://deb.debian.org/debian bookworm main contrib non-free non-free-firmwa
```
**Impact of Missing `non-free-firmware`:**
- Wi-Fi adapters won't work without firmware blobs
- Bluetooth devices may fail to initialize
- Some GPU drivers require proprietary microcode
- Embedded hardware (Raspberry Pi, etc.) becomes unusable
- ❌ Wi-Fi adapters won't work without firmware blobs
- ❌ Bluetooth devices may fail to initialize
- ❌ Some GPU drivers require proprietary microcode
- ❌ Embedded hardware (Raspberry Pi, etc.) becomes unusable
The script ensures all four components are present because:
1. **Hardware Compatibility**: Modern Debian kernels depend on these for out-of-the-box functionality
2. **Security Updates**: `non-free-firmware` receives security patches separately
3. **Future-Proofing**: Newer hardware releases firmware in this component exclusively
@@ -189,7 +175,7 @@ The script ensures all four components are present because:
### 5. Support for Debian Backports
Backports give access to newer, tested packages without compromising system stability:
The backports integration is a sophisticated feature that enables access to newer, tested packages without compromising system stability:
#### Detection Logic (`detect_backports_status` & `detect_backports_location`)
@@ -202,7 +188,6 @@ Backports give access to newer, tested packages without compromising system stab
```
**Return Values:**
- `"standalone-deb822"` → Separate `.sources` file (recommended)
- `"standalone-classic"` → Separate `.list` file
- `"embedded-deb822"` → Inside `debian.sources`
@@ -243,10 +228,10 @@ Backports give access to newer, tested packages without compromising system stab
#### Why Enable Backports?
The script explains this in its confirmation dialog because backports change what you get for three components:
The script includes a detailed explanation because backports enable critical features:
| Feature | Without Backports | With Backports |
| --------- | ------------------- | ---------------- |
|---------|-------------------|----------------|
| **Linux Kernel** | Stable kernel only (e.g., 6.1-6.12) | Newer kernels (e.g., 6.x/7.x series) |
| **GPU "Drivers"** | Latest Mesa from stable | Latest Mesa from testing |
| **Wi-Fi Firmware** | Older firmware versions | Newest firmware for modern cards |
@@ -255,7 +240,6 @@ The script explains this in its confirmation dialog because backports change wha
#### Backports Warning System
The script includes safeguards:
- Only enabled if user explicitly confirms
- Warns about potential compatibility issues
- Can be disabled anytime via Option 3 again
@@ -266,7 +250,6 @@ The script includes safeguards:
### Technical Implementation Notes
**Idempotency Guarantee:**
```bash
# content_differs() ensures no duplicate writes
if [ "$current" = "$generated" ]; then
@@ -276,7 +259,6 @@ return 0 # Changes required
```
**Atomic Backup Mechanism:**
```bash
backup_current_repos() {
REPO_BACKUP_DIR=$(mktemp -d)
@@ -294,7 +276,6 @@ restore_previous_repos() {
**Component Activation Pattern:**
All four components are written in a single operation to prevent partial configurations:
```bash
Components: main contrib non-free non-free-firmware # Atomic write
# Not written as separate lines to avoid merge conflicts
+12 -12
View File
@@ -18,16 +18,16 @@ When installed via `apt`, RetroArch uses these locations:
Because the core and info directories are owned by root, you cannot write to them without `sudo`.
You have two options:
1. **Manual installation**: download cores and info files yourself and copy them with `sudo`.
1. **Manual installation** – download cores and info files yourself and copy them with `sudo`.
2. **Reconfigure RetroArch** to use user‑writable folders, then use the built‑in **Core Downloader**.
Both methods work; choose the one that suits you best.
---
## Method 1: Manual Core Installation (Recommended for Reliability)
## Method 1 – Manual Core Installation (Recommended for Reliability)
This method works for any core, even when the online updater is blocked.
This method guarantees that you can add any core, even if the online updater is blocked.
### 1. Download and Install the Core (`.so`)
@@ -41,7 +41,7 @@ sudo mv ppsspp_libretro.so /usr/lib/x86_64-linux-gnu/libretro/
rm ppsspp_libretro.so.zip
```
> **Replace `ppsspp` with any core name**: e.g., `mame_libretro`, `dolphin_libretro`, etc.
> **Replace `ppsspp` with any core name** – e.g., `mame_libretro`, `dolphin_libretro`, etc.
> Browse all available cores at:
> [https://buildbot.libretro.com/nightly/linux/x86_64/latest/](https://buildbot.libretro.com/nightly/linux/x86_64/latest/)
@@ -61,11 +61,11 @@ sudo mv ppsspp_libretro.info /usr/share/libretro/info/
---
## Method 2: Enable the Built‑in Core Downloader
## Method 2 – Enable the Built‑in Core Downloader
If you prefer using RetroArch’s graphical interface to download cores, you can change the core directories to writable locations.
### Step 1: Edit RetroArch Configuration
### Step 1 – Edit RetroArch Configuration
Create or edit `~/.config/retroarch/retroarch.cfg` and add these lines:
@@ -82,19 +82,19 @@ mkdir -p ~/.config/retroarch/libretro
mkdir -p ~/.config/retroarch/libretro-info
```
### Step 2: Update Core Info Files
### Step 2 – Update Core Info Files
- Launch RetroArch.
- Go to **Online Updater** → **Update Core Info Files**.
- Wait for the update to complete.
### Step 3: Download Cores
### Step 3 – Download Cores
- Go to **Online Updater** → **Core Downloader**.
- Select the core you want (e.g., PPSSPP).
- The core will be downloaded to your user directory and will appear in the core list.
> ⚠️ **Note:** Some cores may require additional system files (BIOS/assets). These are still placed in `~/.config/retroarch/system/`. You’ll need to obtain them separately (see below).
> ⚠️ **Note:** Some cores may require additional system files (BIOS/assets). These are still placed in `~/.config/retroarch/system/` – you’ll need to obtain them separately (see below).
---
@@ -139,7 +139,7 @@ You can find many asset packs at:
## ROM / Game File Formats
- **Supported formats** for PPSSPP: `.iso`, `.cso`, `.chd`, `.pbp`.
- **Always decompress your ROMs**: do not leave them in `.zip`, `.rar`, or `.7z` archives; the cores cannot read compressed archives directly.
- **Always decompress your ROMs** – do not leave them in `.zip`, `.rar`, or `.7z` archives; the cores cannot read compressed archives directly.
---
@@ -151,13 +151,13 @@ You can find many asset packs at:
| Core info files (GitHub) | [https://github.com/libretro/libretro-core-info](https://github.com/libretro/libretro-core-info) |
| System assets (BIOS, firmware, etc.) | [https://buildbot.libretro.com/assets/system/](https://buildbot.libretro.com/assets/system/) |
| Official RetroArch documentation | [https://docs.libretro.com/](https://docs.libretro.com/) |
| Debian Wiki: RetroArch | [https://wiki.debian.org/RetroArch](https://wiki.debian.org/RetroArch) |
| Debian Wiki – RetroArch | [https://wiki.debian.org/RetroArch](https://wiki.debian.org/RetroArch) |
---
## Final Notes
- The Debian package disables auto‑updates and the core downloader **by default**: this is intentional for stability.
- The Debian package disables auto‑updates and the core downloader **by default** – this is intentional for stability.
- Manual installation (Method 1) is the most straightforward and works for **any** core, regardless of distribution restrictions.
- If you choose Method 2, remember that you may still need to manually place BIOS/asset files in `~/.config/retroarch/system/`.
- Always test your video driver setting; `vulkan` is recommended for PPSSPP and other 3D‑heavy cores.
+52 -57
View File
@@ -1,77 +1,72 @@
# Option 10: Swap Management
## Option 9: Swap
### 1. What does this component do?
This module manages disk-based swap space in the `debianito` environment. It leaves existing configuration alone and works alongside ZRAM instead of conflicting with it.
This module is responsible for the secure management of disk-based swap space within the `debianito` environment. Unlike standard Linux tools that might overwrite existing configurations or conflict with memory compression features (like ZRAM), this script operates as a **priority-aware, persistent swap manager**.
Its primary functions include:
* **Dynamic Allocation:** Creating and resizing swapfiles based on detected RAM capacity without requiring physical partition changes.
* **Priority Integration:** Explicitly setting the swap priority (`pri=10`) to ensure it sits below ZRAM (which uses `priority=100`). This prevents the system from using disk I/O for memory swapping until compressed RAM is exhausted, optimizing performance.
* **Persistence Management:** Safely editing `/etc/fstab` with a unique tag (`# debianito-managed-swap`) to ensure swap survives reboots without corrupting manual entries.
* **Safety Locking:** Prevents concurrent operations using file locking mechanisms to avoid race conditions during active system usage.
* **Dynamic Allocation:** Creating and resizing swapfiles based on detected RAM capacity without requiring physical partition changes.
* **Priority Integration:** Explicitly setting the swap priority (`pri=10`) to ensure it sits below ZRAM (which uses `priority=100`). This prevents the system from using disk I/O for memory swapping until compressed RAM is exhausted, optimizing performance.
* **Persistence Management:** Safely editing `/etc/fstab` with a unique tag (`# debianito-managed-swap`) to ensure swap survives reboots without corrupting manual entries.
* **Safety Locking:** Prevents concurrent operations using file locking mechanisms to avoid race conditions during active system usage.
### 2. How does it work?
The script leverages several advanced Linux subsystems and safety protocols:
The script uses several Linux subsystems and safety mechanisms:
1. **File System Detection:** It identifies the filesystem type of the target partition (e.g., `ext4`, `btrfs`). For Btrfs, it applies specific flags (`chattr +C` to disable copy-on-write) to avoid performance loss during swap operations.
2. **Allocation Strategy:** It prefers `fallocate` for instant space reservation on supported filesystems, falling back to `dd if=/dev/zero` for compatibility or zeroing requirements (like Btrfs).
3. **Fstab Validation:** Before writing changes to `/etc/fstab`, it creates a temporary file and validates the syntax using `findmnt --verify`. If validation fails, the script aborts and restores the original state.
4. **Concurrency Control:** It uses `flock` on `/run/lock/debianito-swap.lock`. If another process is modifying swap settings (e.g., a system update), this script waits or exits gracefully to prevent filesystem corruption.
5. **Swappiness Tuning:** It configures `vm.swappiness` via both runtime (`sysctl -w`) and persistent (`/etc/sysctl.d/99-swappiness-debianito.conf`) mechanisms, defaulting to values that favor RAM usage over disk swapping (e.g., 10-20).
1. **File System Detection:** It identifies the filesystem type of the target partition (e.g., `ext4`, `btrfs`). For Btrfs, it applies specific flags (`chattr +C` for copy-on-write optimization) to prevent performance degradation during swap operations.
2. **Allocation Strategy:** It prefers `fallocate` for instant space reservation on supported filesystems, falling back to `dd if=/dev/zero` for compatibility or zeroing requirements (like Btrfs).
3. **Fstab Validation:** Before writing changes to `/etc/fstab`, it creates a temporary file and validates the syntax using `findmnt --verify`. If validation fails, the script aborts and restores the original state.
4. **Concurrency Control:** It utilizes `flock` on `/run/lock/debianito-swap.lock`. This ensures that if another process is modifying swap settings (e.g., a system update), this script will wait or exit gracefully to prevent filesystem corruption.
5. **Swappiness Tuning:** It configures `vm.swappiness` via both runtime (`sysctl -w`) and persistent (`/etc/sysctl.d/99-swappiness-debianito.conf`) mechanisms, defaulting to values that favor RAM usage over disk swapping (e.g., 10-20).
### 3. The Logical Decision Tree (Step by Step)
The execution flow follows a strict logical tree designed for safety and idempotency:
The execution flow runs in a fixed order and is safe to repeat:
1. **Initialization & Locking:**
* The `manage_swap()` function attempts to acquire an exclusive lock (`flock -n`). If the lock is held by another process, it immediately exits with a "Busy" message to prevent conflicts.
1. **Initialization & Locking:**
* The `manage_swap()` function attempts to acquire an exclusive lock (`flock -n`). If the lock is held by another process, it immediately exits with a "Busy" message to prevent conflicts.
2. **Menu Selection Loop:**
* Enters a continuous loop presenting options (Status, Create, Remove, Swappiness).
* Breaks only when the user selects "Back to main menu".
2. **Menu Selection Loop:**
* Enters a continuous loop presenting options (Status, Create, Remove, Swappiness).
* Breaks only when the user selects "Back to main menu".
3. **Action Execution Paths:**
* **Path A: Status Check (`_swap_current_status`)**
* Reads active swap entries via `swapon --show`.
* Reads current swappiness from `/proc/sys/vm/swappiness`.
* Parses `/etc/fstab` for managed tags.
* **Path B: Create/Resize (`_swap_create_file`)**
* **Recommendation:** Calculates optimal size based on RAM (e.g., 2GB for >16GB RAM, 4GB for 8-16GB).
* **Btrfs Check:** If the filesystem is Btrfs, it warns about `nodatacow` requirements and hibernation limitations.
* **Existence Check:** If `/swapfile` exists, it prompts to confirm recreation (deleting old data first via `swapoff`).
* **Allocation:** Uses `fallocate` or `dd` to zero the file. Sets permissions (`chmod 600`) and initializes swap (`mkswap`).
* **Persistence:** Attempts to write the new entry to `/etc/fstab`. If validation fails, it cleans up (removes file) before exiting.
* **Path C: Remove (`_swap_remove_file`)**
* Checks for the unique `SWAP_FSTAB_TAG` in fstab.
* Confirms user intent to delete.
* Executes `swapoff`, removes the fstab line, and deletes the physical file.
* **Path D: Swappiness (`_swap_set_swappiness`)**
* Validates input (0-100 integer).
* Writes a temporary sysctl config file.
* Applies changes immediately via `sysctl -w`.
3. **Action Execution Paths:**
* **Path A: Status Check (`_swap_current_status`)**
* Reads active swap entries via `swapon --show`.
* Reads current swappiness from `/proc/sys/vm/swappiness`.
* Parses `/etc/fstab` for managed tags.
* **Path B: Create/Resize (`_swap_create_file`)**
* **Recommendation:** Calculates optimal size based on RAM (e.g., 2GB for >16GB RAM, 4GB for 8-16GB).
* **Btrfs Check:** If the filesystem is Btrfs, it warns about `nodatacow` requirements and hibernation limitations.
* **Existence Check:** If `/swapfile` exists, it prompts to confirm recreation (deleting old data first via `swapoff`).
* **Allocation:** Uses `fallocate` or `dd` to zero the file. Sets permissions (`chmod 600`) and initializes swap (`mkswap`).
* **Persistence:** Attempts to write the new entry to `/etc/fstab`. If validation fails, it cleans up (removes file) before exiting.
* **Path C: Remove (`_swap_remove_file`)**
* Checks for the unique `SWAP_FSTAB_TAG` in fstab.
* Confirms user intent to delete.
* Executes `swapoff`, removes the fstab line, and deletes the physical file.
* **Path D: Swappiness (`_swap_set_swappiness`)**
* Validates input (0-100 integer).
* Writes a temporary sysctl config file.
* Applies changes immediately via `sysctl -w`.
### 4. What does each menu item do and what does it mean?
Each option in the swap management submenu serves a specific technical purpose:
* **Option 1: Show current swap & swappiness**
* **Technical Action:** Aggregates data from `/proc/swaps`, `/proc/sys/vm/swappiness`, and `/etc/fstab`.
* **Significance:** Shows the current memory management state. It verifies if ZRAM is active (implied by priority check) and how much disk swap is currently in use.
* **Option 1: Show current swap & swappiness**
* **Technical Action:** Aggregates data from `/proc/swaps`, `/proc/sys/vm/swappiness`, and `/etc/fstab`.
* **Significance:** Provides an audit trail of the current memory management state. It verifies if ZRAM is active (implied by priority check) and how much disk swap is currently in use.
* **Option 2: Create / resize swapfile**
* **Technical Action:** Allocates a new block device file (`/swapfile`) or expands an existing one. Sets the `pri=10` flag to ensure it acts as a secondary memory layer after ZRAM fills up.
* **Significance:** Essential for systems with low RAM that need overflow protection without installing physical partitions. The "Resize" capability allows adapting to new hardware configurations dynamically.
* **Option 2: Create / resize swapfile**
* **Technical Action:** Allocates a new block device file (`/swapfile`) or expands an existing one. Sets the `pri=10` flag to ensure it acts as a secondary memory layer after ZRAM fills up.
* **Significance:** Essential for systems with low RAM that need overflow protection without installing physical partitions. The "Resize" capability allows adapting to new hardware configurations dynamically.
* **Option 3: Remove swapfile**
* **Technical Action:** Disables the swapfile (`swapoff`), removes it from `/etc/fstab`, and deletes the file from disk.
* **Significance:** Useful for troubleshooting, freeing up disk space (e.g., on SSDs where write cycles are a concern), or migrating to ZRAM-only configurations if RAM is sufficient.
* **Option 3: Remove swapfile**
* **Technical Action:** Disables the swapfile (`swapoff`), removes it from `/etc/fstab`, and deletes the file from disk.
* **Significance:** Useful for troubleshooting, freeing up disk space (e.g., on SSDs where write cycles are a concern), or migrating to ZRAM-only configurations if RAM is sufficient.
* **Option 4: Change swappiness**
* **Technical Action:** Modifies the kernel parameter `vm.swappiness`.
* **Significance:** Controls the "aggressiveness" of swapping. A lower value (e.g., 10) tells the kernel to keep data in RAM longer, only using swap as a last resort. This matters for desktop performance and battery life on laptops.
* **Option 4: Change swappiness**
* **Technical Action:** Modifies the kernel parameter `vm.swappiness`.
* **Significance:** Controls the "aggressiveness" of swapping. A lower value (e.g., 10) tells the kernel to keep data in RAM longer, only using swap as a last resort. This is critical for desktop performance and battery life on laptops.
* **Option 5: Back to main menu**
* **Technical Action:** Releases the file lock (`exec 9>&-`) and terminates the submenu loop.
* **Significance:** Returns control to the user, ensuring no swap operations are running in the background before they navigate to other system configurations.
* **Option 5: Back to main menu**
* **Technical Action:** Releases the file lock (`exec 9>&-`) and terminates the submenu loop.
* **Significance:** Returns control to the user, ensuring no swap operations are running in the background before they navigate to other system configurations.
+16 -29
View File
@@ -1,49 +1,36 @@
# Option 1: Hardware Detection & System Information
## Option 1: Hardware Detection & System Information
### 1. What Does This Component Do?
This component serves as the **System Abstraction Layer** and diagnostic engine of the Debianito script. It is not merely a display utility; it acts as the foundational state initializer that runs prior to the main menu loop (`main_menu`). Its primary function is to perform pre-flight hardware enumeration, OS validation, and environment checks in "cold" mode (before any configuration changes are made).
This component performs the hardware enumeration, OS validation and environment checks that the rest of the script builds on. It runs in "cold" mode, before any configuration change is made, and the other menu options read the state it initializes instead of re-detecting everything.
**Initialization** (boot checks in `debianito.sh`, hardware state on demand via `_ensure_state_detected`):
1. **Dependency auto-install**: The script checks for `whiptail` and `lsb-release`, installing them automatically if missing, so the TUI and version detection work on minimal systems.
2. **`lspci` guard and cache**: `command -v lspci &>/dev/null` validates availability before execution. If present, the full `lspci -nn` output is captured once into the global variable `LSPCI_OUTPUT`. All subsequent GPU, Ethernet, and network detection reads from this cache, avoiding redundant `lspci` invocations and keeping the data consistent throughout the session.
3. **APT update deduplication**: The helper `_ensure_apt_updated` prevents redundant `apt update` calls by tracking whether the package lists have been refreshed recently. This matters for performance when multiple modules (repos, firmware, GPU, kernel) each need to verify repository state.
4. **State refresh flag**: `STATE_REFRESHED=true` is set before `;;` in every `case` branch of the main menu loop, ensuring that each menu entry starts with a clean, consistent state rather than stale cached data.
The variables it populates (`DEBIAN_VERSION`, `GPU_TYPE`, `CPU_SUMMARY`, network interface states) give the later menu options real context, so the user is not asked to make blind decisions: installing proprietary drivers on hardware that is not there, or selecting repositories that do not match the current Debian codename.
By populating global variables such as `DEBIAN_VERSION`, `GPU_TYPE`, `CPU_SUMMARY`, and network interface states, it ensures that the subsequent menu options have access to accurate context. This prevents the user from making blind decisions—for example, attempting to install proprietary drivers on a system without detected hardware or selecting repositories incompatible with the current Debian codename. It transforms raw kernel data into actionable configuration parameters.
### 2. System Commands Used (Technical Mapping)
The table below maps each piece of diagnostic data to the standard Linux tool `utils.sh` reads it from; none of it needs proprietary binaries.
The following table details the native Linux tools and file descriptors utilized by `utils.sh` to extract specific diagnostic data points. This mapping demonstrates reliance on standard, non-intrusive system utilities rather than proprietary binaries.
| Feature | Command / Tool | Technical Purpose & Logic |
| :--- | :--- | :--- |
| **OS Version** | `lsb_release -cs`, `/etc/os-release` | Parses `VERSION_CODENAME` to determine Debian release (Bullseye, Bookworm, Trixie). Critical for selecting correct repository backports. |
| **CPU Info** | `/proc/cpuinfo` | Reads `model name` and counts cores/threads. Provides cosmetic summary without needing heavy tools like `lscpu`. |
| **Memory** | `/proc/meminfo` | Extracts `MemTotal` to calculate RAM in GB. Used for compatibility warnings with specific software packages. |
| **GPU Detection** | `command -v lspci` guard → cached `LSPCI_OUTPUT` | `lspci -nn` output is cached in the global variable `LSPCI_OUTPUT` on first hardware access (`_ensure_state_detected`). All subsequent GPU detection (PCI/USB buses, Ethernet, network) reads from this cache instead of re-running `lspci`. Identifies VGA/3D controllers via PCI IDs (`10de` for NVIDIA). Checks driver versions via `dpkg` if `nvidia-smi` fails. |
| **GPU Detection** | `lspci -nn`, `nvidia-smi` | Identifies VGA/3D controllers via PCI IDs (`10de` for NVIDIA). Checks driver versions via `dpkg` if `nvidia-smi` fails. |
| **Network (Eth)** | `ip -o link show` | Enumerates Ethernet interfaces, state (UP/DOWN), and IP addresses using the `iproute2` suite. |
| **Network (Wi-Fi)** | `iwgetid`, `lspci` | Identifies wireless chipsets via PCI and retrieves SSID/Connection status for network diagnostics. |
| **Storage** | `lsblk -d -o NAME,SIZE,ROTA` | Distinguishes between NVMe (`nvme`), SSD (RoT=0), and HDD (RoT=1) to provide storage topology summary. |
| **Display Server** | Environment Vars (`XDG_SESSION_TYPE`) | Checks `WAYLAND_DISPLAY` vs `DISPLAY` variables to determine if the system is running Wayland, X11, or TTY. |
| **Display Server**| Environment Vars (`XDG_SESSION_TYPE`) | Checks `WAYLAND_DISPLAY` vs `DISPLAY` variables to determine if the system is running Wayland, X11, or TTY. |
### 3. Why Detection Matters
### 3. Strategic Importance for the Script
This diagnostic phase is vital for engineering stability and user experience (UX) integrity within the script architecture:
Each check protects a different part of the script:
* **Context-Aware Configuration:** The detection of `HAS_NVIDIA`, `HAS_AMD`, or `HAS_INTEL` directly dictates which sub-modules are loaded in `debianito.sh`. If no GPU is detected, graphics driver menus are skipped. This prevents "false positive" installation prompts that confuse the user.
* **Repository Compatibility Guardrails:** The `detect_debian_version` function validates the OS against supported codenames (11, 12, 13). It specifically triggers Bullseye-specific logic (`configure_repos_bullseye`) only when necessary, preventing repository errors on newer or older distributions.
* **APT Update Deduplication:** The `_ensure_apt_updated` helper tracks whether `apt update` has been run recently. Multiple modules (repos, firmware, GPU, kernel) share a single update cycle, preventing redundant network calls and inconsistent package state between modules.
* **State Refresh Consistency:** `STATE_REFRESHED=true` is set before `;;` in every `case` branch of the main menu loop, ensuring each menu entry starts with a clean state and preventing stale variable propagation between sequential selections.
* **Time Synchronization Safety:** The `check_system_time` function prevents package installation failures caused by clock skew, which breaks APT's GPG signatures. It offers an automated NTP sync before proceeding so the signatures verify again.
* **Root/Sudo Enforcement:** Early execution of `check_root` and `check_sudo` enforces security best practices. It prevents accidental privilege escalation or silent failures that often occur when scripts run with incorrect permissions.
* **Context-Aware Configuration:** The detection of `HAS_NVIDIA`, `HAS_AMD`, or `HAS_INTEL` directly dictates which sub-modules are loaded in `debianito.sh`. If no GPU is detected, graphics driver menus are skipped. This prevents "false positive" installation prompts that confuse the user.
* **Repository Compatibility Guardrails:** The `detect_debian_version` function validates the OS against supported codenames (11, 12, 13). It specifically triggers Bullseye-specific logic (`configure_repos_bullseye`) only when necessary, preventing repository errors on newer or older distributions.
* **Time Synchronization Safety:** The `check_system_time` function prevents package installation failures caused by clock skew (which breaks GPG signatures in APT). By offering an automated NTP sync before proceeding, it ensures the integrity of the entire software supply chain within the script.
* **Root/Sudo Enforcement:** Early execution of `check_root` and `check_sudo` enforces security best practices. It prevents accidental privilege escalation or silent failures that often occur when scripts run with incorrect permissions.
### 4. Formatting and UX in the Terminal
The raw data collected by these functions is processed into a human-readable format before being passed to the TUI (Text User Interface) via `whiptail`.
* **Structured String Assembly:** Functions like `_show_sysinfo` build multi-line strings (`msg+="...")`, appending newlines and conditional logic. This ensures that if multiple GPUs are found, they are listed sequentially with drivers identified below each entry.
* **Visual Hierarchy:** The data is organized into logical blocks (OS, Hardware, GPU, Network) with clear separators (`───`). This allows the user to quickly scan specific subsystems without scrolling through a monolithic log.
* **Conditional Rendering:** The script checks for command availability (e.g., `if ! command -v lspci &>/dev/null` guards GPU detection, `if ! command -v ip &>/dev/null` guards network data). If tools are missing, it gracefully degrades to a warning message rather than crashing the TUI. When `lspci` is present, its output is cached in `LSPCI_OUTPUT` for all subsequent detection calls.
* **TUI Integration:** The final formatted string is passed to `_msg`, which wraps the output in a `whiptail --msgbox`. This makes the diagnostic information appear as a modal dialog with consistent dimensions and styling (colors defined globally in `debianito.sh`), regardless of the underlying terminal emulator.
* **Structured String Assembly:** Functions like `_show_sysinfo` build multi-line strings (`msg+="...")`, appending newlines and conditional logic. This ensures that if multiple GPUs are found, they are listed sequentially with drivers identified below each entry.
* **Visual Hierarchy:** The data is organized into logical blocks (OS, Hardware, GPU, Network) with clear separators (`───`). This allows the user to quickly scan specific subsystems without scrolling through a monolithic log.
* **Conditional Rendering:** The script checks for command availability (e.g., `if ! command -v ip &>/dev/null`) before attempting to parse network data. If tools are missing, it gracefully degrades to a warning message rather than crashing the TUI.
* **TUI Integration:** The final formatted string is passed to `_msg`, which wraps the output in a `whiptail --msgbox`. This ensures the diagnostic information appears as a modal dialog with consistent dimensions and styling (colors defined globally in `debianito.sh`), maintaining a professional look regardless of the underlying terminal emulator.
-216
View File
@@ -1,216 +0,0 @@
# Option 3: System Preferences
## 1. What Does This Component Do?
The **System Preferences** module (`modules/system/system_prefs.sh` + `modules/system/audio.sh`) consolidates first-boot personalization into a single menu. It is the only place in Debianito where locale, timezone and audio are configured together, settings that Debian's installer often leaves at generic defaults and that affect every subsequent module.
It groups three independent preference domains behind one entry point:
| Sub-menu | Script | What It Configures |
| ---------- | -------- | --------------------- |
| **Date, Time & Timezone** | `system_prefs.sh:_prefs_date_time` | System clock, NTP sync, `tzdata` |
| **Language, Locales & Keyboard** | `system_prefs.sh:_prefs_locale_keyboard` | `locales`, `keyboard-configuration` |
| **Audio & Sound Stack** | `system/audio.sh:_prefs_audio_menu` | PipeWire, ALSA, pavucontrol, pulsemixer, playerctl |
The menu itself is `_system_preferences_menu` in `system_prefs.sh`, a simple `while true` loop with `whiptail --menu`:
```
1 Date, Time & Timezone
2 Language, Locales & Keyboard
3 Audio & Sound Stack
4 Back to main menu
```
> **Note:** This option was added after the initial repository setup. The main `README.md` menu previously started directly at "Configure Repositories". All Quick Start guides now reference System Preferences as Step 3.
---
## 2. Date, Time & Timezone
### Function: `_prefs_date_time` (`system_prefs.sh`)
```
Current system date/time: 2026-03-15 14:22
Timezone: America/Santiago
→ Confirm "Change the system timezone (dpkg-reconfigure tzdata)?"
→ sudo dpkg-reconfigure tzdata
→ _ensure_time_synced
```
**What it does technically:**
1. Prints the current date/time (`date '+%Y-%m-%d %H:%M'`) and timezone (`timedatectl show -p Timezone --value`).
2. Prompts via `_confirm "Timezone"` to run `sudo dpkg-reconfigure tzdata`, the Debian-standard interactive timezone selector (ncurses).
3. Calls `_ensure_time_synced` from `utils.sh` regardless of the answer:
- Forces `timedatectl set-ntp true`
- Validates the timezone is not `n/a` or `Etc/UTC`; if it is and a display is available, opens `tzdata` again
- Ensures `systemd-timesyncd` is installed, enabled and restarted
- Waits 2 seconds and checks `timedatectl show --property=NTPSynchronized --value` for `yes`
**Why it matters:** An incorrect clock breaks APT GPG verification (`Release file is not valid yet`) and TLS certificates. The script runs a similar check automatically before the first `apt update` (`_ensure_time_synced` is called from `_ensure_apt_updated` in `utils.sh`), but this menu allows the user to fix timezone manually without re-running the whole script.
---
## 3. Language, Locales & Keyboard
### Function: `_prefs_locale_keyboard` (`system_prefs.sh`)
```bash
sudo dpkg-reconfigure locales
sudo dpkg-reconfigure keyboard-configuration
```
**What it does technically:**
- **`dpkg-reconfigure locales`**: Opens the Debian locales selector (e.g., `en_US.UTF-8`, `es_CL.UTF-8`). Generates the selected locales via `locale-gen` and updates `/etc/default/locale`. No reboot is required, but a re-login is needed for the new `LANG` to take effect.
- **`dpkg-reconfigure keyboard-configuration`**: Configures `/etc/default/keyboard` (`XKBMODEL`, `XKBLAYOUT`, `XKBVARIANT`, `XKBOPTIONS`) and, on console, the keymap via `setupcon`. This affects both X11/Wayland and TTY.
Both commands are run with `env LC_ALL=C LANGUAGE=C` to guarantee the debconf dialogs render in English even when the current locale is broken.
This option has **no detection logic**: it delegates entirely to Debian's standard tools. It is idempotent and safe to run repeatedly.
---
## 4. Audio & Sound Stack
### Function: `_prefs_audio_menu` (`system/audio.sh`)
This is the most complex sub-menu. It presents a checklist built dynamically based on system state:
#### 4.1 Menu Construction
```bash
items+=("pipewire-audio" "PipeWire Audio Stack (Bluetooth Hi-Res)" $state)
items+=("alsa-utils" "ALSA Utilities (alsa-utils, alsa-ucm-conf)" $state)
# headless check — pavucontrol is GUI-only
if ! _is_headless; then
items+=("pavucontrol" "Volume Control GUI (pavucontrol)" $state)
fi
items+=("pulsemixer" "Terminal audio mixer (pulsemixer)" $state)
items+=("playerctl" "Multimedia key controller (playerctl)" $state)
```
- **`_state <pkg>`** (in `utils.sh`) returns `ON` if `dpkg -l <pkg>` shows `^ii`, otherwise `OFF`.
- **`_is_headless`** returns true when both `$DISPLAY` and `$WAYLAND_DISPLAY` are empty; in that case `pavucontrol` is hidden because it requires a GUI.
- Package name for PipeWire varies by Debian version:
- Debian 11 Bullseye → `pipewire` (the `pipewire-audio` metapackage does not exist)
- Debian 12/13 → `pipewire-audio`
The checklist is shown via `whiptail --checklist` with the title **"Audio & Sound"**.
#### 4.2 PipeWire Installation Logic: `_install_pipewire_standard`
This function handles the **PipeWire Audio Stack with Bluetooth Hi-Res codecs**.
**Codec package resolution:**
```bash
bt_pkgs="libldacbt-abr2 libldacbt-enc2 libopenaptx0 libspa-0.2-bluetooth"
# Transitional package name: libfdk-aac2t64 on Trixie (64-bit time_t), libfdk-aac2 on older
if apt-cache show libfdk-aac2t64; then bt_pkgs+=" libfdk-aac2t64"
elif apt-cache show libfdk-aac2; then bt_pkgs+=" libfdk-aac2"
fi
```
These packages provide **LDAC** (Sony), **aptX** (Qualcomm), and **AAC** (via FDK) Bluetooth codecs that are not in the default PipeWire installation.
**Version selection (Trixie only, when backports is enabled):**
| Condition | Dialog | Effect |
| ----------- | -------- | -------- |
| Already installed + backports available | `Upgrade to backports vX?` (Yes/No) | `sudo apt install --reinstall -t trixie-backports pipewire-audio $bt_pkgs` + `systemctl --user restart wireplumber pipewire pipewire-pulse` |
| Not installed + backports available | `Backports vX` vs `Stable vY` (Backports/Stable buttons) | Installs from chosen repository; on Trixie `apt_target="-t trixie-backports"` if Backports chosen |
| No backports | `Install PipeWire vX with Hi-Res codecs?` | Installs from stable |
**Per-Debian-version install commands:**
```bash
# Debian 11 Bullseye
sudo apt install -y pipewire libspa-0.2-bluetooth pipewire-alsa libspa-0.2-jack $bt_pkgs
# Debian 12 Bookworm
sudo apt install -y pipewire-audio $bt_pkgs
# Debian 13 Trixie
sudo apt install -y [-t trixie-backports] pipewire-audio $bt_pkgs
```
After installation on Debian 12/13, user PipeWire services are restarted:
```bash
systemctl --user restart wireplumber pipewire pipewire-pulse
```
#### 4.3 Other Audio Packages
| Package | Purpose | Install Guard |
| --------- | --------- | --------------- |
| `alsa-utils` + `alsa-ucm-conf` | ALSA CLI tools (`aplay`, `amixer`, `alsamixer`) and UCM configs for modern sound cards | `is_installed alsa-utils` |
| `pavucontrol` | GTK GUI volume control (PulseAudio/PipeWire compatible). Hidden in headless mode. | `is_installed pavucontrol` |
| `pulsemixer` | Terminal (ncurses) mixer, works in TTY and headless | `is_installed pulsemixer` |
| `playerctl` | MPRIS CLI to control media players via keyboard shortcuts | `is_installed playerctl` |
Each package is installed via `_run_install <pkg>` which confirms the version via `apt-cache policy` before installing.
---
## 5. Logical Execution Flow
```
┌──────────────────────────────────────────────────────────┐
│ _system_preferences_menu (system_prefs.sh) │
├──────────────────────────────────────────────────────────┤
│ whiptail --menu "System Preferences" │
│ 1 Date, Time & Timezone │
│ 2 Language, Locales & Keyboard │
│ 3 Audio & Sound Stack │
│ 4 Back to main menu │
└──────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
_prefs_date_time _prefs_locale_ _prefs_audio
keyboard → _prefs_audio_menu
│ │ │
│ │ ┌───────┴────────┐
│ │ │ Dynamic checklist│
│ │ │ based on │
│ │ │ _state + headless│
│ │ └───────┬────────┘
│ │ │
│ │ pipewire-audio ──→ _install_pipewire_standard
│ │ alsa-utils ──→ apt install alsa-utils
│ │ pavucontrol ──→ apt install pavucontrol
│ │ pulsemixer ──→ apt install pulsemixer
│ │ playerctl ──→ apt install playerctl
```
---
## 6. Integration with Other Modules
- **Firmware (`firmware.sh`)**: Bluetooth firmware packages (`firmware-iwlwifi`, etc.) are separate from the Bluetooth *stack* (`bluez`). This menu complements firmware by ensuring the audio side of Bluetooth (PipeWire codecs) is ready.
- **Desktop & Display (`desktop_display.sh`)**: New desktop installations (XFCE, LXDE, GNOME or KDE Plasma) often need audio configured afterwards. Running System Preferences → Audio after a new desktop ensures the correct mixer is available.
- **Gaming (`gaming.sh`)**: Many games require PipeWire/PulseAudio for voice chat. The gaming module assumes audio is already functional.
- **Time sync (`utils.sh:_ensure_time_synced`)**: Also called automatically before the first `apt update`. This menu is the manual override.
---
## 7. Detection & State Variables
| Variable / Function | Source | Used Here |
| --------------------- | -------- | ----------- |
| `DEBIAN_VERSION` (`11`/`12`/`13`) | `utils.sh:detect_debian_version` | PipeWire package name + install path |
| `AUDIO_SERVER` (`pipewire`/`pulseaudio`/`none`) | `utils.sh:detect_audio_server` | Bluetooth integration in `firmware.sh` |
| `is_backports_enabled` | `utils.sh:is_backports_enabled` | PipeWire backports version choice (Trixie) |
| `_is_headless` | `utils.sh:_is_headless` | Hides `pavucontrol` in TTY/SSH |
| `_state <pkg>` | `utils.sh:_state` | Checklist ON/OFF state |
| `_ensure_time_synced` | `utils.sh:_ensure_time_synced` | Called after timezone change |
---
## References
- [Debian TimeZone Config: wiki.debian.org/TimeZoneChanges](https://wiki.debian.org/TimeZoneChanges)
- [Debian Locales: wiki.debian.org/Locale](https://wiki.debian.org/Locale)
- [PipeWire: wiki.debian.org/PipeWire](https://wiki.debian.org/PipeWire)
- [PipeWire Bluetooth Codecs: GitLab PipeWire wiki](https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/FAQ#bluetooth)
+56 -71
View File
@@ -1,95 +1,80 @@
# Option 2: User Privileges & Feedback
## Option 2: User Privileges & Feedback
### 1. What does this component do?
This component serves as a centralized utility suite designed to streamline administrative access, enhance usability during system maintenance, and correct common permission inconsistencies found in fresh Debian installations or environments where `sudo` usage has been mishandled. It acts as an automated "First-Time User" setup tool that bridges the gap between strict Linux security policies (where standard users cannot modify system files) and practical daily workflow needs (such as installing software without constant password prompts).
This module prepares a normal user account for day-to-day administration on a fresh Debian installation, or on one where `sudo` usage has been mishandled. Debian gives a standard user no administrative rights by default, so routine work such as installing software would ask for a password each time; this menu closes that gap and fixes the ownership and feedback annoyances that come with fresh setups.
It manages four aspects of user privileges:
1. **Elevated Permissions:** Ensures the current user has membership in the `sudo` group.
2. **Workflow Efficiency:** Configures specific commands to run without authentication (NOPASSWD) for maintenance tasks.
3. **Data Integrity:** Repairs ownership issues on the home directory caused by accidental root-level file creation.
4. **User Experience:** Modifies terminal behavior during password entry to provide visual feedback.
At a high level, it manages four critical aspects of user privilege:
1. **Elevated Permissions:** Ensures the current user has membership in the `sudo` group.
2. **Workflow Efficiency:** Configures specific commands to run without authentication (NOPASSWD) for maintenance tasks.
3. **Data Integrity:** Repairs ownership issues on the home directory caused by accidental root-level file creation.
4. **User Experience:** Modifies terminal behavior during password entry to provide visual feedback.
### 2. What exactly does it do and why
The script executes a sub-menu loop that allows the user to toggle between four distinct configurations. Each option addresses a specific pain point in Linux administration:
The script executes a sub-menu loop that allows the user to toggle between four distinct configurations. Each option fixes a specific annoyance in Linux administration:
* **Sudo Group Membership (Option 1):**
* **Action:** Checks if the current username exists within the `/etc/group` file under the `sudo` group entry. If absent, it adds the user via `usermod -aG sudo`.
* **Why:** By default, Debian creates a standard user without administrative rights to prevent accidental system damage. This ensures the user can execute privileged commands (`sudo`) immediately after installation or recovery.
* **Sudo Group Membership (Option 1):**
* **Action:** Checks if the current username exists within the `/etc/group` file under the `sudo` group entry. If absent, it adds the user via `usermod -aG sudo`.
* **Why:** By default, Debian creates a standard user without administrative rights to prevent accidental system damage. This ensures the user can execute privileged commands (`sudo`) immediately after installation or recovery.
* **Passwordless Sudo (Option 2):**
* **Action:** Creates an isolated configuration file in `/etc/sudoers.d/` containing `NOPASSWD` rules for specific binaries (e.g., `/usr/bin/apt`, `/sbin/reboot`). It enforces strict path matching to prevent privilege escalation risks.
* **Why:** Frequent password prompts interrupt workflows during updates or system reboots. This allows automation and quick access for maintenance tasks without compromising security on other commands.
* **Passwordless Sudo (Option 2):**
* **Action:** Creates an isolated configuration file in `/etc/sudoers.d/` containing `NOPASSWD` rules for specific binaries (e.g., `/usr/bin/apt`, `/sbin/reboot`). It enforces strict path matching to prevent privilege escalation risks.
* **Why:** Frequent password prompts interrupt workflows during updates or system reboots. This allows automation and quick access for maintenance tasks without compromising security on other commands.
* **Repair Home Directory Ownership (Option 3):**
* **Action:** Scans the user's home directory (`/home/$USER`) to verify if files are owned by `root` (UID 0) instead of the user. If a mismatch is found, it recursively resets ownership via `chown -R`.
* **Why:** Misconfigured file permissions often occur when users attempt to fix issues using root privileges directly in their home folders. This restores data integrity so applications can read/write their own configuration files without permission errors.
* **Repair Home Directory Ownership (Option 3):**
* **Action:** Scans the user's home directory (`/home/$USER`) to verify if files are owned by `root` (UID 0) instead of the user. If a mismatch is found, it recursively resets ownership via `chown -R`.
* **Why:** Misconfigured file permissions often occur when users attempt to fix issues using root privileges directly in their home folders. This restores data integrity so applications can read/write their own configuration files without permission errors.
* **Sudo Password Feedback (Option 4):**
* **Action:** Toggles the `Defaults pwfeedback` directive within a dedicated sudoers file. When enabled, typing a password displays asterisks (`****`) instead of being hidden.
* **Why:** Linux terminals hide input by default to prevent shoulder surfing. This option improves usability for users who need visual confirmation that their keystrokes are registering correctly, reducing the risk of typos in complex passwords.
* **Sudo Password Feedback (Option 4):**
* **Action:** Toggles the `Defaults pwfeedback` directive within a dedicated sudoers file. When enabled, typing a password displays asterisks (`****`) instead of being hidden.
* **Why:** Linux terminals hide input by default to prevent shoulder surfing. This option improves usability for users who need visual confirmation that their keystrokes are registering correctly, reducing the risk of typos in complex passwords.
### 3. The Logical Decision Tree (Step-by-Step)
The execution flow is governed by `sudo_config.sh`, which acts as a state machine within the main menu loop. Below is the chronological logic for each option:
**Entry Point:**
1. The script enters the `config_sudo()` function and loops until the user selects "Back to main menu".
2. It presents a Whiptail menu with options 1–5.
1. The script enters the `config_sudo()` function and loops until the user selects "Back to main menu".
2. It presents a Whiptail menu with options 1–5.
**Option 1: Sudo Group Membership (`_check_sudo_group`)**
* **Step A:** Execute `groups "$USER"` via pipe to grep for `\bsudo\b`.
* **Decision:**
* *If Match:* Display success message ("User is already in sudo group"). Exit function.
* *If No Match:* Prompt user with a confirmation dialog asking if they want to add the user to the `sudo` group.
* *On Confirm:* Execute `sudo usermod -aG sudo "$USER"`. If successful, display message instructing logout/login for changes to take effect. If failure, log error and return status 1.
* **Step A:** Execute `groups "$USER"` via pipe to grep for `\bsudo\b`.
* **Decision:**
* *If Match:* Display success message ("User is already in sudo group"). Exit function.
* *If No Match:* Prompt user with a confirmation dialog asking if they want to add the user to the `sudo` group.
* *On Confirm:* Execute `sudo usermod -aG sudo "$USER"`. If successful, display message instructing logout/login for changes to take effect. If failure, log error and return status 1.
**Option 2: Passwordless Sudo (`_configure_nopasswd`)**
* **Step A:** Check if `/etc/sudoers.d/${USER}-nopasswd` exists.
* *If Exists:* Prompt to remove the configuration (restore password prompts). If confirmed, delete file and notify success. Return function.
* *If Not Exists:* Prompt user to configure NOPASSWD for maintenance commands.
* *On Confirm:* Display a checklist menu allowing selection of `apt`, `systemctl`, or `power` commands.
* **Step B:** Process selected commands:
* Construct the content string based on selections, explicitly defining paths (e.g., `/usr/bin/apt`, `/sbin/shutdown`) to ensure compatibility across Debian versions.
* **Step C:** Write configuration:
* Pipe content to `sudo tee /etc/sudoers.d/${USER}-nopasswd`.
* Set file permissions to `0440` (readable only by root and owner).
* Notify success or failure.
* **Step A:** Check if `/etc/sudoers.d/${USER}-nopasswd` exists.
* *If Exists:* Prompt to remove the configuration (restore password prompts). If confirmed, delete file and notify success. Return function.
* *If Not Exists:* Prompt user to configure NOPASSWD for maintenance commands.
* *On Confirm:* Display a checklist menu allowing selection of `apt`, `systemctl`, or `power` commands.
* **Step B:** Process selected commands:
* Construct the content string based on selections, explicitly defining paths (e.g., `/usr/bin/apt`, `/sbin/shutdown`) to ensure compatibility across Debian versions.
* **Step C:** Write configuration:
* Pipe content to `sudo tee /etc/sudoers.d/${USER}-nopasswd`.
* Set file permissions to `0440` (readable only by root and owner).
* Notify success or failure.
**Option 3: Repair Home Directory Ownership (`_repair_home_ownership`)**
* **Step A:** Resolve the home directory path securely using `getent passwd "${SUDO_USER:-$USER}"` instead of the legacy insecure `eval echo "~$USER"` pattern. This prevents command injection and ensures correct resolution across all shell environments:
```bash
home=$(getent passwd "${SUDO_USER:-$USER}" | cut -d: -f6)
```
* **Step B:** Retrieve User ID (UID) using `id -u "$USER"`.
* **Step C:** Check current owner UID of `$HOME` using `stat -c '%u'`.
* *If Match:* Notify that ownership is correct and exit.
* *If Mismatch:* Identify the expected username for the conflicting UID. Prompt user to confirm repair.
* *On Confirm:* Execute `sudo chown -R "$USER:$USER" "$home"`. If successful, notify success. If failure, log error and return status 1.
* **Step A:** Resolve the absolute path of `$HOME`. If directory does not exist, notify error and return status 1.
* **Step B:** Retrieve User ID (UID) using `id -u "$USER"`.
* **Step C:** Check current owner UID of `$HOME` using `stat -c '%u'`.
* *If Match:* Notify that ownership is correct and exit.
* *If Mismatch:* Identify the expected username for the conflicting UID. Prompt user to confirm repair.
* *On Confirm:* Execute `sudo chown -R "$USER:$USER" "$home"`. If successful, notify success. If failure, log error and return status 1.
**Option 4: Sudo Password Feedback (`_toggle_pwfeedback`)**
* **Step A:** Check if `/etc/sudoers.d/pwfeedback` exists.
* *If Exists:* Prompt to disable asterisks (restore hidden input). If confirmed, delete file and notify success. Return function.
* *If Not Exists:* Prompt user to enable visual feedback.
* *On Confirm:* Write `Defaults pwfeedback` to `/etc/sudoers.d/pwfeedback`.
* **Step B:** Verify write permission. If successful, notify success; otherwise, log error and return status 1.
* **Step A:** Check if `/etc/sudoers.d/pwfeedback` exists.
* *If Exists:* Prompt to disable asterisks (restore hidden input). If confirmed, delete file and notify success. Return function.
* *If Not Exists:* Prompt user to enable visual feedback.
* *On Confirm:* Write `Defaults pwfeedback` to `/etc/sudoers.d/pwfeedback`.
* **Step B:** Verify write permission. If successful, notify success; otherwise, log error and return status 1.
### 4. Compatibility with all Debian
This module is designed for universal compatibility across the Debian family (Bullseye, Bookworm, Trixie, etc.) due to its reliance on standard POSIX-compliant tools and strict path handling.
### 4. Compatibility Across Debian Versions
The module works across the Debian family (Bullseye, Bookworm, Trixie) because it only uses standard POSIX tools and handles paths strictly.
* **Architecture Independence:** The script uses `usermod`, `chown`, and `grep`, which are available on all x86_64, arm64, and i386 Debian architectures.
* **Version Independence (Debian 11+):**
* **Sudoers Syntax:** The script writes to `/etc/sudoers.d/`, a directory introduced in `sudo` version 1.9.0p5 (available since Debian 7). It avoids editing the master file (`/etc/sudoers`) directly, preventing lockfile issues and syntax errors regardless of the specific Debian version's sudo configuration style.
* **Path Hardening:** The NOPASSWD logic explicitly includes both `/usr/bin` and `/bin` paths for commands like `apt`. This ensures that on older Debian versions (e.g., Bullseye) where binaries might reside in different locations or symlinks differ, the permissions remain valid.
* **Security Best Practices:** By isolating configurations into separate files (`/etc/sudoers.d/`) and setting restrictive permissions (`0440`), it adheres to Debian's security guidelines for `sudo`. This ensures that even on older systems with stricter default policies, the configuration is accepted without requiring a full system reboot or sudo upgrade.
* **Importance:** Users can move between Debian releases (e.g., from 11 to 12) without reconfiguring these privileges by hand.
* **Architecture Independence:** The script utilizes `usermod`, `chown`, and `grep` which are available on all x86_64, arm64, and i386 Debian architectures.
* **Version Agnosticism (Debian 11+):**
* **Sudoers Syntax:** The script writes to `/etc/sudoers.d/`, a directory introduced in `sudo` version 1.9.0p5 (available since Debian 7). It avoids editing the master file (`/etc/sudoers`) directly, preventing lockfile issues and syntax errors regardless of the specific Debian version's sudo configuration style.
* **Path Hardening:** The NOPASSWD logic explicitly includes both `/usr/bin` and `/bin` paths for commands like `apt`. This ensures that on older Debian versions (e.g., Bullseye) where binaries might reside in different locations or symlinks differ, the permissions remain valid.
* **Security Best Practices:** By isolating configurations into separate files (`/etc/sudoers.d/`) and setting restrictive permissions (`0440`), it adheres to Debian's security guidelines for `sudo`. This ensures that even on older systems with stricter default policies, the configuration is accepted without requiring a full system reboot or sudo upgrade.
* **Importance:** Consistent behavior across versions means users can migrate between Debian releases (e.g., from 11 to 12) without needing to manually reconfigure these specific privileges, ensuring a stable and secure environment regardless of the underlying OS version.
+64 -107
View File
@@ -1,17 +1,17 @@
# Option 9: ZRAM Configuration & Memory Optimization
# Option 8: ZRAM Configuration & Memory Optimization
## 1. ZRAM vs. Traditional Swap
## 1. The Science of ZRAM vs. Traditional Swap
### Core Concept: CPU Cycles vs. Disk I/O
Traditional swap has a latency gap that matters under memory pressure:
Traditional swap storage operates on a fundamental latency gap that becomes critical under memory pressure:
| Storage Medium | Latency Range | Write Amplification | SSD Wear Impact |
| --------------- | --------------- | --------------------- | ----------------- |
|---------------|---------------|---------------------|-----------------|
| **DRAM (RAM)** | ~10–50 nanoseconds | None | Zero |
| **NVMe SSD** | ~20–70 microseconds | 1.2x–3.0x | Moderate to High |
| **SATA SSD** | ~100–200 microseconds | 1.5x–4.0x | High |
| **HDD** | ~5–10 milliseconds | N/A (mechanical) | Irrelevant |
| **NVMe SSD** | ~20–70 microseconds | 1.2x–3.0x | Moderate to High |
| **SATA SSD** | ~100–200 microseconds | 1.5x–4.0x | High |
| **HDD** | ~5–10 milliseconds | N/A (mechanical) | Irrelevant |
When a Linux system experiences memory pressure, the kernel must decide what to swap out. Traditional swap writes pages directly to disk storage:
@@ -27,19 +27,19 @@ ZRAM creates a compressed block device entirely within physical memory. When pag
2. **Stored in RAM pool** at compressed size (typically 2:1 to 3:1 ratio)
3. **Decompressed instantly** when needed (microseconds vs milliseconds)
The trade is CPU cycles for less I/O latency. Modern CPUs compress and decompress pages in microseconds, which is far cheaper than any disk operation.
The trade-off is explicit: **CPU cycles for reduced I/O latency**. Modern CPUs can compress/decompress pages in microseconds, making this far cheaper than any disk operation.
### Why Only LZ4 and ZSTD?
The script offers only two algorithms because they hit the best balance of speed and ratio:
The script offers only two algorithms because they represent the optimal balance points:
| Algorithm | Compression Ratio | Speed | CPU Overhead | Best Use Case |
|-----------|------------------|-------|--------------|---------------|
| **LZ4** | ~2:1–3:1 | Fastest | Lowest | Gaming, real-time workloads |
| **ZSTD** | ~3:1–5:1 | Medium | Moderate | General use, better memory savings |
- **LZ4**: Prioritizes speed over compression ratio. Best when CPU is limited or latency matters (gaming servers).
- **ZSTD**: Compresses more at acceptable overhead. Best when the goal is maximum effective RAM.
- **LZ4**: Prioritizes speed over compression ratio. Ideal for systems where CPU availability is limited or latency-sensitive (gaming servers).
- **ZSTD**: Offers superior compression ratios with acceptable overhead. Best for systems prioritizing maximum effective RAM capacity.
The kernel supports additional algorithms (lzo-rle, deflate, lz4hc), but these are either deprecated, slower, or offer diminishing returns compared to LZ4/ZSTD in modern hardware.
@@ -49,7 +49,7 @@ By intercepting swap writes before they reach physical storage:
- **Write Reduction**: Pages that would write to disk now compress in RAM
- **TBW Conservation**: Each avoided write preserves P/E cycles on NAND flash cells
- **System Longevity**: Matters for systems with limited SSD endurance (e.g., 100 TBW consumer drives)
- **System Longevity**: Critical for systems with limited SSD endurance ratings (e.g., 100 TBW consumer drives)
As the Linux kernel documentation states: *"Users with SSDs as swap devices can extend device lifespan by drastically reducing writes that shorten its life."*
@@ -59,7 +59,7 @@ As the Linux kernel documentation states: *"Users with SSDs as swap devices can
### Pipeline Execution Sequence
The script follows a fixed sequence:
The script follows a deterministic flow to ensure safe, reproducible configuration:
```
┌─────────────────────────────────────────────────────────────┐
@@ -74,13 +74,10 @@ The script follows a fixed sequence:
│ │
│ 3. Size Calculation Logic │
│ ┌──────────────────────────────────────────────┐ │
│ │ ram_gb <= 8 ? 50% : 4096 MB fixed │ │
│ │ recommended_mb = (ram_gb <= 8) │ │
│ │ ? (RAM_KB/1024/1024 + 1) │ │
│ │ / 2 * 1024 │ │
│ │ : 4096 │ │
│ │ half_ram_mb = ((RAM_KB / 1024 / 1024 + 1) │ │
│ │ / 2) * 1024 │ │
│ └──────────────────────────────────────────────┘ │
│ └─ Result: 50% RAM if ≤8 GB, else fixed 4096 MB │
│ └─ Result: ~50% of total physical RAM in MB │
│ │
│ 4. Configuration Confirmation │
│ ├─ Display summary with algorithm, size, priority=100 │
@@ -89,67 +86,40 @@ The script follows a fixed sequence:
│ 5. Package Installation │
│ sudo apt install -y zram-tools │
│ │
│ 6. Reset Existing ZRAM Device │
│ sudo swapoff /dev/zram0 (ignore errors) │
│ sudo modprobe -r zram (ignore errors) │
│ └─ Guarantees the old device is released before │
│ applying the new configuration │
│ │
│ 7. Configuration File Write │
│ 6. Configuration File Write │
│ /etc/default/zramswap │
│ ALGO=$algo │
│ SIZE=$zram_size │
│ PRIORITY=100 │
│ │
│ 8. Service Restart │
│ sudo systemctl restart zramswap || true || true │
│ └─ Verify: comp_algorithm shows [algo]; sudo zramctl │
│ 7. Service Restart │
│ sudo systemctl restart zramswap │
└─────────────────────────────────────────────────────────────┘
```
### Size Formula
### Mathematical Size Calculation
The script uses this formula to determine ZRAM size:
```bash
ram_gb=$(( RAM_KB / 1024 / 1024 ))
if [ "$ram_gb" -le 8 ]; then
recommended_mb=$(( ((RAM_KB / 1024 / 1024 + 1) / 2) * 1024 ))
else
recommended_mb=4096
fi
half_ram_mb=$(( ((RAM_KB / 1024 / 1024 + 1) / 2) * 1024 ))
```
**Breakdown:**
- `RAM_KB`: Total RAM in kilobytes from `/proc/meminfo`
- `/ 1024 / 1024`: Convert KB to GB
- `/ 1024 / 1024`: Convert KB to MB
- `+ 1`: Add rounding buffer for odd values
- **≤8 GB**: Target 50% of total RAM (generous swap for low-memory systems)
- **>8 GB**: Fixed 4096 MB, which avoids excessive RAM reservation on high-memory machines while still providing meaningful swap space
**Examples:**
- `/ 2`: Target approximately 50% of total RAM
- `* 1024`: Round back to nearest MB
**Example:**
```
System with 4 GB (4194304 KB) RAM (<=8 GB):
recommended_mb = ((4194304 / 1024 / 1024 + 1) / 2) * 1024
= ((4 + 1) / 2) * 1024
= (5 / 2) * 1024
= 2 * 1024
= 2048 MB (2 GB)
System with 8 GB (8388608 KB) RAM (<=8 GB):
recommended_mb = ((8388608 / 1024 / 1024 + 1) / 2) * 1024
= ((8 + 1) / 2) * 1024
= (9 / 2) * 1024
= 4 * 1024
= 4096 MB (4 GB)
System with 16 GB (16777216 KB) RAM (>8 GB):
recommended_mb = 4096 MB (fixed)
System with 32 GB (33554432 KB) RAM (>8 GB):
recommended_mb = 4096 MB (fixed)
System with 8 GB (8388608 KB) RAM:
half_ram_mb = ((8388608 / 1024 / 1024 + 1) / 2) * 1024
= ((8 + 1) / 2) * 1024
= (9 / 2) * 1024
= 4.5 * 1024
= 4608 MB (~4.5 GB)
```
### Priority Configuration (`PRIORITY=100`)
@@ -164,51 +134,39 @@ This prevents thrashing where pages bounce between slow disk swap and fast RAM-b
---
## 3. Priority & Swappiness Integration
## 3. Kernel Parameter Tuning (`sysctl`)
### How ZRAM Coexists with Disk Swap
### Essential VM Parameters for Aggressive ZRAM Usage
The script does **not** hardcode `vm.swappiness` or watermark tuning. It uses a priority-based swap hierarchy combined with explicit swappiness control in the Swap module:
| Swap Device | Priority | Config Location | When It Is Used |
|-------------|----------|-----------------|-----------------|
| **ZRAM** | `100` | `/etc/default/zramswap` (`PRIORITY=100`) | **First**: kernel prefers higher priority |
| **Disk swapfile** (`/swapfile`) | `10` | `/etc/fstab` (`pri=10`) + `# debianito-managed-swap` tag | **Second**: only after ZRAM device is full |
This is implemented in `zram.sh:_zram_create` (writes `PRIORITY=100`) and `swap.sh:_swap_create_file` (writes `pri=10`). Priority is the canonical Linux `swapon` mechanism: `swapon --show` lists `PRIO` and the kernel always fills the highest-priority device first.
### Swappiness Is Managed Separately
Swappiness (`vm.swappiness`, 0–100, default 60 on Debian) controls how eagerly the kernel swaps at all, regardless of which device is preferred.
- The ZRAM module does not change swappiness. Changing it would affect both ZRAM and disk swap in ways that are workload-specific.
- To tune it, use **Option 10 → Swap Management → 4. Change swappiness** (`swap.sh:_swap_set_swappiness`):
```bash
cat /proc/sys/vm/swappiness # current value
# Persistent config
/etc/sysctl.d/99-swappiness-debianito.conf → vm.swappiness=<value>
# Applied immediately
sudo sysctl -w vm.swappiness=<value>
```
- Recommended starting points (not enforced by the script):
- **General desktop**: `60` (Debian default)
- **Gaming / 8 GB or less**: `80–100`, which allows ZRAM to be used earlier, trading CPU for reduced disk I/O
- **ZRAM-only, no disk swap**: `100–150` is safe because swap *is* RAM (compressed); there is no SSD wear cost
> **Previous documentation** recommended `vm.swappiness = 180` plus `watermark_*` and `page-cluster` tuning for ZRAM. The current script does not write those values; they are omitted here so this document matches the implementation. If you need watermark tuning, add it manually to `/etc/sysctl.d/` and validate with your workload.
### Verifying the Hierarchy
While the current script focuses on `zram-tools` configuration, optimal performance requires complementary kernel parameter tuning:
```bash
sudo swapon --show
# NAME TYPE SIZE USED PRIO
# /dev/zram0 partition 4G 0B 100
# /swapfile file 4G 0B 10
cat /proc/sys/vm/swappiness
# Recommended sysctl configuration for ZRAM systems
vm.swappiness = 180
vm.watermark_boost_factor = 0
vm.watermark_scale_factor = 125
vm.page-cluster = 0
```
### Parameter Explanations
| Parameter | Value | Purpose |
|-----------|-------|---------|
| **`vm.swappiness`** | `180–200` | Aggressively prefer swap over keeping pages in RAM. Higher values (up to 200) are ideal for ZRAM because it's faster than disk swap. Default 60 is too conservative for memory-constrained systems. |
| **`vm.watermark_boost_factor`** | `0` | Disable additional watermark boosting that could cause premature page reclaim |
| **`vm.watermark_scale_factor`** | `125` | Adjust low-memory watermark thresholds to trigger swap earlier when RAM is constrained |
| **`vm.page-cluster`** | `0` | Disable page clustering. Research shows this reduces unnecessary sequential reads during swap operations, improving ZRAM efficiency by ~15% in gaming workloads |
### Why High Swappiness for ZRAM?
Traditional wisdom suggests keeping swappiness low (20–40) to avoid swapping frequently. However:
- **ZRAM is faster than disk**: Microseconds vs milliseconds
- **Thrashing prevention**: Higher swappiness moves pages to ZRAM before they hit slow disk swap
- **Effective RAM expansion**: Compressed pages in ZRAM can store 2–3x more data, effectively increasing available memory
The Pop!_OS project and Linux kernel documentation both recommend values beyond 100 for in-memory swap scenarios like ZRAM/ZSWAP.
---
## 4. Service Lifecycle and Validation
@@ -216,11 +174,10 @@ cat /proc/sys/vm/swappiness
### Safe Service Initialization
```bash
sudo systemctl restart zramswap || true
sudo systemctl restart zramswap
```
**Why `restart` instead of `start`:**
- Ensures previous configuration is cleanly terminated
- Prevents orphaned processes from conflicting with new settings
- Reloads systemd unit files if they were modified during installation
@@ -234,14 +191,13 @@ sudo zramctl
```
**Output Interpretation:**
```
NAME ALGORITHM DISKSIZE DATA COMPR TOTAL STREAMS MOUNTPOINT
/dev/zram0 lz4 4G 2.1G 318.6M 424.9M [SWAP]
```
| Column | Meaning |
| -------- | --------- |
|--------|---------|
| **NAME** | Device identifier (/dev/zram0) |
| **ALGORITHM** | Active compression algorithm (lz4, zstd, etc.) |
| **DISKSIZE** | Maximum uncompressed data capacity configured |
@@ -273,20 +229,21 @@ watch -n 5 'free -h && zramctl'
### Troubleshooting Indicators
| Symptom | Likely Cause | Solution |
| --------- | -------------- | ---------- |
|---------|--------------|----------|
| `DATA` equals `DISKSIZE` but `COMPR` is near zero | System under memory pressure, ZRAM not being used | Increase `vm.swappiness` or check if physical swap has lower priority |
| High CPU usage with low compression ratio | Incompressible data (e.g., encrypted files) | Consider backing device for incompressible pages |
| Service fails to start | Missing dependencies (`zram-tools`, kernel module) | Run `sudo apt install zram-tools` and verify `modprobe zram` |
### Permanent Configuration
To ensure ZRAM persists across reboots, the script writes configuration to `/etc/default/zramswap`. This file is read by systemd's `zramswap.service` unit at boot time. The following also loads the kernel module:
To ensure ZRAM persists across reboots, the script writes configuration to `/etc/default/zramswap`. This file is read by systemd's `zramswap.service` unit at boot time. Additionally, adding the following ensures the kernel module loads:
```bash
echo "zram" | sudo tee /etc/modules-load.d/zram.conf
```
### References
### References:
- [https://docs.kernel.org/admin-guide/blockdev/zram.html](https://docs.kernel.org/admin-guide/blockdev/zram.html)
- [https://wiki.debian.org/ZRam](https://wiki.debian.org/ZRam)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

+6 -27
View File
@@ -19,8 +19,6 @@ _install_bluetooth_stack() {
return
fi
local stack_failed=false
local service_enable_only=false
if is_installed bluez; then
echo " → Bluetooth stack already installed."
service_enable_only=true
@@ -32,10 +30,7 @@ _install_bluetooth_stack() {
! is_installed bluez-tools && bt_pkgs+=(bluez-tools)
! is_installed bluez-obexd && bt_pkgs+=(bluez-obexd)
if [ ${#bt_pkgs[@]} -gt 0 ]; then
if ! _run_cmd "Bluetooth" "sudo DEBIAN_FRONTEND=noninteractive apt install -y ${bt_pkgs[*]}" "Installing Bluetooth stack..."; then
_msg_red "Bluetooth" "Failed to install the Bluetooth stack."
stack_failed=true
fi
_run_cmd "Bluetooth" "sudo DEBIAN_FRONTEND=noninteractive apt install -y ${bt_pkgs[*]}" "Installing Bluetooth stack..."
fi
fi
@@ -49,21 +44,11 @@ _install_bluetooth_stack() {
case "${DESKTOP_ENV:-other}" in
kde)
if ! is_installed bluedevil; then
if ! _run_cmd "Bluetooth" "sudo DEBIAN_FRONTEND=noninteractive apt install -y bluedevil" "Installing bluedevil..."; then
_msg_red "Bluetooth" "Failed to install bluedevil."
fi
_run_cmd "Bluetooth" "sudo DEBIAN_FRONTEND=noninteractive apt install -y bluedevil" "Installing bluedevil..."
fi
if [ "${AUDIO_SERVER:-}" = "pipewire" ]; then
if ! is_installed pipewire-pulse; then
if ! _run_cmd "Bluetooth" "sudo DEBIAN_FRONTEND=noninteractive apt install -y pipewire-pulse" "Installing pipewire-pulse..."; then
_msg_red "Bluetooth" "Failed to install pipewire-pulse."
fi
fi
if ! is_installed wireplumber; then
if ! _run_cmd "Bluetooth" "sudo DEBIAN_FRONTEND=noninteractive apt install -y wireplumber" "Installing wireplumber..."; then
_msg_red "Bluetooth" "Failed to install wireplumber."
fi
fi
! is_installed pipewire-pulse && _run_cmd "Bluetooth" "sudo DEBIAN_FRONTEND=noninteractive apt install -y pipewire-pulse" "Installing pipewire-pulse..."
! is_installed wireplumber && _run_cmd "Bluetooth" "sudo DEBIAN_FRONTEND=noninteractive apt install -y wireplumber" "Installing wireplumber..."
fi
;;
gnome)
@@ -71,9 +56,7 @@ _install_bluetooth_stack() {
;;
xfce|other)
if ! is_installed blueman; then
if ! _run_cmd "Bluetooth" "sudo DEBIAN_FRONTEND=noninteractive apt install -y blueman" "Installing blueman..."; then
_msg_red "Bluetooth" "Failed to install blueman."
fi
_run_cmd "Bluetooth" "sudo DEBIAN_FRONTEND=noninteractive apt install -y blueman" "Installing blueman..."
fi
;;
esac
@@ -85,9 +68,5 @@ _install_bluetooth_stack() {
sudo systemctl start bluetooth 2>/dev/null || true
fi
if $stack_failed; then
_msg_red "Bluetooth Setup" "Bluetooth setup finished with errors.\n\nThe stack may be incomplete.\nA session restart or reboot is\nrecommended to load the desktop\napplets and tray icons." 10 60
else
_msg "Bluetooth Setup" "Bluetooth stack installed.\n\nA session restart or reboot is\nrecommended to load the desktop\napplets and tray icons." 10 60
fi
_msg "Bluetooth Setup" "Bluetooth stack installed.\n\nA session restart or reboot is\nrecommended to load the desktop\napplets and tray icons." 10 60
}
+339 -512
View File
File diff suppressed because it is too large Load Diff
+31 -83
View File
@@ -14,43 +14,16 @@ check_bullseye_archive_phase() {
current_year=$(date +%Y)
current_month=$(date +%-m)
if [ "$current_year" -gt 2026 ] ||
{ [ "$current_year" -eq 2026 ] && [ "$current_month" -ge 9 ]; }; then
if [ "$current_year" -gt 2026 ] || \
{ [ "$current_year" -eq 2026 ] && [ "$current_month" -ge 9 ]; }; then
BULLSEYE_USE_ARCHIVE=true
fi
if $BULLSEYE_USE_ARCHIVE; then
# Idempotency guard: skip when sources.list already points to the archive
grep -q 'archive.debian.org' /etc/apt/sources.list 2>/dev/null && return 0
_msg "Debian 11 — Archive Phase" \
"Bullseye LTS support ended on 31 Aug 2026.\n\n\
The script can switch APT sources to archive.debian.org.\n\
The script will use archive.debian.org mirrors.\n\
No security updates will be available." 12 60
if ! _confirm "Debian 11 — Archive Phase" \
"Rewrite /etc/apt/sources.list to use archive.debian.org?\n\n\
A timestamped backup will be created before the change." 12 60; then
echo "Archive phase: keeping the current repository configuration."
return 0
fi
# Timestamped backup before rewriting (same convention as firmware.sh / gpu.sh)
if [ -f /etc/apt/sources.list ]; then
sudo cp /etc/apt/sources.list "/etc/apt/sources.list.backup.$(date +%Y%m%d_%H%M%S)"
fi
echo "Writing archive.debian.org sources..."
sudo tee /etc/apt/sources.list >/dev/null <<'EOF'
deb http://archive.debian.org/debian bullseye main contrib non-free
#deb http://archive.debian.org/debian-security bullseye-security main contrib non-free
# deb http://archive.debian.org/debian bullseye-updates main contrib non-free
# deb http://archive.debian.org/debian bullseye-backports main contrib non-free
EOF
# Refresh package lists; may fail on EOL archives (expired Release files)
echo "Running apt-get update (failures are expected on EOL archives)..."
sudo apt-get update || true
fi
}
@@ -60,8 +33,7 @@ EOF
install_nvidia_bullseye() {
echo -e "${YELLOW}NVIDIA GPU detected (Bullseye mode).${NC}"
local is_fermi
is_fermi=$(is_nvidia_fermi)
local is_fermi; is_fermi=$(is_nvidia_fermi)
local nv_pkg=""
local gpu_gen=""
@@ -82,17 +54,17 @@ install_nvidia_bullseye() {
msg+="Driver: ${gpu_gen} — ${nv_pkg} ${nv_ver:-unknown}\n"
msg+="[+] firmware-misc-nonfree\n"
msg+="[+] nvidia-settings\n\n"
msg+="Install NVIDIA driver?"
msg+="Instalar driver NVIDIA?"
if ! _confirm "NVIDIA — Bullseye" "$msg" 14 70; then
echo "Skipping NVIDIA driver."
echo "Omitiendo driver NVIDIA."
NVIDIA_DRIVER_MODE=""
return 0
fi
_run_cmd "NVIDIA" \
"sudo apt install -y $nv_pkg firmware-misc-nonfree nvidia-settings" \
"Installing NVIDIA driver ${gpu_gen}..."
"Instalando driver NVIDIA ${gpu_gen}..."
local i386_active=false
dpkg --print-foreign-architectures | grep -q i386 && i386_active=true
@@ -109,8 +81,8 @@ install_nvidia_bullseye() {
fi
fi
export NVIDIA_DRIVER_MODE="stable"
echo -e "${GREEN}NVIDIA driver ${nv_pkg} installed. Reboot required.${NC}"
NVIDIA_DRIVER_MODE="stable"
echo -e "${GREEN}Driver NVIDIA ${nv_pkg} instalado. Requiere reinicio.${NC}"
echo ""
echo "──────────────────────────────────────────────"
@@ -131,22 +103,18 @@ install_nvidia_bullseye() {
# 32-bit Mesa/NVIDIA + gamemode + mangohud + goverlay + lutris
# ---------------------------------------------------------------------------
install_gaming_bullseye() {
# Hardware state is detected on demand; GPU_TYPE is
# consulted below for the 32-bit driver choice.
_ensure_state_detected
echo -e "${YELLOW}Gaming setup (Bullseye)...${NC}"
local choices
choices=$(_checklist "Gaming Setup — Bullseye" \
"Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
"i386" "Enable 32-bit (i386) architecture" ON \
"steam" "Steam (requires 32-bit support)" ON \
"i386" "Enable 32-bit (i386) architecture" ON \
"steam" "Steam (requires 32-bit support)" ON \
"mangohud" "Performance overlay (Vulkan/OpenGL)" ON \
"gamemode" "Game performance optimization" OFF \
"goverlay" "MangoHud config GUI" ON \
"java" "Minecraft Java Runtime" OFF \
"lutris" "Lutris + Wine (requires 32-bit support)" OFF \
"java" "Minecraft Java Runtime" OFF \
"lutris" "Lutris + Wine (requires 32-bit support)" OFF \
"retroarch" "RetroArch Emulator Frontend" OFF)
if [ -z "$choices" ]; then
@@ -159,13 +127,8 @@ install_gaming_bullseye() {
cleaned=$(echo "$choices" | tr -d '"')
local need_32bit=false
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for p in "${_pkgs[@]}"; do
case $p in steam | lutris) need_32bit=true ;; esac
for p in $cleaned; do
case $p in steam|lutris) need_32bit=true ;; esac
done
echo "$cleaned" | grep -qw i386 && need_32bit=true
@@ -176,7 +139,7 @@ install_gaming_bullseye() {
if $need_32bit && ! dpkg --print-foreign-architectures 2>/dev/null | grep -q i386; then
echo -e "${YELLOW}Enabling i386 architecture...${NC}"
sudo dpkg --add-architecture i386
_ensure_apt_updated
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
fi
if $need_32bit; then
@@ -188,40 +151,25 @@ install_gaming_bullseye() {
fi
fi
# BH-004: Convert to array to avoid word splitting and injection.
local -a _install_pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _install_pkgs+=("$_pkg")
done < <(echo "$install_list" | tr ' ' '\n')
# Pre-apply summary: confirm the selection before the
# package installation starts. Skipped when only the
# i386 pseudo-entry was selected.
if [ ${#_install_pkgs[@]} -gt 0 ]; then
_confirm_install_list "Gaming Setup — Bullseye" "${_install_pkgs[@]}" || return
fi
for pkg in "${_install_pkgs[@]}"; do
for pkg in $install_list; do
case $pkg in
steam)
if ensure_contrib_repo; then
install_steam
else
echo -e "${YELLOW}Skipping Steam installation (contrib repository not enabled).${NC}"
fi
;;
java) install_minecraft_java ;;
mangohud) install_mangohud ;;
gamemode) install_gamemode ;;
goverlay) install_goverlay ;;
lutris) install_lutris ;;
retroarch) install_retroarch ;;
*) _run_install "$pkg" ;;
steam)
if ensure_contrib_repo; then
install_steam
else
echo -e "${YELLOW}Skipping Steam installation (contrib repository not enabled).${NC}"
fi
;;
java) install_minecraft_java ;;
mangohud) install_mangohud ;;
gamemode) install_gamemode ;;
goverlay) install_goverlay ;;
lutris) install_lutris ;;
retroarch) install_retroarch ;;
*) _run_install "$pkg" ;;
esac
done
# Release the per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}Gaming setup complete.${NC}"
_pause
}
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# repos.sh — Bullseye: classic repos with archive phase
# repos.sh — Bullseye: repos clásicos con archive phase
# License GPL v3
configure_repos_bullseye() {
+158 -283
View File
@@ -1,10 +1,6 @@
#!/usr/bin/env bash
manage_desktop_display() {
# Hardware state is detected on demand; HAS_NVIDIA
# (GDM3 Wayland option) is populated by the battery.
_ensure_state_detected
while true; do
local choice
choice=$(_menu "Desktop & Display" \
@@ -15,45 +11,9 @@ manage_desktop_display() {
[ -z "$choice" ] && break
clear
case "$choice" in
1) desktop_environment_menu ;;
2) display_manager_menu ;;
3) break ;;
esac
done
}
desktop_environment_menu() {
while true; do
local -a env_items=()
env_items+=("1" "XFCE")
env_items+=("2" "LXDE")
env_items+=("3" "GNOME")
env_items+=("4" "KDE Plasma")
env_items+=("5" "Back")
local choice
choice=$(_menu "Desktop Environment" \
"Select a desktop environment:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
"${env_items[@]}")
[ -z "$choice" ] && break
clear
case "$choice" in
1)
_load_module xfce || continue
xfce_menu
;;
2)
_load_module lxde || continue
lxde_menu
;;
3)
_load_module gnome || continue
gnome_menu
;;
4)
_load_module kde || continue
kde_menu
;;
5) break ;;
1) _msg "Coming Soon" "Desktop Environment management will be available in a future update." 10 60 ;;
2) display_manager_menu ;;
3) break ;;
esac
done
}
@@ -77,26 +37,26 @@ display_manager_menu() {
[ -z "$choice" ] && break
clear
case "$choice" in
1) lightdm_config_menu ;;
2) configure_gdm3 ;;
3) configure_sddm ;;
4)
if [ "$DEBIAN_VERSION" = "12" ] || [ "$DEBIAN_VERSION" = "13" ]; then
configure_greetd
else
break
fi
;;
5) break ;;
1) lightdm_config_menu ;;
2) configure_gdm3 ;;
3) configure_sddm ;;
4)
if [ "$DEBIAN_VERSION" = "12" ] || [ "$DEBIAN_VERSION" = "13" ]; then
configure_greetd
else
break
fi
;;
5) break ;;
esac
done
}
lightdm_config_menu() {
local -a items=()
items+=("install_lightdm" "Install LightDM & GTK Greeter" "$(_state lightdm)")
items+=("enable_userlist" "Enable user list at login screen" "OFF")
items+=("enable_autologin" "Enable autologin for current user" "OFF")
items+=("install_lightdm" "Install LightDM & GTK Greeter" "$(_state lightdm)")
items+=("enable_userlist" "Enable user list at login screen" "OFF")
items+=("enable_autologin" "Enable autologin for current user" "OFF")
local choices
choices=$(_checklist "LightDM Configuration" \
"Select options to apply:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
@@ -108,45 +68,36 @@ lightdm_config_menu() {
fi
local cleaned
cleaned=$(echo "$choices" | tr -d '"')
# SECURITY: Convert to array to avoid word splitting and command injection.
local -a lm_items=()
while IFS= read -r _item; do
[ -n "$_item" ] && lm_items+=("$_item")
done < <(echo "$cleaned" | tr ' ' '\n')
for item in "${lm_items[@]}"; do
for item in $cleaned; do
case $item in
install_lightdm)
if ! is_installed lightdm || ! is_installed lightdm-gtk-greeter-settings; then
echo "lightdm shared/default-x-display-manager select lightdm" | sudo debconf-set-selections
_run_cmd "LightDM" "sudo apt install -y lightdm lightdm-gtk-greeter lightdm-gtk-greeter-settings" "Installing LightDM & GTK Greeter..."
else
echo -e "${GREEN}[LightDM] LightDM and GTK Greeter settings are already installed, skipping...${NC}"
fi
;;
enable_userlist)
local conf="/etc/lightdm/lightdm.conf.d/50-debianito-userlist.conf"
sudo mkdir -p "$(dirname "$conf")"
if echo "[Seat:*]
greeter-hide-users=false" | sudo tee "$conf" >/dev/null; then
echo -e "${GREEN}User list enabled in LightDM.${NC}"
else
echo -e "${RED}Failed to write configuration.${NC}"
fi
;;
enable_autologin)
local dm_conf="/etc/lightdm/lightdm.conf"
local lightdm_user="${SUDO_USER:-}"
if [ -z "$lightdm_user" ] || [ "$lightdm_user" = "root" ]; then
lightdm_user=$(awk -F: '$3>=1000 && $3<65534 {print $1}' /etc/passwd | head -1)
fi
sudo sed -i 's/^#[[:space:]]*autologin-user[[:space:]=].*/autologin-user='"$lightdm_user"'/' "$dm_conf"
sudo sed -i 's/^#[[:space:]]*autologin-user-timeout[[:space:]=].*/autologin-user-timeout=0/' "$dm_conf"
echo -e "${GREEN}Autologin enabled for user: ${lightdm_user}${NC}"
;;
esac
done
_pause
install_lightdm)
if ! is_installed lightdm || ! is_installed lightdm-gtk-greeter-settings; then
echo "lightdm shared/default-x-display-manager select lightdm" | sudo debconf-set-selections
_run_cmd "LightDM" "sudo apt install -y lightdm lightdm-gtk-greeter lightdm-gtk-greeter-settings" "Installing LightDM & GTK Greeter..."
else
echo -e "${GREEN}[LightDM] LightDM and GTK Greeter settings are already installed, skipping...${NC}"
fi
;;
enable_userlist)
local conf="/etc/lightdm/lightdm.conf.d/50-debianito-userlist.conf"
sudo mkdir -p "$(dirname "$conf")"
if echo "[Seat:*]
greeter-hide-users=false" | sudo tee "$conf" > /dev/null; then
echo -e "${GREEN}User list enabled in LightDM.${NC}"
else
echo -e "${RED}Failed to write configuration.${NC}"
fi
;;
enable_autologin)
local dm_conf="/etc/lightdm/lightdm.conf"
local lightdm_user="${SUDO_USER:-$USER}"
sudo sed -i 's/^#[[:space:]]*autologin-user[[:space:]=].*/autologin-user='"$lightdm_user"'/' "$dm_conf"
sudo sed -i 's/^#[[:space:]]*autologin-user-timeout[[:space:]=].*/autologin-user-timeout=0/' "$dm_conf"
echo -e "${GREEN}Autologin enabled for user: ${lightdm_user}${NC}"
;;
esac
done
_pause
}
configure_greetd() {
@@ -170,47 +121,47 @@ configure_greetd() {
[ -z "$choice" ] && return
clear
case "$choice" in
1)
echo "greetd shared/default-x-display-manager select greetd" | sudo debconf-set-selections
_run_cmd "greetd" "sudo apt install -y greetd" "Installing greetd..."
_msg "greetd" "Warning: greetd was installed but NOT configured.\n\nYou must manually create /etc/greetd/config.toml\nto be able to log in.\n\nSee 'man greetd' and 'man 5 greetd-sessions'."
;;
2)
if [ "$DEBIAN_VERSION" = "12" ]; then
if [ "$(is_backports_enabled)" != true ]; then
_write_deb822_backports "bookworm"
_ensure_apt_updated
1)
echo "greetd shared/default-x-display-manager select greetd" | sudo debconf-set-selections
_run_cmd "greetd" "sudo apt install -y greetd" "Installing greetd..."
_msg "greetd" "Warning: greetd was installed but NOT configured.\n\nYou must manually create /etc/greetd/config.toml\nto be able to log in.\n\nSee 'man greetd' and 'man 5 greetd-sessions'."
;;
2)
if [ "$DEBIAN_VERSION" = "12" ]; then
if [ "$(is_backports_enabled)" != true ]; then
_write_deb822_backports "bookworm"
sudo apt update -qq
fi
_run_cmd "greetd" "sudo apt install -y -t bookworm-backports tuigreet greetd" "Installing greetd + tuigreet..."
else
_run_cmd "greetd" "sudo apt install -y greetd tuigreet" "Installing greetd + tuigreet..."
fi
_run_cmd "greetd" "sudo apt install -y -t bookworm-backports tuigreet greetd" "Installing greetd + tuigreet..."
else
_run_cmd "greetd" "sudo apt install -y greetd tuigreet" "Installing greetd + tuigreet..."
fi
_msg "greetd" "Warning: greetd was installed but NOT configured.\n\nYou must manually create /etc/greetd/config.toml\nto be able to log in.\n\nSee 'man greetd' and 'man 5 greetd-sessions'."
;;
3)
if [ "$DEBIAN_VERSION" = "13" ]; then
echo "greetd shared/default-x-display-manager select greetd" | sudo debconf-set-selections
_run_cmd "greetd" "sudo apt install -y greetd gtkgreet" "Installing greetd + gtkgreet..."
_msg "greetd" "Warning: greetd was installed but NOT configured.\n\nYou must manually create /etc/greetd/config.toml\nto be able to log in.\n\nSee 'man greetd' and 'man 5 greetd-sessions'."
else
return
fi
;;
4)
if [ "$DEBIAN_VERSION" = "13" ]; then
echo "greetd shared/default-x-display-manager select greetd" | sudo debconf-set-selections
_run_cmd "greetd" "sudo apt install -y greetd nwg-hello" "Installing greetd + nwg-hello..."
_msg "greetd" "Warning: greetd was installed but NOT configured.\n\nYou must manually create /etc/greetd/config.toml\nto be able to log in.\n\nSee 'man greetd' and 'man 5 greetd-sessions'."
fi
;;
5)
if [ "$DEBIAN_VERSION" = "13" ]; then
echo "greetd shared/default-x-display-manager select greetd" | sudo debconf-set-selections
_run_cmd "greetd" "sudo apt install -y greetd wlgreet" "Installing greetd + wlgreet..."
_msg "greetd" "Warning: greetd was installed but NOT configured.\n\nYou must manually create /etc/greetd/config.toml\nto be able to log in.\n\nSee 'man greetd' and 'man 5 greetd-sessions'."
fi
;;
6) return ;;
;;
3)
if [ "$DEBIAN_VERSION" = "13" ]; then
echo "greetd shared/default-x-display-manager select greetd" | sudo debconf-set-selections
_run_cmd "greetd" "sudo apt install -y greetd gtkgreet" "Installing greetd + gtkgreet..."
_msg "greetd" "Warning: greetd was installed but NOT configured.\n\nYou must manually create /etc/greetd/config.toml\nto be able to log in.\n\nSee 'man greetd' and 'man 5 greetd-sessions'."
else
return
fi
;;
4)
if [ "$DEBIAN_VERSION" = "13" ]; then
echo "greetd shared/default-x-display-manager select greetd" | sudo debconf-set-selections
_run_cmd "greetd" "sudo apt install -y greetd nwg-hello" "Installing greetd + nwg-hello..."
_msg "greetd" "Warning: greetd was installed but NOT configured.\n\nYou must manually create /etc/greetd/config.toml\nto be able to log in.\n\nSee 'man greetd' and 'man 5 greetd-sessions'."
fi
;;
5)
if [ "$DEBIAN_VERSION" = "13" ]; then
echo "greetd shared/default-x-display-manager select greetd" | sudo debconf-set-selections
_run_cmd "greetd" "sudo apt install -y greetd wlgreet" "Installing greetd + wlgreet..."
_msg "greetd" "Warning: greetd was installed but NOT configured.\n\nYou must manually create /etc/greetd/config.toml\nto be able to log in.\n\nSee 'man greetd' and 'man 5 greetd-sessions'."
fi
;;
6) return ;;
esac
done
}
@@ -218,12 +169,12 @@ configure_greetd() {
configure_gdm3() {
while true; do
local items_enabled=()
items_enabled+=("install" "Install/Reinstall gdm3 (Set as default)" "$(_state gdm3)")
items_enabled+=("userlist" "Hide user list (disable-user-list)" "OFF")
items_enabled+=("autologin" "Configure Autologin" "OFF")
items_enabled+=("install" "Install/Reinstall gdm3 (Set as default)" "$(_state gdm3)")
items_enabled+=("userlist" "Hide user list (disable-user-list)" "OFF")
items_enabled+=("autologin" "Configure Autologin" "OFF")
if [ "$DEBIAN_VERSION" = "12" ] || [ "$DEBIAN_VERSION" = "13" ]; then
if [ "$HAS_NVIDIA" = true ]; then
items_enabled+=("wayland" "Force Enable Wayland on NVIDIA (Risk!)" "OFF")
items_enabled+=("wayland" "Force Enable Wayland on NVIDIA (Risk!)" "OFF")
fi
fi
local choice
@@ -236,92 +187,58 @@ configure_gdm3() {
local cleaned
cleaned=$(echo "$choice" | tr -d '"')
# SECURITY: Convert to array to avoid word splitting and command injection.
local -a gdm_items=()
while IFS= read -r _item; do
[ -n "$_item" ] && gdm_items+=("$_item")
done < <(echo "$cleaned" | tr ' ' '\n')
for item in "${gdm_items[@]}"; do
for item in $cleaned; do
case $item in
install)
echo "gdm3 shared/default-x-display-manager select gdm3" | sudo debconf-set-selections
_run_cmd "GDM3" "sudo apt install -y gdm3" "Installing gdm3..."
;;
userlist)
local greeter_conf="/etc/gdm3/greeter.dconf-defaults"
if sudo grep -q '^disable-user-list=true' "$greeter_conf" 2>/dev/null; then
sudo sed -i 's/^disable-user-list=true/# disable-user-list=true/' "$greeter_conf"
echo -e "${GREEN}User list is now VISIBLE.${NC}"
else
sudo sed -i 's/^#[[:space:]]*disable-user-list=true/disable-user-list=true/' "$greeter_conf"
echo -e "${GREEN}User list is now HIDDEN.${NC}"
fi
sudo dpkg-reconfigure gdm3 >/dev/null 2>&1
;;
autologin)
local daemon_conf="/etc/gdm3/daemon.conf"
local username
username=$(_inputbox "GDM3 Autologin" \
"Enter username to autologin (leave empty to DISABLE autologin):" 10 60)
if [ -n "$username" ]; then
if ! [[ "$username" =~ ^[a-z_][a-z0-9_-]{0,31}$ ]]; then
_msg_red "GDM3 Autologin" "Invalid username: ${username}"
continue
install)
echo "gdm3 shared/default-x-display-manager select gdm3" | sudo debconf-set-selections
_run_cmd "GDM3" "sudo apt install -y gdm3" "Installing gdm3..."
;;
userlist)
local greeter_conf="/etc/gdm3/greeter.dconf-defaults"
if sudo grep -q '^disable-user-list=true' "$greeter_conf" 2>/dev/null; then
sudo sed -i 's/^disable-user-list=true/# disable-user-list=true/' "$greeter_conf"
echo -e "${GREEN}User list is now VISIBLE.${NC}"
else
sudo sed -i 's/^#[[:space:]]*disable-user-list=true/disable-user-list=true/' "$greeter_conf"
echo -e "${GREEN}User list is now HIDDEN.${NC}"
fi
[ -f "$daemon_conf" ] || sudo touch "$daemon_conf"
sudo sed -i 's/^# *AutomaticLoginEnable[[:space:]=].*/AutomaticLoginEnable=true/' "$daemon_conf"
sudo sed -i "s|^# *AutomaticLogin[[:space:]=].*|AutomaticLogin=${username}|" "$daemon_conf"
echo -e "${GREEN}Autologin enabled for user: ${username}${NC}"
else
sudo sed -i 's/^AutomaticLoginEnable[[:space:]=].*/# AutomaticLoginEnable=false/' "$daemon_conf"
sudo sed -i 's/^AutomaticLogin[[:space:]=].*/# AutomaticLogin=/' "$daemon_conf"
echo -e "${GREEN}Autologin disabled.${NC}"
fi
;;
wayland)
local rules_link="/etc/udev/rules.d/61-gdm.rules"
if [ -L "$rules_link" ] || [ -f "$rules_link" ]; then
sudo rm -f "$rules_link"
echo -e "${GREEN}Reverted. Wayland will be disabled by default on next reboot.${NC}"
elif [ -f "/lib/udev/rules.d/61-gdm.rules" ]; then
if _confirm "Wayland on NVIDIA" \
"Force-enable Wayland on NVIDIA?\n\nRisk: This may break graphics or cause instability.\nProceed?"; then
sudo ln -s /dev/null "$rules_link"
echo -e "${GREEN}Wayland override created. Reboot required.${NC}"
sudo dpkg-reconfigure gdm3 >/dev/null 2>&1
;;
autologin)
local daemon_conf="/etc/gdm3/daemon.conf"
local username
username=$(whiptail --title "GDM3 Autologin" \
--inputbox "Enter username to autologin (leave empty to DISABLE autologin):" \
10 60 "" 3>&1 1>&2 2>&3 || true)
if [ -n "$username" ]; then
sudo sed -i 's/^# *AutomaticLoginEnable[[:space:]=].*/AutomaticLoginEnable=true/' "$daemon_conf"
sudo sed -i 's/^# *AutomaticLogin[[:space:]=].*/AutomaticLogin='"$username"'/' "$daemon_conf"
echo -e "${GREEN}Autologin enabled for user: ${username}${NC}"
else
sudo sed -i 's/^AutomaticLoginEnable[[:space:]=].*/# AutomaticLoginEnable=false/' "$daemon_conf"
sudo sed -i 's/^AutomaticLogin[[:space:]=].*/# AutomaticLogin=/' "$daemon_conf"
echo -e "${GREEN}Autologin disabled.${NC}"
fi
fi
;;
;;
wayland)
local rules_link="/etc/udev/rules.d/61-gdm.rules"
if [ -L "$rules_link" ] || [ -f "$rules_link" ]; then
sudo rm -f "$rules_link"
echo -e "${GREEN}Reverted. Wayland will be disabled by default on next reboot.${NC}"
elif [ -f "/lib/udev/rules.d/61-gdm.rules" ]; then
if _confirm "Wayland on NVIDIA" \
"Force-enable Wayland on NVIDIA?\n\nRisk: This may break graphics or cause instability.\nProceed?"; then
sudo ln -s /dev/null "$rules_link"
echo -e "${GREEN}Wayland override created. Reboot required.${NC}"
fi
fi
;;
esac
done
_pause
done
}
# Ordered session candidates for SDDM autologin:
# Wayland first, then X11, per release. Existence
# decides (no codename gates). The LXQt sessions
# stay last for when the module starts offering LXQt.
_sddm_session_candidates() {
local wayland_dir="${SESSION_DIR_WAYLAND:-/usr/share/wayland-sessions}"
local x11_dir="${SESSION_DIR_X11:-/usr/share/xsessions}"
local candidate
for candidate in \
"$wayland_dir/plasmawayland.desktop" \
"$wayland_dir/plasma.desktop" \
"$x11_dir/plasma.desktop" \
"$x11_dir/plasmax11.desktop" \
"$wayland_dir/lxqt-wayland.desktop" \
"$x11_dir/lxqt.desktop"; do
if [ -f "$candidate" ]; then
basename "$candidate" .desktop
return 0
fi
done
return 0
}
configure_sddm() {
while true; do
local choice
@@ -334,79 +251,37 @@ configure_sddm() {
[ -z "$choice" ] && return
clear
case "$choice" in
1)
echo "sddm shared/default-x-display-manager select sddm" | sudo debconf-set-selections
_run_cmd "SDDM" "sudo apt install -y sddm" "Installing SDDM..."
;;
2)
local sddm_session=""
sddm_session=$(_sddm_session_candidates)
local sddm_user="${SUDO_USER:-}"
if [ -z "$sddm_user" ] || [ "$sddm_user" = "root" ]; then
sddm_user=$(awk -F: '$3>=1000 && $3<65534 {print $1}' /etc/passwd | head -1)
fi
sudo mkdir -p /etc/sddm.conf.d
cat <<EOF | sudo tee /etc/sddm.conf.d/autologin.conf >/dev/null
1)
echo "sddm shared/default-x-display-manager select sddm" | sudo debconf-set-selections
_run_cmd "SDDM" "sudo apt install -y sddm" "Installing SDDM..."
;;
2)
local sddm_session=""
local sddm_user="${SUDO_USER:-$USER}"
if [ -f /usr/share/wayland-sessions/plasmawayland.desktop ]; then
sddm_session="plasmawayland"
elif [ -f /usr/share/wayland-sessions/lxqt-wayland.desktop ]; then
sddm_session="lxqt-wayland"
elif [ -f /usr/share/xsessions/plasma.desktop ]; then
sddm_session="plasma"
elif [ -f /usr/share/xsessions/lxqt.desktop ]; then
sddm_session="lxqt"
fi
sudo mkdir -p /etc/sddm.conf.d
cat << EOF | sudo tee /etc/sddm.conf.d/autologin.conf > /dev/null
[Autologin]
User=${sddm_user}
Session=${sddm_session}
Relogin=false
EOF
if [ -n "$sddm_session" ]; then
echo -e "${GREEN}Autologin enabled for user ${sddm_user}, session: ${sddm_session}.${NC}"
else
echo -e "${YELLOW}Autologin enabled for user ${sddm_user}. No session set (SDDM will use default).${NC}"
fi
_pause
;;
3) return ;;
if [ -n "$sddm_session" ]; then
echo -e "${GREEN}Autologin enabled for user ${sddm_user}, session: ${sddm_session}.${NC}"
else
echo -e "${YELLOW}Autologin enabled for user ${sddm_user}. No session set (SDDM will use default).${NC}"
fi
_pause
;;
3) return ;;
esac
done
}
# ── SP2 shared helpers ────────────────────────────
# Print the display manager already configured as the
# X11 default (/etc/X11/default-display-manager), or an
# enabled DM systemd unit; print nothing when none is
# found. Desktop installers use this to avoid overwriting
# an existing display manager setup.
_existing_display_manager() {
local dm=""
local dm_path=""
# Config path is overridable for tests (same pattern as
# APT_DIR): the suite must never read the real system file.
local dm_file="${DM_FILE:-/etc/X11/default-display-manager}"
if [ -s "$dm_file" ]; then
dm_path=$(cat "$dm_file" 2>/dev/null) || dm_path=""
[ -n "$dm_path" ] && dm=$(basename "$dm_path")
fi
if [ -z "$dm" ]; then
local cand
for cand in gdm3 lightdm sddm greetd gdm; do
if systemctl is-enabled "$cand" 2>/dev/null | grep -q '^enabled$'; then
dm="$cand"
break
fi
done
fi
printf '%s\n' "$dm"
}
# Offer the standard PipeWire audio stack right after a
# desktop install, when no PipeWire server is present
# yet. _install_pipewire_standard gates itself internally,
# so no pre-prompt is needed here. Declining is a choice,
# not an error: always ends in return 0 so it is safe as
# the last call of an installer.
_offer_pipewire_after_desktop() {
local pkg_check="pipewire-audio"
[ "$DEBIAN_VERSION" = "11" ] && pkg_check="pipewire"
is_installed "$pkg_check" && return 0
if _confirm "PipeWire Audio" \
"No PipeWire audio server is installed.\n\nInstall the PipeWire audio stack (recommended)?"; then
_install_pipewire_standard
fi
return 0
}
-108
View File
@@ -1,108 +0,0 @@
#!/usr/bin/env bash
# desktop_gnome.sh — GNOME desktop installer (SP2)
# Full: gnome + gdm3 | Core: gnome-core + gdm3
# Debian ships GNOME 48 (13), 43 (12) and 3.38 (11).
# License GPL v3
# Pick the terminal emulator for this release. gnome-core
# declares the OR-dependency "gnome-terminal (>= 3.56) or
# gnome-console (>= 46) or ptyxis (>= 47)" (packages.debian.org
# /trixie/gnome-core, verified 2026-10-16) and apt resolves an
# unset OR-dep with the FIRST alternative, so gnome-core alone
# would pull gnome-terminal. Listing ptyxis in the same apt
# transaction satisfies the OR-dep with ptyxis instead. ptyxis
# (GTK4) exists in trixie (48.5-1~deb13u1) but NOT in bookworm
# (packages.debian.org/bookworm/ptyxis → no such package), hence
# the trixie-only gate. An already-installed gnome-terminal is
# left alone: both can coexist; no purge is ever attempted.
_gnome_terminal_pkg() {
case "${DEBIAN_CODENAME:-}" in
trixie) printf '%s\n' "ptyxis" ;;
*) printf '%s\n' "gnome-terminal" ;;
esac
}
gnome_menu() {
while true; do
local -a gn_items=()
gn_items+=("1" "GNOME (Full Desktop)")
gn_items+=("2" "GNOME Core (Minimal Install)")
gn_items+=("3" "Back")
local choice
choice=$(_menu "GNOME" \
"Select an option:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
"${gn_items[@]}")
[ -z "$choice" ] && break
clear
case "$choice" in
1) _install_gnome_full ;;
2) _install_gnome_core ;;
3) break ;;
esac
done
}
_install_gnome_full() {
# Pre-apply summary: confirm the selection before any
# debconf or apt transaction starts. The terminal package
# is resolved once (ptyxis on trixie, gnome-terminal
# elsewhere) so the summary lists what apt will install.
local terminal_pkg
terminal_pkg=$(_gnome_terminal_pkg)
_confirm_install_list "GNOME Full Desktop" gnome gdm3 "$terminal_pkg" || return
echo -e "${GREEN}Installing GNOME (Full Desktop)...${NC}"
# Keep an already-configured display manager intact.
local target_dm="gdm3"
local keep_dm
keep_dm=$(_existing_display_manager)
if [ -n "$keep_dm" ] && [ "$keep_dm" != "gdm3" ]; then
echo -e "${YELLOW}Display manager '${keep_dm}' is already configured — keeping it as default.${NC}"
target_dm="$keep_dm"
fi
echo "${target_dm} shared/default-x-display-manager select ${target_dm}" | sudo debconf-set-selections
_ensure_apt_updated
_run_cmd "GNOME Full" "sudo apt install -y gnome gdm3 $terminal_pkg" \
"Installing GNOME (Full Meta Package) + GDM3..."
if [ "$target_dm" = "gdm3" ]; then
sudo systemctl enable gdm3
fi
# Selection confirmed once above; release the
# per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}GNOME installed. Default display manager: ${target_dm}.${NC}"
_offer_pipewire_after_desktop
}
_install_gnome_core() {
# Pre-apply summary: confirm the selection before any
# debconf or apt transaction starts. The terminal package
# is resolved once (ptyxis on trixie, gnome-terminal
# elsewhere) so the summary lists what apt will install.
local terminal_pkg
terminal_pkg=$(_gnome_terminal_pkg)
_confirm_install_list "GNOME Core" gnome-core gdm3 "$terminal_pkg" || return
echo -e "${GREEN}Installing GNOME Core (Minimal)...${NC}"
# Keep an already-configured display manager intact.
local target_dm="gdm3"
local keep_dm
keep_dm=$(_existing_display_manager)
if [ -n "$keep_dm" ] && [ "$keep_dm" != "gdm3" ]; then
echo -e "${YELLOW}Display manager '${keep_dm}' is already configured — keeping it as default.${NC}"
target_dm="$keep_dm"
fi
echo "${target_dm} shared/default-x-display-manager select ${target_dm}" | sudo debconf-set-selections
_ensure_apt_updated
_run_cmd "GNOME Core" "sudo apt install -y gnome-core gdm3 $terminal_pkg" \
"Installing GNOME Core (Minimal) + GDM3..."
if [ "$target_dm" = "gdm3" ]; then
sudo systemctl enable gdm3
fi
# Selection confirmed once above; release the
# per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}GNOME Core installed. Default display manager: ${target_dm}.${NC}"
_offer_pipewire_after_desktop
}
-194
View File
@@ -1,194 +0,0 @@
#!/usr/bin/env bash
# desktop_kde.sh — KDE Plasma desktop installer (SP2)
# Full: kde-standard + sddm | Minimal: kde-plasma-desktop + sddm
# Custom: kde-plasma-desktop + sddm + konsole + extras elegidos
# bookworm: Plasma 5.27.5 / KF5 / Qt5; SDDM defaults to
# X11 (plasma.desktop in xsessions); Wayland is optional
# via plasma-workspace-wayland (plasmawayland.desktop).
# trixie: Plasma 6.3.6 / KF6 / Qt6; Wayland is the
# primary session (plasma.desktop in wayland-sessions),
# X11 the alternative (plasmax11.desktop). sddm is only
# Recommends in the meta-kde chain, so the explicit sddm
# in both routes is what guarantees the DM (and
# xserver-xorg/xauth, hard deps of sddm in both releases).
# License GPL v3
# plasma-workspace-wayland exists only on bookworm:
# bookworm's plasma-workspace ships the X11 session only,
# and the Wayland session (plasmawayland.desktop) lives
# in this separate package (a Recommends of plasma-desktop,
# listed explicitly for the same determinism as the
# explicit sddm). trixie integrates Wayland into
# plasma-workspace itself and the package does not exist
# there (packages.debian.org/trixie/plasma-workspace-wayland),
# so naming it off-bookworm would fail the apt transaction.
_kde_wayland_pkg() {
case "${DEBIAN_CODENAME:-}" in
bookworm) printf '%s\n' "plasma-workspace-wayland" ;;
*) printf '%s\n' "" ;;
esac
}
kde_menu() {
while true; do
local -a kd_items=()
kd_items+=("1" "KDE Plasma (Full Desktop)")
kd_items+=("2" "KDE Plasma Desktop (Minimal)")
kd_items+=("3" "KDE Custom (Choose packages)")
kd_items+=("4" "Back")
local choice
choice=$(_menu "KDE Plasma" \
"Select an option:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
"${kd_items[@]}")
[ -z "$choice" ] && break
clear
case "$choice" in
1) _install_kde_full ;;
2) _install_kde_minimal ;;
3) _install_kde_custom ;;
4) break ;;
esac
done
}
_install_kde_full() {
# Pre-apply summary: confirm the selection before any
# debconf or apt transaction starts. The Wayland
# session package is resolved once (bookworm only;
# the package does not exist on trixie) so the
# summary lists exactly what apt will install.
local wayland_pkg
wayland_pkg=$(_kde_wayland_pkg)
local -a pkgs=(kde-standard sddm)
[ -n "$wayland_pkg" ] && pkgs+=("$wayland_pkg")
_confirm_install_list "KDE Plasma Full" "${pkgs[@]}" || return
echo -e "${GREEN}Installing KDE Plasma (Full)...${NC}"
# Keep an already-configured display manager intact.
local target_dm="sddm"
local keep_dm
keep_dm=$(_existing_display_manager)
if [ -n "$keep_dm" ] && [ "$keep_dm" != "sddm" ]; then
echo -e "${YELLOW}Display manager '${keep_dm}' is already configured — keeping it as default.${NC}"
target_dm="$keep_dm"
fi
echo "${target_dm} shared/default-x-display-manager select ${target_dm}" | sudo debconf-set-selections
_ensure_apt_updated
_run_cmd "KDE Full" "sudo apt install -y ${pkgs[*]}" \
"Installing KDE Plasma (Full Meta Package) + SDDM..."
if [ "$target_dm" = "sddm" ]; then
sudo systemctl enable sddm
fi
# Selection confirmed once above; release the
# per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}KDE Plasma installed. Default display manager: ${target_dm}.${NC}"
_offer_pipewire_after_desktop
}
_install_kde_minimal() {
# Pre-apply summary: confirm the selection before any
# debconf or apt transaction starts. The Wayland
# session package is resolved once (bookworm only;
# the package does not exist on trixie) so the
# summary lists exactly what apt will install.
local wayland_pkg
wayland_pkg=$(_kde_wayland_pkg)
local -a pkgs=(kde-plasma-desktop sddm)
[ -n "$wayland_pkg" ] && pkgs+=("$wayland_pkg")
_confirm_install_list "KDE Plasma Minimal" "${pkgs[@]}" || return
echo -e "${GREEN}Installing KDE Plasma Desktop (Minimal)...${NC}"
# Keep an already-configured display manager intact.
local target_dm="sddm"
local keep_dm
keep_dm=$(_existing_display_manager)
if [ -n "$keep_dm" ] && [ "$keep_dm" != "sddm" ]; then
echo -e "${YELLOW}Display manager '${keep_dm}' is already configured — keeping it as default.${NC}"
target_dm="$keep_dm"
fi
echo "${target_dm} shared/default-x-display-manager select ${target_dm}" | sudo debconf-set-selections
_ensure_apt_updated
_run_cmd "KDE Minimal" "sudo apt install -y ${pkgs[*]}" \
"Installing KDE Plasma Desktop (Minimal) + SDDM..."
if [ "$target_dm" = "sddm" ]; then
sudo systemctl enable sddm
fi
# Selection confirmed once above; release the
# per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}KDE Plasma Desktop installed. Default display manager: ${target_dm}.${NC}"
_offer_pipewire_after_desktop
}
_install_kde_custom() {
# Fixed base (not selectable); extras chosen from the
# checklist. konsole is pinned because it is only a
# Recommends of kde-baseapps on trixie. All 17 extra
# names are identical on bookworm and trixie
# (packages.debian.org), so no per-release handling is
# needed beyond the shared Wayland gate. knotes is gone
# on trixie and konqueror/kwrite are legacy: excluded.
local cleaned
cleaned=$(_select_packages_checklist "KDE Custom" \
"Select the extra KDE packages to install:" \
"kate|advanced text editor" \
"ark|archive manager" \
"gwenview|image viewer" \
"okular|document viewer" \
"kcalc|calculator" \
"kde-spectacle|screenshot capture" \
"khelpcenter|documentation center" \
"sweeper|history cleaner" \
"print-manager|printer management" \
"kwalletmanager|password manager" \
"plasma-nm|network applet (needs NetworkManager)" \
"juk|music player" \
"dragonplayer|video player" \
"kmail|email client" \
"korganizer|calendar and tasks" \
"kaddressbook|contacts address book" \
"akregator|RSS feed reader")
[ -z "$cleaned" ] && return
# BH-004: reparse the space-separated selection into an array.
local -a kde_pkgs=()
local _pkg
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && kde_pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Fixed base: kde-plasma-desktop + sddm + konsole, plus
# the Wayland session package on bookworm only.
local wayland_pkg
wayland_pkg=$(_kde_wayland_pkg)
local -a base_pkgs=(kde-plasma-desktop sddm konsole)
[ -n "$wayland_pkg" ] && base_pkgs+=("$wayland_pkg")
# Pre-apply summary: confirm base + selection before any
# debconf or apt transaction starts.
_confirm_install_list "KDE Custom" "${base_pkgs[@]}" "${kde_pkgs[@]}" || return
echo -e "${GREEN}Installing KDE Plasma (Custom)...${NC}"
# Keep an already-configured display manager intact.
local target_dm="sddm"
local keep_dm
keep_dm=$(_existing_display_manager)
if [ -n "$keep_dm" ] && [ "$keep_dm" != "sddm" ]; then
echo -e "${YELLOW}Display manager '${keep_dm}' is already configured — keeping it as default.${NC}"
target_dm="$keep_dm"
fi
echo "${target_dm} shared/default-x-display-manager select ${target_dm}" | sudo debconf-set-selections
_ensure_apt_updated
_run_cmd "KDE Custom" "sudo apt install -y ${base_pkgs[*]} ${kde_pkgs[*]}" \
"Installing KDE Plasma (Custom) + SDDM..."
if [ "$target_dm" = "sddm" ]; then
sudo systemctl enable sddm
fi
# Selection confirmed once above; release the
# per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}KDE Plasma installed. Default display manager: ${target_dm}.${NC}"
_offer_pipewire_after_desktop
}
-159
View File
@@ -1,159 +0,0 @@
#!/usr/bin/env bash
# desktop_lxde.sh — LXDE desktop installer (extracted from
# desktop_display.sh for architectural symmetry with
# desktop_gnome.sh and desktop_kde.sh).
# License GPL v3
lxde_menu() {
while true; do
local -a lx_items=()
lx_items+=("1" "LXDE (Full Desktop)")
lx_items+=("2" "LXDE Core (Minimal Install)")
lx_items+=("3" "LXDE Custom (Choose packages)")
lx_items+=("4" "Back")
local choice
choice=$(_menu "LXDE" \
"Select an option:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
"${lx_items[@]}")
[ -z "$choice" ] && break
clear
case "$choice" in
1) _install_lxde_full ;;
2) _install_lxde_core ;;
3) _install_lxde_custom ;;
4) break ;;
esac
done
}
_install_lxde_full() {
# Pre-apply summary: confirm the selection before any
# debconf or apt transaction starts.
_confirm_install_list "LXDE Full Desktop" lxde lightdm || return
echo -e "${GREEN}Installing LXDE (Full Desktop)...${NC}"
# Keep an already-configured display manager intact.
local target_dm="lightdm"
local keep_dm
keep_dm=$(_existing_display_manager)
if [ -n "$keep_dm" ] && [ "$keep_dm" != "lightdm" ]; then
echo -e "${YELLOW}Display manager '${keep_dm}' is already configured — keeping it as default.${NC}"
target_dm="$keep_dm"
fi
echo "${target_dm} shared/default-x-display-manager select ${target_dm}" | sudo debconf-set-selections
_ensure_apt_updated
if [ "$target_dm" = "lightdm" ]; then
_run_cmd "LXDE Full" "sudo apt install -y lxde lightdm" \
"Installing LXDE (Full Meta Package) + LightDM..."
sudo systemctl enable lightdm
else
_run_cmd "LXDE Full" "sudo apt install -y lxde" \
"Installing LXDE (Full Meta Package)..."
fi
# Selection confirmed once above; release the
# per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}LXDE installed. Default display manager: ${target_dm}.${NC}"
_offer_pipewire_after_desktop
}
_install_lxde_core() {
# Pre-apply summary: confirm the selection before any
# debconf or apt transaction starts.
_confirm_install_list "LXDE Core" lxde-core lightdm || return
echo -e "${GREEN}Installing LXDE Core (Minimal)...${NC}"
# Keep an already-configured display manager intact.
local target_dm="lightdm"
local keep_dm
keep_dm=$(_existing_display_manager)
if [ -n "$keep_dm" ] && [ "$keep_dm" != "lightdm" ]; then
echo -e "${YELLOW}Display manager '${keep_dm}' is already configured — keeping it as default.${NC}"
target_dm="$keep_dm"
fi
echo "${target_dm} shared/default-x-display-manager select ${target_dm}" | sudo debconf-set-selections
_ensure_apt_updated
if [ "$target_dm" = "lightdm" ]; then
_run_cmd "LXDE Core" "sudo apt install -y lxde-core lightdm" \
"Installing LXDE Core (Minimal) + LightDM..."
sudo systemctl enable lightdm
else
_run_cmd "LXDE Core" "sudo apt install -y lxde-core" \
"Installing LXDE Core (Minimal)..."
fi
# Selection confirmed once above; release the
# per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}LXDE Core installed. Default display manager: ${target_dm}.${NC}"
_offer_pipewire_after_desktop
}
_install_lxde_custom() {
# Fixed base (not selectable); extras chosen from the checklist.
# lxterminal, polkitd, notification-daemon and light-locker are
# intentionally absent: they arrive via lxde-core's Recommends
# (verified on packages.debian.org/trixie, 2026-10-16).
local cleaned
cleaned=$(_select_packages_checklist "LXDE Custom" \
"Select the extra LXDE packages to install:" \
"lxappearance|Theme/appearance config" \
"lxappearance-obconf|Openbox theme settings" \
"lxrandr|Monitor configuration" \
"lxinput|Keyboard/mouse settings" \
"lxsession-edit|Session editor" \
"lxhotkey-gtk|Keyboard shortcuts config" \
"lxde-icon-theme|LXDE icon theme" \
"gpicview|Image viewer" \
"xarchiver|Archive manager" \
"galculator|Calculator" \
"mousepad|Text editor" \
"lxpolkit|PolicyKit authentication agent" \
"lxtask|Task manager" \
"numlockx|Enable NumLock on startup" \
"network-manager-applet|NetworkManager applet" \
"gnome-disk-utility|Disk utility")
[ -z "$cleaned" ] && return
# BH-004: reparse the space-separated selection into an array.
local -a lxde_pkgs=()
local _pkg
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && lxde_pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
if [ ${#lxde_pkgs[@]} -eq 0 ]; then
_msg "LXDE Custom" "No extra packages selected — installing the\nfixed base only (equivalent to LXDE Core)." 10 60
fi
# Fixed base: lxde-core + xserver-xorg always; lightdm only
# when no display manager is configured yet — never force it
# over an existing one.
local -a base_pkgs=(lxde-core xserver-xorg)
local install_dm=0 keep_dm
keep_dm=$(_existing_display_manager)
if [ -z "$keep_dm" ]; then
base_pkgs+=(lightdm)
install_dm=1
else
echo -e "${YELLOW}Display manager '${keep_dm}' is already configured — keeping it as default.${NC}"
fi
# Pre-apply summary: confirm base + selection before any
# debconf or apt transaction starts.
_confirm_install_list "LXDE Custom" "${base_pkgs[@]}" "${lxde_pkgs[@]}" || return
if [ "$install_dm" -eq 1 ]; then
echo "lightdm shared/default-x-display-manager select lightdm" | sudo debconf-set-selections
fi
_ensure_apt_updated
_run_cmd "LXDE Custom" "sudo apt install -y ${base_pkgs[*]} ${lxde_pkgs[*]}" \
"Installing LXDE base + selected packages..."
if [ "$install_dm" -eq 1 ]; then
sudo systemctl enable lightdm
fi
# Selection confirmed once above; release the
# per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}LXDE installed (base: ${base_pkgs[*]}).${NC}"
_offer_pipewire_after_desktop
}
-126
View File
@@ -1,126 +0,0 @@
#!/usr/bin/env bash
# desktop_xfce.sh — XFCE desktop installer (extracted from
# desktop_display.sh for architectural symmetry with
# desktop_gnome.sh and desktop_kde.sh).
# License GPL v3
xfce_menu() {
while true; do
local -a xf_items=()
xf_items+=("1" "XFCE (Full Meta Package)")
xf_items+=("2" "XFCE (Minimal Core)")
[ "$DEBIAN_VERSION" = "13" ] && xf_items+=("3" "XFCE Wayland (Experimental — labwc)")
xf_items+=("4" "XFCE Custom (Choose packages)")
xf_items+=("5" "Back")
local choice
choice=$(_menu "XFCE" \
"Select an option:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
"${xf_items[@]}")
[ -z "$choice" ] && break
clear
case "$choice" in
1) _install_xfce_full ;;
2) _install_xfce_minimal ;;
3) _install_xfce_wayland ;;
4) _install_xfce_custom ;;
5) break ;;
esac
done
}
_install_xfce_full() {
_run_cmd "XFCE Full" "sudo apt install -y xfce4 xfce4-goodies xfce4-power-manager" \
"Installing XFCE (Full Meta Package)..."
_xfce_polkit_rules
_offer_pipewire_after_desktop
}
_install_xfce_minimal() {
_run_cmd "XFCE Minimal" "sudo apt install -y xfce4" \
"Installing XFCE (Minimal Core)..."
_xfce_polkit_rules
_offer_pipewire_after_desktop
}
_install_xfce_wayland() {
_run_cmd "XFCE Wayland" "sudo apt install -y xfce4 labwc" \
"Installing XFCE + labwc (Wayland, experimental)..."
_msg "XFCE Wayland" "labwc is EXPERIMENTAL on XFCE 4.20.\n\nAfter reboot, select the Wayland session\nfrom the login screen." 12 65
_xfce_polkit_rules
_offer_pipewire_after_desktop
}
_install_xfce_custom() {
# BH-004: helper returns a space-separated selection;
# reparsed into an array to avoid word splitting and injection.
local cleaned
cleaned=$(_select_packages_checklist "XFCE Custom" \
"Select the XFCE packages to install:" \
"thunar|File manager" \
"xfdesktop4|Desktop manager" \
"xfwm4|Window manager" \
"xfce4-panel|Panel" \
"xfce4-terminal|Terminal emulator" \
"xfce4-screenshooter|Screenshot tool" \
"ristretto|Image viewer" \
"mousepad|Text editor" \
"xfce4-session|Session manager" \
"xfce4-settings|Settings manager" \
"xfce4-power-manager|Power manager")
[ -z "$cleaned" ] && return
local -a xfce_pkgs=()
local _pkg
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && xfce_pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Pre-apply summary: confirm the selection before
# the batch apt transaction starts.
_confirm_install_list "XFCE Custom" "${xfce_pkgs[@]}" || return
_run_cmd "XFCE Custom" "sudo apt install -y ${xfce_pkgs[*]}" \
"Installing selected XFCE packages..."
# Release the per-package prompt suppression.
_SELECTION_CONFIRMED=0
_xfce_polkit_rules
_offer_pipewire_after_desktop
}
_xfce_polkit_rules() {
is_installed xfce4-power-manager || return 0
local rules_dir="/etc/polkit-1/rules.d"
sudo mkdir -p "$rules_dir"
cat <<'EOF' | sudo tee "$rules_dir/85-suspend.rules" >/dev/null
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.login1.suspend" &&
subject.isInGroup("users")) {
return polkit.Result.YES;
}
});
EOF
cat <<'EOF' | sudo tee "$rules_dir/89-backlight.rules" >/dev/null
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.upower.backlight" &&
subject.isInGroup("backlight")) {
return polkit.Result.YES;
}
});
EOF
if ! getent group backlight >/dev/null 2>&1; then
sudo groupadd --system backlight || true
fi
# SECURITY: Validate that the target user is a real login user, not root.
# If SUDO_USER is empty (script run directly as root), fall back to the
# first non-system user from /etc/passwd, never to root.
local de_user="${SUDO_USER:-}"
if [ -z "$de_user" ] || [ "$de_user" = "root" ]; then
de_user=$(awk -F: '$3>=1000 && $3<65534 {print $1}' /etc/passwd | head -1)
fi
if [ -n "$de_user" ] && ! id -nG "$de_user" 2>/dev/null | grep -qw backlight; then
sudo usermod -aG backlight "$de_user" || true
fi
sudo systemctl restart polkit.service 2>/dev/null || true
echo -e "${GREEN}Polkit rules installed (suspend + backlight). User '${de_user}' added to 'backlight' group.${NC}"
}
+1 -4
View File
@@ -9,9 +9,6 @@ _load_extras() {
shopt -s nullglob
for _mod in "${_EXTRAS_DIR}"/*/*.sh "${_EXTRAS_DIR}"/*/*/*.sh; do
[ -f "$_mod" ] || continue
# Dynamic module discovery (glob): the concrete paths
# cannot be resolved statically.
# shellcheck disable=SC1090
source "$_mod"
done
shopt -u nullglob
@@ -42,7 +39,7 @@ install_extras() {
"7" "Code Editors & IDEs" \
"8" "Servers & Dev Tools" \
"9" "Security & Networking" \
"10" "Software Center & Flatpak" \
"10" "Software Centers" \
"11" "Office & Productivity" \
"12" "System Tools" \
"13" "Fetch / System Info" \
+19 -35
View File
@@ -6,7 +6,7 @@ _install_signal() {
if [ ! -f /etc/apt/sources.list.d/extrepo_signal.sources ]; then
_run_cmd "Signal" "sudo extrepo enable signal" "Enabling Signal repository..."
fi
_ensure_apt_updated
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
if ! is_installed "signal-desktop"; then
_run_cmd "Signal" "sudo apt install -y signal-desktop" "Installing Signal..."
echo -e "${GREEN}Signal installed.${NC}"
@@ -50,16 +50,13 @@ _cat_communication() {
fi
local -a items=()
local signal_state
signal_state=$(_state "signal-desktop")
local telegram_state
telegram_state=$(_state "telegram-desktop")
local hexchat_state
hexchat_state=$(_state "hexchat")
local signal_state; signal_state=$(_state "signal-desktop")
local telegram_state; telegram_state=$(_state "telegram-desktop")
local hexchat_state; hexchat_state=$(_state "hexchat")
items+=(
"signal-desktop" "Signal Private Messenger (extrepo)" "$signal_state"
"telegram-desktop" "Telegram Desktop messaging" "$telegram_state"
"hexchat" "IRC client (HexChat)" "$hexchat_state"
"signal-desktop" "Signal Private Messenger (extrepo)" "$signal_state"
"telegram-desktop" "Telegram Desktop messaging" "$telegram_state"
"hexchat" "IRC client (HexChat)" "$hexchat_state"
)
local item_count=${#items[@]}
@@ -70,33 +67,20 @@ _cat_communication() {
clear
[ -z "$choices" ] && return
local cleaned
cleaned=$(echo "$choices" | tr -d '"')
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Pre-apply summary: confirm the selection once,
# before any apt transaction starts.
_confirm_install_list "Communication" "${_pkgs[@]}" || return
for pkg in "${_pkgs[@]}"; do
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
for pkg in $cleaned; do
case $pkg in
signal-desktop) _install_signal ;;
telegram-desktop) _install_telegram ;;
hexchat)
if ! is_installed "hexchat"; then
_run_install "hexchat"
echo -e "${GREEN}hexchat installed.${NC}"
else
echo "hexchat already installed."
fi
;;
signal-desktop) _install_signal ;;
telegram-desktop) _install_telegram ;;
hexchat)
if ! is_installed "hexchat"; then
_run_install "hexchat"
echo -e "${GREEN}hexchat installed.${NC}"
else
echo "hexchat already installed."
fi
;;
esac
done
# Release the per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}Communication tools installed.${NC}"
}
+1 -13
View File
@@ -45,16 +45,7 @@ _cat_design() {
[ -z "$choices" ] && return
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Pre-apply summary: confirm the selection once,
# before any apt transaction starts.
_confirm_install_list "Multimedia & Design" "${_pkgs[@]}" || return
for pkg in "${_pkgs[@]}"; do
for pkg in $cleaned; do
if ! is_installed "$pkg"; then
_run_install "$pkg"
else
@@ -62,8 +53,5 @@ _cat_design() {
fi
done
# Release the per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}Multimedia & design tools installed.${NC}"
}
+3 -13
View File
@@ -2,6 +2,8 @@
# dev.sh — Development & Servers (extrepo, zenmap, fail2ban, ufw moved out)
_cat_dev() {
local headless=false
_is_headless && headless=true
local -a items=()
local apache_state; apache_state=$(_state "apache2")
local build_state; build_state=$(_state "build-essential")
@@ -48,16 +50,7 @@ _cat_dev() {
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Pre-apply summary: confirm the selection once,
# before any apt transaction starts.
_confirm_install_list "Development & Servers" "${_pkgs[@]}" || return
for pkg in "${_pkgs[@]}"; do
for pkg in $cleaned; do
case $pkg in
python3-pip)
local need=()
@@ -96,8 +89,5 @@ _cat_dev() {
esac
done
# Release the per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}Development tools and servers installed.${NC}"
}
+1 -1
View File
@@ -8,7 +8,7 @@ _enable_jellyfin_repo() {
fi
_run_cmd "Jellyfin" "sudo extrepo enable jellyfin" "Enabling Jellyfin repository..."
fi
_ensure_apt_updated
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
}
install_jellyfin() {
+1 -13
View File
@@ -62,16 +62,7 @@ _cat_download() {
[ -z "$cleaned" ] && { echo "No download tools selected."; return; }
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Pre-apply summary: confirm the selection once,
# before any apt transaction starts.
_confirm_install_list "Downloaders & Torrent Clients" "${_pkgs[@]}" || return
for pkg in "${_pkgs[@]}"; do
for pkg in $cleaned; do
case $pkg in
yt-dlp)
install_backports_or_stable yt-dlp
@@ -92,8 +83,5 @@ _cat_download() {
esac
done
# Release the per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}Download & network tools installed.${NC}"
}
+4 -4
View File
@@ -9,11 +9,11 @@ _quick_install() {
fetch_pkg="neofetch"
fi
local -a comp_pkgs
local comp_pkgs
if [ "$DEBIAN_CODENAME" = "trixie" ]; then
comp_pkgs=(7zip 7zip-rar)
comp_pkgs="7zip 7zip-rar"
else
comp_pkgs=(p7zip-full p7zip-rar)
comp_pkgs="p7zip-full p7zip-rar"
fi
_msg "Essential Pack" \
@@ -21,7 +21,7 @@ _quick_install() {
local quick_pkgs=(
zip unzip rar unrar
"${comp_pkgs[@]}"
$comp_pkgs
"$fetch_pkg" htop inxi curl wget ufw
ca-certificates gnupg lsb-release
)
+1 -13
View File
@@ -47,16 +47,7 @@ _cat_fetch() {
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Pre-apply summary: confirm the selection once,
# before any apt transaction starts.
_confirm_install_list "Fetch Tools" "${_pkgs[@]}" || return
for pkg in "${_pkgs[@]}"; do
for pkg in $cleaned; do
case $pkg in
neofetch|fastfetch)
if ! is_installed "$pkg"; then
@@ -75,8 +66,5 @@ _cat_fetch() {
esac
done
# Release the per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}Fetch tools installed.${NC}"
}
+127 -204
View File
@@ -16,13 +16,13 @@ _enable_mozilla_repo() {
_run_cmd "Mozilla" "sudo extrepo enable mozilla" "Enabling Mozilla repository..."
fi
if [ ! -f /etc/apt/preferences.d/mozilla ]; then
sudo tee /etc/apt/preferences.d/mozilla >/dev/null <<'EOF'
sudo tee /etc/apt/preferences.d/mozilla > /dev/null << 'EOF'
Package: *
Pin: origin packages.mozilla.org
Pin-Priority: 1000
EOF
fi
_ensure_apt_updated
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
}
_enable_floorp_repo() {
@@ -30,7 +30,7 @@ _enable_floorp_repo() {
_ensure_extrepo
_run_cmd "Floorp" "sudo extrepo enable floorp" "Enabling Floorp repository..."
fi
_ensure_apt_updated
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
}
install_palemoon() {
@@ -60,7 +60,7 @@ install_palemoon() {
else
_ensure_extrepo
_run_cmd "Pale Moon" "sudo extrepo enable ${REPO_PALEMOON}" "Enabling ${REPO_PALEMOON}..."
_ensure_apt_updated
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
fi
if ! is_installed "palemoon"; then
@@ -76,7 +76,7 @@ _enable_librewolf_repo() {
_ensure_extrepo
_run_cmd "LibreWolf" "sudo extrepo enable librewolf" "Enabling LibreWolf repository..."
fi
_ensure_apt_updated
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
}
_enable_tailscale_repo() {
@@ -84,7 +84,7 @@ _enable_tailscale_repo() {
_ensure_extrepo
_run_cmd "Tailscale" "sudo extrepo enable tailscale" "Enabling Tailscale repository..."
fi
_ensure_apt_updated
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
}
_enable_mullvad_repo() {
@@ -92,14 +92,14 @@ _enable_mullvad_repo() {
_ensure_extrepo
_run_cmd "Mullvad" "sudo extrepo enable mullvad" "Enabling Mullvad repository..."
fi
_ensure_apt_updated
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
}
install_protonvpn() {
if [ ! -f /etc/apt/sources.list.d/extrepo_protonvpn.sources ]; then
_ensure_extrepo
_run_cmd "ProtonVPN" "sudo extrepo enable protonvpn stable" "Enabling ProtonVPN repository (stable suite)..."
_ensure_apt_updated
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
else
echo "ProtonVPN repository already enabled."
fi
@@ -119,197 +119,143 @@ _cat_internet() {
_is_headless && headless=true
local -a items=()
if ! $headless; then
local chromium_state
chromium_state=$(_state "chromium")
local dillo_state
dillo_state=$(_state "dillo")
local epiphany_state
epiphany_state=$(_state "epiphany-browser")
local falkon_state
falkon_state=$(_state "falkon")
local chromium_state; chromium_state=$(_state "chromium")
local dillo_state; dillo_state=$(_state "dillo")
local epiphany_state; epiphany_state=$(_state "epiphany-browser")
local falkon_state; falkon_state=$(_state "falkon")
local firefox_state="OFF"
if command -v firefox &>/dev/null && ! is_installed "firefox-esr"; then
firefox_state="ON"
fi
local firefox_esr_state
firefox_esr_state=$(_state "firefox-esr")
local floorp_state
floorp_state=$(_state "floorp")
local konqueror_state
konqueror_state=$(_state "konqueror")
local librewolf_state
librewolf_state=$(_state "librewolf")
local palemoon_state
palemoon_state=$(_state "palemoon")
local privacybrowser_state
privacybrowser_state=$(_state "privacybrowser")
local qutebrowser_state
qutebrowser_state=$(_state "qutebrowser")
local thunderbird_state
thunderbird_state=$(_state "thunderbird")
local torbrowser_state
torbrowser_state=$(_state "torbrowser-launcher")
local mullvadbrowser_state
mullvadbrowser_state=$(_state "mullvad-browser")
local protonvpn_state
protonvpn_state=$(_state "protonvpn")
local floorp_state; floorp_state=$(_state "floorp")
local konqueror_state; konqueror_state=$(_state "konqueror")
local librewolf_state; librewolf_state=$(_state "librewolf")
local palemoon_state; palemoon_state=$(_state "palemoon")
local privacybrowser_state; privacybrowser_state=$(_state "privacybrowser")
local qutebrowser_state; qutebrowser_state=$(_state "qutebrowser")
local thunderbird_state; thunderbird_state=$(_state "thunderbird")
local torbrowser_state; torbrowser_state=$(_state "torbrowser-launcher")
local mullvadbrowser_state; mullvadbrowser_state=$(_state "mullvad-browser")
local protonvpn_state; protonvpn_state=$(_state "protonvpn")
items+=(
"chromium" "Chromium web browser" "$chromium_state"
"dillo" "Lightweight graphical browser" "$dillo_state"
"epiphany-browser" "GNOME web browser" "$epiphany_state"
"falkon" "KDE web browser (QtWebEngine)" "$falkon_state"
"firefox" "Firefox from Mozilla (replaces ESR)" "$firefox_state"
"firefox-esr" "Firefox ESR (official Debian + locale auto)" "$firefox_esr_state"
"floorp" "Firefox-based browser (extrepo)" "$floorp_state"
"konqueror" "KDE file manager / web browser" "$konqueror_state"
"librewolf" "Privacy-focused Firefox fork (extrepo)" "$librewolf_state"
"palemoon" "Classic Firefox-derived browser (extrepo)" "$palemoon_state"
"privacybrowser" "Privacy-focused web browser" "$privacybrowser_state"
"qutebrowser" "Keyboard-driven browser (Qt)" "$qutebrowser_state"
"thunderbird" "Email client" "$thunderbird_state"
"torbrowser-launcher" "Tor Browser launcher" "$torbrowser_state"
"mullvad-browser" "Mullvad privacy browser" "$mullvadbrowser_state"
"protonvpn" "ProtonVPN client" "$protonvpn_state"
"chromium" "Chromium web browser" "$chromium_state"
"dillo" "Lightweight graphical browser" "$dillo_state"
"epiphany-browser" "GNOME web browser" "$epiphany_state"
"falkon" "KDE web browser (QtWebEngine)" "$falkon_state"
"firefox" "Firefox from Mozilla (replaces ESR)" "$firefox_state"
"firefox-esr" "Firefox ESR (official Debian + locale auto)" "$firefox_esr_state"
"floorp" "Firefox-based browser (extrepo)" "$floorp_state"
"konqueror" "KDE file manager / web browser" "$konqueror_state"
"librewolf" "Privacy-focused Firefox fork (extrepo)" "$librewolf_state"
"palemoon" "Classic Firefox-derived browser (extrepo)" "$palemoon_state"
"privacybrowser" "Privacy-focused web browser" "$privacybrowser_state"
"qutebrowser" "Keyboard-driven browser (Qt)" "$qutebrowser_state"
"thunderbird" "Email client" "$thunderbird_state"
"torbrowser-launcher" "Tor Browser launcher" "$torbrowser_state"
"mullvad-browser" "Mullvad privacy browser" "$mullvadbrowser_state"
"protonvpn" "ProtonVPN client" "$protonvpn_state"
)
fi
local elinks_state
elinks_state=$(_state "elinks")
local riseupvpn_state
riseupvpn_state=$(_state "riseup-vpn")
local w3m_state
w3m_state=$(_state "w3m")
local tailscale_state
tailscale_state=$(_state "tailscale")
local mullvad_state
mullvad_state=$(_state "mullvad-vpn")
local elinks_state; elinks_state=$(_state "elinks")
local riseupvpn_state; riseupvpn_state=$(_state "riseup-vpn")
local w3m_state; w3m_state=$(_state "w3m")
local tailscale_state; tailscale_state=$(_state "tailscale")
local mullvad_state; mullvad_state=$(_state "mullvad-vpn")
items+=(
"elinks" "Text-mode web browser" "$elinks_state"
"riseup-vpn" "Riseup VPN client" "$riseupvpn_state"
"w3m" "Text-mode browser + deps (w3m-img)" "$w3m_state"
"tailscale" "Zero-config VPN & mesh networking" "$tailscale_state"
"mullvad-vpn" "Mullvad VPN client (WireGuard)" "$mullvad_state"
"elinks" "Text-mode web browser" "$elinks_state"
"riseup-vpn" "Riseup VPN client" "$riseupvpn_state"
"w3m" "Text-mode browser + deps (w3m-img)" "$w3m_state"
"tailscale" "Zero-config VPN & mesh networking" "$tailscale_state"
"mullvad-vpn" "Mullvad VPN client (WireGuard)" "$mullvad_state"
)
local item_count=${#items[@]}
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
local choices
choices=$(
_checklist "Internet" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
"${items[@]}"
)
choices=$(_checklist "Internet" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
"${items[@]}" \
)
clear
[ -z "$choices" ] && return
local cleaned
cleaned=$(echo "$choices" | tr -d '"')
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
local has_firefox=false
local has_firefox_esr=false
local fchoice=""
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for _p in "${_pkgs[@]}"; do
[ "$_p" = "firefox" ] && has_firefox=true
[ "$_p" = "firefox-esr" ] && has_firefox_esr=true
done
if $has_firefox && $has_firefox_esr; then
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" \
"both" "Keep both variants")
[ -z "$fchoice" ] && return
case "$fchoice" in
mozilla) cleaned=$(printf '%s\n' "${_pkgs[@]}" | grep -vx "firefox-esr" | tr '\n' ' ') ;;
esr) cleaned=$(printf '%s\n' "${_pkgs[@]}" | grep -vx "firefox" | tr '\n' ' ') ;;
esac
if echo "$cleaned" | grep -q "firefox" && echo "$cleaned" | grep -q "firefox-esr"; then
_msg "Firefox" "You selected both Firefox (Mozilla) and Firefox ESR.\nPlease choose only one Firefox variant." 10 60
return
fi
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Pre-apply summary: confirm the selection once,
# before any apt transaction starts.
_confirm_install_list "Internet" "${_pkgs[@]}" || return
for pkg in "${_pkgs[@]}"; do
for pkg in $cleaned; do
case $pkg in
firefox)
install_firefox_mozilla "$fchoice"
;;
firefox-esr)
install_firefox_esr "$fchoice"
;;
floorp)
_enable_floorp_repo
_run_install floorp
echo -e "${GREEN}Floorp installed.${NC}"
;;
librewolf)
_enable_librewolf_repo
_run_install librewolf
echo -e "${GREEN}LibreWolf installed.${NC}"
;;
palemoon)
install_palemoon
;;
tailscale)
_enable_tailscale_repo
_run_install tailscale
echo -e "${GREEN}Tailscale installed.${NC}"
;;
mullvad-vpn)
_enable_mullvad_repo
_run_install mullvad-vpn
echo -e "${GREEN}Mullvad VPN installed.${NC}"
;;
mullvad-browser)
_enable_mullvad_repo
_run_install mullvad-browser
echo -e "${GREEN}Mullvad Browser installed.${NC}"
;;
protonvpn)
install_protonvpn
;;
riseup-vpn)
install_backports_or_stable riseup-vpn
;;
w3m)
local need=()
! is_installed "w3m" && need+=("w3m")
! is_installed "w3m-img" && need+=("w3m-img")
! is_installed "ca-certificates" && need+=("ca-certificates")
! is_installed "xsel" && need+=("xsel")
if [ ${#need[@]} -gt 0 ]; then
_run_install_batch "${need[@]}"
else
echo "w3m already installed."
fi
;;
*)
if ! is_installed "$pkg"; then
_run_install "$pkg"
else
echo "$pkg already installed."
fi
;;
firefox)
install_firefox_mozilla
;;
firefox-esr)
install_firefox_esr
;;
floorp)
_enable_floorp_repo
_run_install floorp
echo -e "${GREEN}Floorp installed.${NC}"
;;
librewolf)
_enable_librewolf_repo
_run_install librewolf
echo -e "${GREEN}LibreWolf installed.${NC}"
;;
palemoon)
install_palemoon
;;
tailscale)
_enable_tailscale_repo
_run_install tailscale
echo -e "${GREEN}Tailscale installed.${NC}"
;;
mullvad-vpn)
_enable_mullvad_repo
_run_install mullvad-vpn
echo -e "${GREEN}Mullvad VPN installed.${NC}"
;;
mullvad-browser)
_enable_mullvad_repo
_run_install mullvad-browser
echo -e "${GREEN}Mullvad Browser installed.${NC}"
;;
protonvpn)
install_protonvpn
;;
riseup-vpn)
install_backports_or_stable riseup-vpn
;;
w3m)
local need=()
! is_installed "w3m" && need+=("w3m")
! is_installed "w3m-img" && need+=("w3m-img")
! is_installed "ca-certificates" && need+=("ca-certificates")
! is_installed "xsel" && need+=("xsel")
if [ ${#need[@]} -gt 0 ]; then
_run_install_batch w3m w3m-img ca-certificates xsel
else
echo "w3m already installed."
fi
;;
*)
if ! is_installed "$pkg"; then
_run_install "$pkg"
else
echo "$pkg already installed."
fi
;;
esac
done
# Release the per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}Internet tools installed.${NC}"
}
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
@@ -321,24 +267,13 @@ install_firefox_mozilla() {
fi
if is_installed "firefox-esr"; then
case "$fchoice" in
both)
echo -e "${CYAN}[INFO] Keeping Firefox ESR (both selected).${NC}"
;;
mozilla)
echo "Removing Firefox ESR (as selected)..."
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
;;
*)
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
else
echo "Keeping Firefox ESR."
return
fi
fi
_enable_mozilla_repo
@@ -347,31 +282,19 @@ 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
case "$fchoice" in
both)
echo -e "${CYAN}[INFO] Keeping Mozilla Firefox (both selected).${NC}"
;;
esr)
echo "Removing Mozilla Firefox (as selected)..."
if _confirm "Firefox" "Mozilla Firefox is installed.\nRemove it before installing Firefox ESR?"; then
echo "Removing Mozilla Firefox..."
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
else
echo "Keeping Mozilla Firefox."
return
fi
fi
_run_cmd "Firefox ESR" "sudo apt install -y firefox-esr" "Installing Firefox ESR..."
+7 -26
View File
@@ -10,40 +10,24 @@ _enable_temurin_repo() {
_run_cmd "extrepo" "sudo apt install -y extrepo" "Installing extrepo..."
fi
_run_cmd "Temurin" "sudo extrepo enable temurin" "Enabling Adoptium Temurin repository..."
_ensure_apt_updated
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
}
install_minecraft_java() {
local choices
choices=$(_checklist "Java Runtimes for Minecraft" \
"Select Java version(s) to install:" 15 65 4 \
"8" "Java 8 — Classic mods & Minecraft <= 1.16.5" OFF \
"Select Java version(s) to install:" 14 65 3 \
"8" "Java 8 — Classic mods & Minecraft <= 1.16.5" OFF \
"17" "Java 17 — Minecraft 1.17 to 1.20.4" ON \
"21" "Java 21 — Modern Minecraft >= 1.20.5 & 1.21+" OFF \
"25" "Java 25 — Minecraft 26+" OFF)
[ -z "$choices" ] && {
echo "No Java version selected."
return
}
"21" "Java 21 — Modern Minecraft >= 1.20.5 & 1.21+" OFF)
[ -z "$choices" ] && { echo "No Java version selected."; return; }
_enable_temurin_repo
local cleaned
cleaned=$(echo "$choices" | tr -d '"')
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Pre-apply summary: confirm the selection once,
# before any apt transaction starts.
_confirm_install_list "Java Runtimes for Minecraft" "${_pkgs[@]}" || return
for ver in "${_pkgs[@]}"; do
for ver in $cleaned; do
_run_cmd "Java" "sudo apt install -y temurin-${ver}-jre" \
"Installing Temurin JRE ${ver}..."
done
# Release the per-package prompt suppression.
_SELECTION_CONFIRMED=0
}
_install_dev_java() {
@@ -53,10 +37,7 @@ _install_dev_java() {
"17" "Java 17 LTS Development Kit" \
"21" "Java 21 LTS Development Kit" \
"25" "Java 25 LTS Development Kit")
[ -z "$ver" ] && {
echo "No JDK version selected."
return
}
[ -z "$ver" ] && { echo "No JDK version selected."; return; }
_enable_temurin_repo
_run_install "temurin-${ver}-jdk"
}
+12 -26
View File
@@ -10,7 +10,7 @@ _enable_onlyoffice_repo() {
fi
_run_cmd "OnlyOffice" "sudo extrepo enable onlyoffice-desktopeditors" "Enabling OnlyOffice repository..."
fi
_ensure_apt_updated
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
}
install_onlyoffice() {
@@ -36,7 +36,7 @@ install_joplin() {
if [ ! -f /etc/apt/sources.list.d/extrepo_joplin.sources ]; then
_run_cmd "Joplin" "sudo extrepo enable joplin" "Enabling Joplin repository..."
fi
_ensure_apt_updated
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
if ! is_installed "joplin"; then
_run_cmd "Joplin" "sudo apt install -y joplin" "Installing Joplin..."
echo -e "${GREEN}Joplin installed.${NC}"
@@ -84,8 +84,8 @@ _cat_office() {
local -a items=()
if ! $headless; then
items+=(
"onlyoffice" "OnlyOffice Desktop Editors (extrepo)" OFF
"libreoffice" "LibreOffice (backports on Bookworm/Trixie)" OFF
"onlyoffice" "OnlyOffice Desktop Editors (extrepo)" OFF
"libreoffice" "LibreOffice (backports on Bookworm/Trixie)" OFF
)
if [ "$DEBIAN_VERSION" = "12" ] || [ "$DEBIAN_VERSION" = "13" ]; then
items+=(
@@ -97,32 +97,18 @@ _cat_office() {
local item_count=${#items[@]}
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
local choices
choices=$(
_checklist "Office & Productivity" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
"${items[@]}"
)
choices=$(_checklist "Office & Productivity" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
"${items[@]}" \
)
[ -z "$choices" ] && return
local cleaned
cleaned=$(echo "$choices" | tr -d '"')
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Pre-apply summary: confirm the selection once,
# before any apt transaction starts.
_confirm_install_list "Office & Productivity" "${_pkgs[@]}" || return
for pkg in "${_pkgs[@]}"; do
for pkg in $cleaned; do
case $pkg in
onlyoffice) install_onlyoffice ;;
libreoffice) install_libreoffice_bpo ;;
joplin) install_joplin ;;
onlyoffice) install_onlyoffice ;;
libreoffice) install_libreoffice_bpo ;;
joplin) install_joplin ;;
esac
done
# Release the per-package prompt suppression.
_SELECTION_CONFIRMED=0
}
+1 -13
View File
@@ -26,16 +26,7 @@ _cat_players() {
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Pre-apply summary: confirm the selection once,
# before any apt transaction starts.
_confirm_install_list "Media Players" "${_pkgs[@]}" || return
for pkg in "${_pkgs[@]}"; do
for pkg in $cleaned; do
if ! is_installed "$pkg"; then
_run_install "$pkg"
else
@@ -43,8 +34,5 @@ _cat_players() {
fi
done
# Release the per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}Media players installed.${NC}"
}
+39 -64
View File
@@ -5,92 +5,67 @@ _cat_programming() {
local headless=false
_is_headless && headless=true
local -a items=()
local vim_state
vim_state=$(_state "vim")
local neovim_state
neovim_state=$(_state "neovim")
local hx_state
hx_state=$(_state "hx")
local nano_state
nano_state=$(_state "nano")
local emacs_state
emacs_state=$(_state "emacs")
local vim_state; vim_state=$(_state "vim")
local neovim_state; neovim_state=$(_state "neovim")
local hx_state; hx_state=$(_state "hx")
local nano_state; nano_state=$(_state "nano")
local emacs_state; emacs_state=$(_state "emacs")
items+=(
"vim" "Classic terminal editor" "$vim_state"
"neovim" "Modern vim fork" "$neovim_state"
"hx" "Helix modal editor (Rust)" "$hx_state"
"nano" "Simple terminal editor" "$nano_state"
"emacs" "Extensible editor / IDE" "$emacs_state"
"vim" "Classic terminal editor" "$vim_state"
"neovim" "Modern vim fork" "$neovim_state"
"hx" "Helix modal editor (Rust)" "$hx_state"
"nano" "Simple terminal editor" "$nano_state"
"emacs" "Extensible editor / IDE" "$emacs_state"
)
if ! $headless; then
local vimgtk_state
vimgtk_state=$(_state "vim-gtk3")
local kate_state
kate_state=$(_state "kate")
local mousepad_state
mousepad_state=$(_state "mousepad")
local gedit_state
gedit_state=$(_state "gedit")
local geany_state
geany_state=$(_state "geany")
local gte_state
gte_state=$(_state "gnome-text-editor")
local vimgtk_state; vimgtk_state=$(_state "vim-gtk3")
local kate_state; kate_state=$(_state "kate")
local mousepad_state; mousepad_state=$(_state "mousepad")
local gedit_state; gedit_state=$(_state "gedit")
local geany_state; geany_state=$(_state "geany")
local gte_state; gte_state=$(_state "gnome-text-editor")
local codium_state="OFF"
if command -v codium &>/dev/null; then
codium_state="ON"
fi
items+=(
"vim-gtk3" "Vim with GTK3 GUI" "$vimgtk_state"
"kate" "KDE advanced text editor" "$kate_state"
"mousepad" "Xfce text editor" "$mousepad_state"
"gedit" "GNOME text editor" "$gedit_state"
"geany" "Lightweight IDE" "$geany_state"
"gnome-text-editor" "GNOME modern text editor" "$gte_state"
"vscodium" "VS Code open-source (extrepo)" "$codium_state"
"vim-gtk3" "Vim with GTK3 GUI" "$vimgtk_state"
"kate" "KDE advanced text editor" "$kate_state"
"mousepad" "Xfce text editor" "$mousepad_state"
"gedit" "GNOME text editor" "$gedit_state"
"geany" "Lightweight IDE" "$geany_state"
"gnome-text-editor" "GNOME modern text editor" "$gte_state"
"vscodium" "VS Code open-source (extrepo)" "$codium_state"
)
fi
local item_count=${#items[@]}
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
local choices
choices=$(
_checklist "Programming Applications" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
"${items[@]}"
)
choices=$(_checklist "Programming Applications" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
"${items[@]}" \
)
clear
[ -z "$choices" ] && return
local cleaned
cleaned=$(echo "$choices" | tr -d '"')
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Pre-apply summary: confirm the selection once,
# before any apt transaction starts.
_confirm_install_list "Programming Applications" "${_pkgs[@]}" || return
for pkg in "${_pkgs[@]}"; do
for pkg in $cleaned; do
case $pkg in
vscodium)
install_vscodium
;;
*)
if ! is_installed "$pkg"; then
_run_install "$pkg"
else
echo "$pkg already installed."
fi
;;
vscodium)
install_vscodium
;;
*)
if ! is_installed "$pkg"; then
_run_install "$pkg"
else
echo "$pkg already installed."
fi
;;
esac
done
# Release the per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}Programming applications installed.${NC}"
}
@@ -101,7 +76,7 @@ _enable_vscodium_repo() {
fi
_run_cmd "VSCodium" "sudo extrepo enable vscodium" "Enabling VSCodium repository..."
fi
_ensure_apt_updated
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
}
install_vscodium() {
+1 -13
View File
@@ -36,16 +36,7 @@ _cat_security() {
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Pre-apply summary: confirm the selection once,
# before any apt transaction starts.
_confirm_install_list "Security & Networking" "${_pkgs[@]}" || return
for pkg in "${_pkgs[@]}"; do
for pkg in $cleaned; do
case $pkg in
zenmap)
install_backports_or_stable zenmap
@@ -63,8 +54,5 @@ _cat_security() {
esac
done
# Release the per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}Security & networking tools installed.${NC}"
}
+35 -96
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env bash
# software_centers.sh — Standalone Software Center + Flatpak installer
# software_centers.sh — Standalone Software Center installer
_cat_software_centers() {
local headless=false
@@ -8,106 +7,46 @@ _cat_software_centers() {
echo "Software centers require a GUI — skipping."
return
fi
local de_type
de_type=$(_detect_desktop_type)
local sc_choice
sc_choice=$(_menu "Software Centers" "Choose a software store to install:" 12 65 3 \
"gnome-software" "Software Center for GNOME" \
"plasma-discover" "Software manager for Plasma" \
"synaptic" "Classic APT package manager (GTK)" \
)
[ -z "$sc_choice" ] && return
local -a items=()
local gs_state; gs_state=$(_state "gnome-software")
local pd_state; pd_state=$(_state "plasma-discover")
local sy_state; sy_state=$(_state "synaptic")
local fp_state; fp_state=$(_state "flatpak")
items+=(
"gnome-software" "Software Center for GNOME" "$gs_state"
"plasma-discover" "Software manager for Plasma" "$pd_state"
"synaptic" "Classic APT package manager (GTK)" "$sy_state"
"flatpak" "Flatpak sandbox + Flathub" "$fp_state"
)
if [ "$sc_choice" = "synaptic" ]; then
_run_cmd "Install" "sudo apt install -y synaptic" "Installing synaptic..."
echo -e "${GREEN}synaptic installed.${NC}"
return
fi
local item_count=${#items[@]}
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
local choices
choices=$(_checklist "Software Center & Flatpak" \
"Check [*] the packages you want installed.\n" \
$TUI_ALTO $TUI_ANCHO $lista_alto "${items[@]}")
clear
[ -z "$choices" ] && return
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Pre-apply summary: confirm the selection once,
# before any apt transaction starts.
_confirm_install_list "Software Center & Flatpak" "${_pkgs[@]}" || return
for pkg in "${_pkgs[@]}"; do
case $pkg in
gnome-software)
if [ "$de_type" = "qt" ]; then
_msg "Warning" "Warning: This store requires extra background libraries \
if { [ "$de_type" = "qt" ] && [ "$sc_choice" = "gnome-software" ]; } || \
{ [ "$de_type" = "gtk" ] && [ "$sc_choice" = "plasma-discover" ]; }; then
_msg "Warning" "Warning: This store requires extra background libraries \
and may look visually inconsistent with your current desktop environment."
! _confirm "Continue?" "Install anyway?" && continue
fi
_run_cmd "Install" "sudo apt install -y gnome-software" "Installing GNOME Software..."
echo -e "${GREEN}gnome-software installed.${NC}"
;;
plasma-discover)
if [ "$de_type" = "gtk" ]; then
_msg "Warning" "Warning: This store requires extra background libraries \
and may look visually inconsistent with your current desktop environment."
! _confirm "Continue?" "Install anyway?" && continue
fi
_run_cmd "Install" "sudo apt install -y plasma-discover" "Installing Plasma Discover..."
echo -e "${GREEN}plasma-discover installed.${NC}"
;;
synaptic)
_run_cmd "Install" "sudo apt install -y synaptic" "Installing synaptic..."
echo -e "${GREEN}synaptic installed.${NC}"
;;
flatpak)
if ! is_installed "flatpak"; then
_run_cmd "Flatpak" "sudo apt install -y flatpak" "Installing Flatpak..."
else
echo "Flatpak already installed."
fi
flatpak remote-add --if-not-exists flathub \
https://dl.flathub.org/repo/flathub.flatpakrepo
echo "Flathub repository added."
! _confirm "Continue?" "Install anyway?" && return
fi
if command -v plasma-discover &>/dev/null; then
if ! is_installed "plasma-discover-backend-flatpak" 2>/dev/null; then
if _confirm "Discover Backend" "Install Flatpak backend for Plasma Discover?"; then
_run_cmd "Backend" "sudo apt install -y plasma-discover-backend-flatpak" "Installing Flatpak backend..."
fi
fi
elif command -v gnome-software &>/dev/null; then
if ! is_installed "gnome-software-plugin-flatpak" 2>/dev/null; then
if _confirm "GNOME Plugin" "Install Flatpak plugin for GNOME Software?"; then
_run_cmd "Plugin" "sudo apt install -y gnome-software-plugin-flatpak" "Installing Flatpak plugin..."
fi
fi
else
if [ "$de_type" = "qt" ]; then
if _confirm "Software Center" "Install Plasma Discover for Flatpak management?"; then
_run_cmd "Discover" "sudo apt install -y plasma-discover plasma-discover-backend-flatpak" "Installing Discover..."
fi
else
if _confirm "Software Center" "Install GNOME Software for Flatpak management?"; then
_run_cmd "GNOME Software" "sudo apt install -y gnome-software gnome-software-plugin-flatpak" "Installing GNOME Software..."
fi
fi
fi
echo -e "${GREEN}A reboot is recommended.${NC}"
;;
esac
done
_run_cmd "Install" "sudo apt install -y $sc_choice" "Installing ${sc_choice}..."
# Release the per-package prompt suppression.
_SELECTION_CONFIRMED=0
if _confirm "Flatpak Support" "Do you want to enable Flatpak support for this software center?"; then
local bpkg
if [ "$sc_choice" = "gnome-software" ]; then
bpkg="gnome-software-plugin-flatpak"
else
bpkg="plasma-discover-backend-flatpak"
fi
if ! is_installed "flatpak"; then
_run_cmd "Flatpak" "sudo apt install -y flatpak" "Installing Flatpak..."
fi
_run_cmd "Plugin" "sudo apt install -y $bpkg" "Installing $bpkg..."
flatpak remote-add --if-not-exists flathub \
https://dl.flathub.org/repo/flathub.flatpakrepo
echo "Flathub repository added."
fi
echo -e "${GREEN}Software center & Flatpak setup complete.${NC}"
echo -e "${GREEN}Software store installed.${NC}"
}
+224 -232
View File
@@ -7,14 +7,10 @@ _detect_desktop_type() {
desktop="${desktop,,}"
case "$desktop" in
*kde* | *lxqt* | *razor* | *plasma*)
echo "qt"
return
;;
*gnome* | *xfce* | *cinnamon* | *mate* | *lxde* | *budgie* | *sway* | *hyprland* | *i3* | *bspwm* | *openbox* | *fluxbox*)
echo "gtk"
return
;;
*kde*|*lxqt*|*razor*|*plasma*)
echo "qt"; return ;;
*gnome*|*xfce*|*cinnamon*|*mate*|*lxde*|*budgie*|*sway*|*hyprland*|*i3*|*bspwm*|*openbox*|*fluxbox*)
echo "gtk"; return ;;
esac
echo "gtk"
@@ -24,267 +20,263 @@ _cat_general() {
local headless=false
_is_headless && headless=true
local -a items=()
local btop_state
btop_state=$(_state "btop")
local btop_state; btop_state=$(_state "btop")
local compress_state
if is_installed "zip" && is_installed "unzip" && is_installed "p7zip-full"; then
compress_state="ON"
else
compress_state="OFF"
fi
local cpufetch_state
cpufetch_state=$(_state "cpufetch")
local cpu_x_state
cpu_x_state=$(_state "cpu-x")
local cpufetch_state; cpufetch_state=$(_state "cpufetch")
local cpu_x_state; cpu_x_state=$(_state "cpu-x")
local curl_wget_state
if is_installed "curl" && is_installed "wget"; then
curl_wget_state="ON"
else
curl_wget_state="OFF"
fi
local extrepo_state
extrepo_state=$(_state "extrepo")
local fwupd_state
fwupd_state=$(_state "fwupd")
local htop_state
htop_state=$(_state "htop")
local inxi_state
inxi_state=$(_state "inxi")
local jq_state
jq_state=$(_state "jq")
local kvm_state
kvm_state=$(_state "virt-manager")
local lshw_state
lshw_state=$(_state "lshw")
local mc_state
mc_state=$(_state "mc")
local nala_state
nala_state=$(_state "nala")
local ncdu_state
ncdu_state=$(_state "ncdu")
local tmux_state
tmux_state=$(_state "tmux")
local wine_state
wine_state=$(_state "wine")
local nvme_state
nvme_state=$(_state "nvme-cli")
local extrepo_state; extrepo_state=$(_state "extrepo")
local flatpak_state; flatpak_state=$(_state "flatpak")
local fwupd_state; fwupd_state=$(_state "fwupd")
local htop_state; htop_state=$(_state "htop")
local inxi_state; inxi_state=$(_state "inxi")
local jq_state; jq_state=$(_state "jq")
local kvm_state; kvm_state=$(_state "virt-manager")
local lshw_state; lshw_state=$(_state "lshw")
local mc_state; mc_state=$(_state "mc")
local nala_state; nala_state=$(_state "nala")
local ncdu_state; ncdu_state=$(_state "ncdu")
local tmux_state; tmux_state=$(_state "tmux")
local wine_state; wine_state=$(_state "wine")
local nvme_state; nvme_state=$(_state "nvme-cli")
items+=(
"btop" "Resource monitor (fancy top)" "$btop_state"
"compress" "Compression tools (zip, unrar, 7z)" "$compress_state"
"cpufetch" "CPU info fetcher" "$cpufetch_state"
"cpu-x" "CPU-X (alternative to CPU-Z)" "$cpu_x_state"
"curl-wget" "HTTP transfer tools (curl, wget)" "$curl_wget_state"
"extrepo" "External repository manager" "$extrepo_state"
"fwupd" "Firmware update daemon" "$fwupd_state"
"htop" "Interactive process viewer" "$htop_state"
"inxi" "System information tool" "$inxi_state"
"jq" "JSON command-line processor" "$jq_state"
"kvm" "QEMU/KVM virtualization" "$kvm_state"
"lshw" "List hardware details" "$lshw_state"
"mc" "Midnight Commander (file manager)" "$mc_state"
"nala" "APT frontend (parallel downloads)" "$nala_state"
"ncdu" "Disk usage analyzer (ncurses)" "$ncdu_state"
"nvme-cli" "NVMe SSD health monitoring" "$nvme_state"
"tmux" "Terminal multiplexer" "$tmux_state"
"wine" "Windows compatibility layer" "$wine_state"
"btop" "Resource monitor (fancy top)" "$btop_state"
"compress" "Compression tools (zip, unrar, 7z)" "$compress_state"
"cpufetch" "CPU info fetcher" "$cpufetch_state"
"cpu-x" "CPU-X (alternative to CPU-Z)" "$cpu_x_state"
"curl-wget" "HTTP transfer tools (curl, wget)" "$curl_wget_state"
"extrepo" "External repository manager" "$extrepo_state"
"flatpak" "Flatpak sandbox + Flathub" "$flatpak_state"
"fwupd" "Firmware update daemon" "$fwupd_state"
"htop" "Interactive process viewer" "$htop_state"
"inxi" "System information tool" "$inxi_state"
"jq" "JSON command-line processor" "$jq_state"
"kvm" "QEMU/KVM virtualization" "$kvm_state"
"lshw" "List hardware details" "$lshw_state"
"mc" "Midnight Commander (file manager)" "$mc_state"
"nala" "APT frontend (parallel downloads)" "$nala_state"
"ncdu" "Disk usage analyzer (ncurses)" "$ncdu_state"
"nvme-cli" "NVMe SSD health monitoring" "$nvme_state"
"tmux" "Terminal multiplexer" "$tmux_state"
"wine" "Windows compatibility layer" "$wine_state"
)
if ! $headless; then
local bleachbit_state
bleachbit_state=$(_state "bleachbit")
local conky_state
conky_state=$(_state "conky-all")
local gdebi_state
gdebi_state=$(_state "gdebi")
local corectrl_state
corectrl_state=$(_state "corectrl")
local dcgtk_state
dcgtk_state=$(_state "doublecmd-gtk")
local dcqt_state
dcqt_state=$(_state "doublecmd-qt")
local disks_state
disks_state=$(_state "gnome-disk-utility")
local gparted_state
gparted_state=$(_state "gparted")
local hardinfo_state
hardinfo_state=$(_state "hardinfo")
local psensor_state
psensor_state=$(_state "psensor")
local timeshift_state
timeshift_state=$(_state "timeshift")
local bleachbit_state; bleachbit_state=$(_state "bleachbit")
local conky_state; conky_state=$(_state "conky")
local gdebi_state; gdebi_state=$(_state "gdebi")
local corectrl_state; corectrl_state=$(_state "corectrl")
local dcgtk_state; dcgtk_state=$(_state "doublecmd-gtk")
local dcqt_state; dcqt_state=$(_state "doublecmd-qt")
local disks_state; disks_state=$(_state "gnome-disk-utility")
local gparted_state; gparted_state=$(_state "gparted")
local hardinfo_state; hardinfo_state=$(_state "hardinfo")
local psensor_state; psensor_state=$(_state "psensor")
local timeshift_state; timeshift_state=$(_state "timeshift")
items+=(
"bleachbit" "System cleaner (GUI)" "$bleachbit_state"
"conky-all" "System monitor for desktop" "$conky_state"
"gdebi" "Install .deb packages with deps" "$gdebi_state"
"corectrl" "AMD GPU control (CoreCtrl)" "$corectrl_state"
"doublecmd-gtk" "Dual-panel file manager (GTK)" "$dcgtk_state"
"doublecmd-qt" "Dual-panel file manager (Qt)" "$dcqt_state"
"gnome-disk-utility" "Disk management GUI" "$disks_state"
"gparted" "GNOME partition editor" "$gparted_state"
"hardinfo" "Graphical system profiler" "$hardinfo_state"
"psensor" "Hardware temperature monitor" "$psensor_state"
"timeshift" "System restore snapshots" "$timeshift_state"
"bleachbit" "System cleaner (GUI)" "$bleachbit_state"
"conky" "System monitor for desktop" "$conky_state"
"gdebi" "Install .deb packages with deps" "$gdebi_state"
"corectrl" "AMD GPU control (CoreCtrl)" "$corectrl_state"
"doublecmd-gtk" "Dual-panel file manager (GTK)" "$dcgtk_state"
"doublecmd-qt" "Dual-panel file manager (Qt)" "$dcqt_state"
"gnome-disk-utility" "Disk management GUI" "$disks_state"
"gparted" "GNOME partition editor" "$gparted_state"
"hardinfo" "Graphical system profiler" "$hardinfo_state"
"psensor" "Hardware temperature monitor" "$psensor_state"
"timeshift" "System restore snapshots" "$timeshift_state"
)
fi
local item_count=${#items[@]}
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
local choices
choices=$(
_checklist "System Tools" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
"${items[@]}"
)
choices=$(_checklist "System Tools" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
"${items[@]}" \
)
clear
[ -z "$choices" ] && return
local cleaned
cleaned=$(echo "$choices" | tr -d '"')
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Pre-apply summary: confirm the selection once,
# before any apt transaction starts.
_confirm_install_list "System Tools" "${_pkgs[@]}" || return
for pkg in "${_pkgs[@]}"; do
for pkg in $cleaned; do
case $pkg in
compress)
local need=()
! is_installed "zip" && need+=("zip")
! is_installed "unzip" && need+=("unzip")
! is_installed "rar" && need+=("rar")
! is_installed "unrar" && need+=("unrar")
! is_installed "p7zip-full" && need+=("p7zip-full")
! is_installed "p7zip-rar" && need+=("p7zip-rar")
if [ ${#need[@]} -gt 0 ]; then
_run_install_batch "${need[@]}"
echo -e "${GREEN}Compression utilities installed.${NC}"
fi
;;
curl-wget)
local need=()
! is_installed "curl" && need+=("curl")
! is_installed "wget" && need+=("wget")
if [ ${#need[@]} -gt 0 ]; then
_run_install_batch "${need[@]}"
else
echo "curl and wget already installed."
fi
;;
extrepo)
install_backports_or_stable extrepo
;;
fwupd)
if ! is_installed "fwupd"; then
_run_cmd "fwupd" "sudo apt install -y fwupd" "Installing fwupd..."
else
echo "fwupd already installed."
fi
if _confirm "Firmware Scan" "Scan for firmware updates now?\n\nThis will run:\n fwupdmgr refresh\n fwupdmgr get-updates\n fwupdmgr update (if available)"; then
_run_cmd "fwupd" "sudo fwupdmgr refresh --force" "Refreshing firmware metadata..."
echo ""
echo "Checking for firmware updates..."
local _fwupd_out
_fwupd_out=$(sudo fwupdmgr get-updates 2>&1 || true)
# Strict match: must not trigger on "No updates available"
# or "Devices with the latest available firmware version".
if echo "$_fwupd_out" | grep -Eq 'Upgrade available|New version:'; then
if _confirm "Firmware Update" "Firmware updates are available.\nInstall them now?"; then
_run_cmd "fwupd" "sudo fwupdmgr update -y" "Installing firmware updates..."
else
echo "Skipping firmware update."
_pause
compress)
local need=()
! is_installed "zip" && need+=("zip")
! is_installed "unzip" && need+=("unzip")
! is_installed "rar" && need+=("rar")
! is_installed "unrar" && need+=("unrar")
! is_installed "p7zip-full" && need+=("p7zip-full")
! is_installed "p7zip-rar" && need+=("p7zip-rar")
if [ ${#need[@]} -gt 0 ]; then
_run_install_batch "${need[@]}"
echo -e "${GREEN}Compression utilities installed.${NC}"
fi
;;
curl-wget)
local need=()
! is_installed "curl" && need+=("curl")
! is_installed "wget" && need+=("wget")
if [ ${#need[@]} -gt 0 ]; then
_run_install_batch "${need[@]}"
else
echo "curl and wget already installed."
fi
;;
extrepo)
install_backports_or_stable extrepo
;;
flatpak)
if ! is_installed "flatpak"; then
_run_cmd "Flatpak" "sudo apt install -y flatpak" "Installing Flatpak..."
else
echo "Flatpak already installed."
fi
flatpak remote-add --if-not-exists flathub \
https://dl.flathub.org/repo/flathub.flatpakrepo
echo "Flathub repository added."
if command -v plasma-discover &>/dev/null; then
if ! is_installed "plasma-discover-backend-flatpak" 2>/dev/null; then
if _confirm "Discover Backend" "Install Flatpak backend for Plasma Discover?"; then
_run_cmd "Backend" "sudo apt install -y plasma-discover-backend-flatpak" "Installing Flatpak backend..."
fi
fi
elif command -v gnome-software &>/dev/null; then
if ! is_installed "gnome-software-plugin-flatpak" 2>/dev/null; then
if _confirm "GNOME Plugin" "Install Flatpak plugin for GNOME Software?"; then
_run_cmd "Plugin" "sudo apt install -y gnome-software-plugin-flatpak" "Installing Flatpak plugin..."
fi
fi
else
echo "No firmware updates available."
local de_type
de_type=$(_detect_desktop_type)
if [ "$de_type" = "qt" ]; then
if _confirm "Software Center" "Install Plasma Discover for Flatpak management?"; then
_run_cmd "Discover" "sudo apt install -y plasma-discover plasma-discover-backend-flatpak" "Installing Discover..."
fi
else
if _confirm "Software Center" "Install GNOME Software for Flatpak management?"; then
_run_cmd "GNOME Software" "sudo apt install -y gnome-software gnome-software-plugin-flatpak" "Installing GNOME Software..."
fi
fi
fi
echo -e "${GREEN}A reboot is recommended.${NC}"
;;
fwupd)
if ! is_installed "fwupd"; then
_run_cmd "fwupd" "sudo apt install -y fwupd" "Installing fwupd..."
else
echo "fwupd already installed."
fi
if _confirm "Firmware Scan" "Scan for firmware updates now?\n\nThis will run:\n fwupdmgr refresh\n fwupdmgr get-updates\n fwupdmgr update (if available)"; then
_run_cmd "fwupd" "sudo fwupdmgr refresh --force" "Refreshing firmware metadata..."
echo ""
echo "Checking for firmware updates..."
sudo fwupdmgr get-updates 2>&1 || true
if sudo fwupdmgr get-updates 2>&1 | grep -q "available"; then
if _confirm "Firmware Update" "Firmware updates are available.\nInstall them now?"; then
_run_cmd "fwupd" "sudo fwupdmgr update -y" "Installing firmware updates..."
else
echo "Skipping firmware update."
_pause
fi
else
echo "No firmware updates available."
_pause
fi
fi
echo -e "${GREEN}fwupd setup complete.${NC}"
_pause
;;
kvm)
if ! is_installed "virt-manager"; then
_run_cmd "KVM" "sudo apt install -y qemu-system-x86 qemu-utils libvirt-daemon-system libvirt-clients bridge-utils virt-manager" "Installing KVM..."
sudo adduser "$USER" libvirt 2>/dev/null || true
sudo adduser "$USER" kvm 2>/dev/null || true
echo -e "${GREEN}QEMU/KVM installed. A reboot is recommended.${NC}"
else
echo "QEMU/KVM already installed."
fi
;;
wine)
if ! is_installed "wine64"; then
_run_cmd "Wine" "sudo apt install -y --no-install-recommends wine64 fonts-wine" "Installing Wine (64-bit only)..."
local wine_ver
wine_ver=$(wine --version 2>/dev/null)
if [ -n "$wine_ver" ]; then
echo -e "${GREEN}Wine (64-bit) installed: ${wine_ver}${NC}"
else
echo -e "${YELLOW}Wine installed but version check failed.${NC}"
fi
else
echo "Wine64 already installed."
fi
;;
nvme-cli)
if ! timeout 2 lsblk -d -o TRAN 2>/dev/null | grep -q "^nvme$"; then
echo "No NVMe controller detected. Skipping."
continue
fi
if ! is_installed "nvme-cli"; then
_run_cmd "nvme-cli" "sudo apt install -y nvme-cli" "Installing nvme-cli..."
fi
local nvme_devs=()
while read -r dev; do
nvme_devs+=("$dev")
done < <(timeout 2 lsblk -d -o NAME,TRAN 2>/dev/null | awk '$2 == "nvme" {print $1}')
if [ ${#nvme_devs[@]} -eq 0 ]; then
echo "No NVMe block devices found for health check."
continue
fi
local dev_list=""
local dev
for dev in "${nvme_devs[@]}"; do
[ -n "$dev_list" ] && dev_list+=", "
dev_list+="/dev/${dev}"
done
if _confirm "NVMe Health" "Run smart-log on ${#nvme_devs[@]} NVMe device(s): ${dev_list}?"; then
echo ""
for dev in "${nvme_devs[@]}"; do
local cw="" temp="" pu=""
while IFS= read -r line; do
case "$line" in
*critical_warning*) cw="${line##*: }" ;;
*temperature*) temp="${line##*: }" ;;
*percentage_used*) pu="${line##*: }" ;;
esac
done < <(sudo nvme smart-log "/dev/${dev}" 2>/dev/null || true)
echo -e "${YELLOW}━━━ /dev/${dev} ━━━${NC}"
echo -e " ${GREEN}Critical Warning:${NC} ${cw:-N/A}"
echo -e " ${GREEN}Temperature:${NC} ${temp:-N/A}"
echo -e " ${GREEN}Percentage Used:${NC} ${pu:-N/A}"
echo ""
done
_pause
fi
fi
echo -e "${GREEN}fwupd setup complete.${NC}"
_pause
;;
kvm)
if ! is_installed "virt-manager"; then
_run_cmd "KVM" "sudo apt install -y qemu-system-x86 qemu-utils libvirt-daemon-system libvirt-clients bridge-utils virt-manager" "Installing KVM..."
sudo adduser "$USER" libvirt 2>/dev/null || true
sudo adduser "$USER" kvm 2>/dev/null || true
echo -e "${GREEN}QEMU/KVM installed. A reboot is recommended.${NC}"
else
echo "QEMU/KVM already installed."
fi
;;
wine)
if ! is_installed "wine64"; then
_run_cmd "Wine" "sudo apt install -y --no-install-recommends wine64 fonts-wine" "Installing Wine (64-bit only)..."
local wine_ver
wine_ver=$(wine --version 2>/dev/null)
if [ -n "$wine_ver" ]; then
echo -e "${GREEN}Wine (64-bit) installed: ${wine_ver}${NC}"
;;
*)
if ! is_installed "$pkg"; then
_run_install "$pkg"
else
echo -e "${YELLOW}Wine installed but version check failed.${NC}"
echo "$pkg already installed."
fi
else
echo "Wine64 already installed."
fi
;;
nvme-cli)
if ! timeout 2 lsblk -d -o TRAN 2>/dev/null | grep -q "^nvme$"; then
echo "No NVMe controller detected. Skipping."
continue
fi
if ! is_installed "nvme-cli"; then
_run_cmd "nvme-cli" "sudo apt install -y nvme-cli" "Installing nvme-cli..."
fi
local nvme_devs=()
while read -r dev; do
nvme_devs+=("$dev")
done < <(timeout 2 lsblk -d -o NAME,TRAN 2>/dev/null | awk '$2 == "nvme" {print $1}')
if [ ${#nvme_devs[@]} -eq 0 ]; then
echo "No NVMe block devices found for health check."
continue
fi
local dev_list=""
local dev
for dev in "${nvme_devs[@]}"; do
[ -n "$dev_list" ] && dev_list+=", "
dev_list+="/dev/${dev}"
done
if _confirm "NVMe Health" "Run smart-log on ${#nvme_devs[@]} NVMe device(s): ${dev_list}?"; then
echo ""
for dev in "${nvme_devs[@]}"; do
local cw="" temp="" pu=""
while IFS= read -r line; do
case "$line" in
*critical_warning*) cw="${line##*: }" ;;
*temperature*) temp="${line##*: }" ;;
*percentage_used*) pu="${line##*: }" ;;
esac
done < <(sudo nvme smart-log "/dev/${dev}" 2>/dev/null || true)
echo -e "${YELLOW}━━━ /dev/${dev} ━━━${NC}"
echo -e " ${GREEN}Critical Warning:${NC} ${cw:-N/A}"
echo -e " ${GREEN}Temperature:${NC} ${temp:-N/A}"
echo -e " ${GREEN}Percentage Used:${NC} ${pu:-N/A}"
echo ""
done
_pause
fi
;;
*)
if ! is_installed "$pkg"; then
_run_install "$pkg"
else
echo "$pkg already installed."
fi
;;
;;
esac
done
# Release the per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}System tools installed.${NC}"
_pause
}
+1 -13
View File
@@ -33,24 +33,12 @@ _cat_terminals() {
clear
[ -z "$choices" ] && return
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$choices" | tr -d '"' | tr ' ' '\n')
# Pre-apply summary: confirm the selection once,
# before any apt transaction starts.
_confirm_install_list "Terminals" "${_pkgs[@]}" || return
for pkg in "${_pkgs[@]}"; do
for pkg in $(echo "$choices" | tr -d '"'); do
if ! is_installed "$pkg"; then
_run_install "$pkg"
else
echo "$pkg already installed."
fi
done
# Release the per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}Terminals installed.${NC}"
}
+1 -13
View File
@@ -34,16 +34,7 @@ _cat_cursors() {
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Pre-apply summary: confirm the selection once,
# before any apt transaction starts.
_confirm_install_list "Cursor Themes" "${_pkgs[@]}" || return
for pkg in "${_pkgs[@]}"; do
for pkg in $cleaned; do
if ! is_installed "$pkg"; then
_run_install "$pkg"
else
@@ -51,8 +42,5 @@ _cat_cursors() {
fi
done
# Release the per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}Cursor themes installed.${NC}"
}
@@ -36,16 +36,7 @@ _cat_themes() {
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Pre-apply summary: confirm the selection once,
# before any apt transaction starts.
_confirm_install_list "Desktop Themes (GTK/KDE)" "${_pkgs[@]}" || return
for pkg in "${_pkgs[@]}"; do
for pkg in $cleaned; do
if ! is_installed "$pkg"; then
_run_install "$pkg"
else
@@ -53,8 +44,5 @@ _cat_themes() {
fi
done
# Release the per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}Desktop themes installed.${NC}"
}
+1 -13
View File
@@ -38,16 +38,7 @@ _cat_fonts() {
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Pre-apply summary: confirm the selection once,
# before any apt transaction starts.
_confirm_install_list "Fonts" "${_pkgs[@]}" || return
for pkg in "${_pkgs[@]}"; do
for pkg in $cleaned; do
if ! is_installed "$pkg"; then
_run_install "$pkg"
else
@@ -55,8 +46,5 @@ _cat_fonts() {
fi
done
# Release the per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}Fonts installed.${NC}"
}
+1 -13
View File
@@ -53,16 +53,7 @@ _cat_icons() {
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Pre-apply summary: confirm the selection once,
# before any apt transaction starts.
_confirm_install_list "Icon Themes" "${_pkgs[@]}" || return
for pkg in "${_pkgs[@]}"; do
for pkg in $cleaned; do
if ! is_installed "$pkg"; then
_run_install "$pkg"
else
@@ -70,8 +61,5 @@ _cat_icons() {
fi
done
# Release the per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}Icon themes installed.${NC}"
}
+193 -182
View File
@@ -1,11 +1,74 @@
#!/usr/bin/env bash
# ── Global arrays ──
# Device arrays (PCI_NET_DEVS, USB_WIFI_DEVS, PCI_BT_DEVS, USB_BT_DEVS)
# and _FW_PLAN_HW_LINES are declared in hw_detect.sh.
PCI_NET_DEVS=()
USB_WIFI_DEVS=()
PCI_BT_DEVS=()
USB_BT_DEVS=()
_DETECTED_FW_PKGS=()
_FW_PLAN_HW_LINES=()
_FW_PLAN_PKG_LINES=()
# ── Network device detection (PCI + USB) ──
_detect_all_network_devices() {
! is_installed pciutils && _run_install_pkg pciutils
! is_installed usbutils && _run_install_pkg usbutils
PCI_NET_DEVS=()
while IFS= read -r line; do
PCI_NET_DEVS+=("$line")
done < <(timeout 2 lspci -nn 2>/dev/null | grep -iE 'network controller|ethernet controller' || true)
USB_WIFI_DEVS=()
while IFS= read -r line; do
if echo "$line" | grep -qiE 'wireless|wifi|802\.11|bluetooth|wlan'; then
USB_WIFI_DEVS+=("$line")
fi
done < <(lsusb 2>/dev/null || true)
PCI_BT_DEVS=()
while IFS= read -r line; do
PCI_BT_DEVS+=("$line")
done < <(timeout 2 lspci -nn 2>/dev/null | grep -i 'Bluetooth controller' || true)
USB_BT_DEVS=()
while IFS= read -r line; do
if echo "$line" | grep -qi 'bluetooth'; then
if ! echo "$line" | grep -qiE 'wireless|wifi|802\.11|wlan'; then
USB_BT_DEVS+=("$line")
fi
fi
done < <(lsusb 2>/dev/null || true)
_FW_PLAN_HW_LINES=()
for dev in "${PCI_NET_DEVS[@]}"; do
local desc dev_type
desc=$(echo "$dev" | sed -E 's/^[^ ]+ [^:]+: //; s/ \[[0-9a-fA-F]{4}:[0-9a-fA-F]{4}\]//; s/ \(rev.*\)//')
if echo "$dev" | grep -qiE 'network controller|wireless|wi-fi|wlan|802\.11'; then
dev_type="WiFi PCI"
else
dev_type="Ethernet PCI"
fi
_FW_PLAN_HW_LINES+=(" \xe2\x97\x8f ${desc} (${dev_type})")
done
for dev in "${USB_WIFI_DEVS[@]}"; do
local desc
desc=$(echo "$dev" | sed 's/^.*ID //')
_FW_PLAN_HW_LINES+=(" \xe2\x97\x8f ${desc} (USB)")
done
for dev in "${PCI_BT_DEVS[@]}"; do
local desc
desc=$(echo "$dev" | sed -E 's/^[^ ]+ [^:]+: //; s/ \[[0-9a-fA-F]{4}:[0-9a-fA-F]{4}\]//; s/ \(rev.*\)//')
_FW_PLAN_HW_LINES+=(" \xe2\x97\x8f ${desc} (Bluetooth PCI)")
done
for dev in "${USB_BT_DEVS[@]}"; do
local desc
desc=$(echo "$dev" | sed 's/^.*ID //')
_FW_PLAN_HW_LINES+=(" \xe2\x97\x8f ${desc} (Bluetooth USB)")
done
}
# ── Firmware package mapping ──
_detect_firmware_needs() {
local -A pkg_info
@@ -23,16 +86,16 @@ _detect_firmware_needs() {
[[ "$vendor_lc" != *intel* && "$vendor_lc" != *realtek* && "$vendor_lc" != *atheros* && "$vendor_lc" != *qualcomm* && "$vendor_lc" != *mediatek* ]] && continue
case "$vendor_lc" in
*intel*)
if echo "$dev" | grep -qiE 'wireless|wi-fi|wlan|802\.11'; then
pkg="firmware-iwlwifi"
else
pkg="firmware-intel-misc"
fi
;;
*realtek*) pkg="firmware-realtek" ;;
*atheros* | *qualcomm*) pkg="firmware-atheros" ;;
*mediatek*) pkg="firmware-mediatek" ;;
*intel*)
if echo "$dev" | grep -qiE 'wireless|wi-fi|wlan|802\.11'; then
pkg="firmware-iwlwifi"
else
pkg="firmware-intel-misc"
fi
;;
*realtek*) pkg="firmware-realtek" ;;
*atheros*|*qualcomm*) pkg="firmware-atheros" ;;
*mediatek*) pkg="firmware-mediatek" ;;
esac
local short_dev
@@ -52,6 +115,29 @@ _detect_firmware_needs() {
mapfile -t _DETECTED_FW_PKGS < <(printf '%s\n' "${_DETECTED_FW_PKGS[@]}" | sort -u)
}
# ── Broadcom chip classification helpers ──
_is_broadcom_brcm() {
local id="$1"
for supported in 4357 4358 4360 4727 43a0 43a1 43a2 43b1; do
[ "$id" = "$supported" ] && return 0
done
return 1
}
_is_broadcom_b43() {
local id="$1"
_is_broadcom_brcm "$id" && return 1
local dec=$((16#$id))
if [ "$dec" -ge $((16#4301)) ] && [ "$dec" -le $((16#4331)) ]; then return 0; fi
if [ "$dec" -ge $((16#4336)) ] && [ "$dec" -le $((16#4338)) ]; then return 0; fi
case "$id" in 4352) return 0 ;; esac
return 1
}
_is_broadcom_b43legacy() {
[ "$1" = "4302" ] || [ "$1" = "4306" ]
}
# ── Build plan string ──
_build_firmware_plan() {
local plan=""
@@ -73,7 +159,7 @@ _build_firmware_plan() {
local fw_line
if is_installed firmware-linux-nonfree; then
local cur_ver
cur_ver=$(_get_installed_version "firmware-linux-nonfree")
cur_ver=$(dpkg -l firmware-linux-nonfree 2>/dev/null | awk '/^ii/{print $3}')
fw_line=" [+] firmware-linux-nonfree ${cur_ver} (already installed)"
else
fw_line=" [+] firmware-linux-nonfree (base meta-package)"
@@ -90,8 +176,7 @@ _build_firmware_plan() {
if ! $has_bt; then
for dev in "${USB_WIFI_DEVS[@]}"; do
if echo "$dev" | grep -qi 'bluetooth'; then
has_bt=true
break
has_bt=true; break
fi
done
fi
@@ -104,14 +189,13 @@ _build_firmware_plan() {
plan+=" [+] bluez + bluez-tools + bluez-obexd (base stack)\n"
fi
case "${DESKTOP_ENV:-other}" in
kde)
plan+=" [+] bluedevil (KDE applet)\n"
if [ "${AUDIO_SERVER:-}" = "pipewire" ]; then
plan+=" → pipewire-pulse + wireplumber (if missing)\n"
fi
;;
gnome) plan+=" (already in gnome-control-center)\n" ;;
*) plan+=" [+] blueman (GTK Bluetooth manager)\n" ;;
kde) plan+=" [+] bluedevil (KDE applet)\n"
if [ "${AUDIO_SERVER:-}" = "pipewire" ]; then
plan+=" → pipewire-pulse + wireplumber (if missing)\n"
fi
;;
gnome) plan+=" (already in gnome-control-center)\n" ;;
*) plan+=" [+] blueman (GTK Bluetooth manager)\n" ;;
esac
plan+=" → Bluetooth service will be enabled\n"
else
@@ -135,7 +219,7 @@ _install_detected_firmware() {
continue
fi
local ver
ver=$(_get_pkg_version "$pkg")
ver=$(apt-cache policy "$pkg" 2>/dev/null | awk 'NR==3 {print $2; exit}')
if [ -z "$ver" ] || [ "$ver" = "(none)" ]; then
echo " --> $pkg not available in repositories, skipping."
continue
@@ -144,15 +228,13 @@ _install_detected_firmware() {
done
if [ ${#to_install[@]} -gt 0 ]; then
_run_cmd "Firmware" "sudo DEBIAN_FRONTEND=noninteractive apt install -y ${to_install[*]}" \
"Installing network firmware packages..." || true
"Installing network firmware packages..."
fi
}
# ── Wireless handler (Broadcom single-path wl) ──
# ── Wireless handler (Broadcom 3-tier) ──
_handle_wireless() {
local installed_any=false
local wl_build_failed=false
if [ ${#PCI_NET_DEVS[@]} -eq 0 ] && [ ${#USB_WIFI_DEVS[@]} -eq 0 ]; then
_detect_all_network_devices
fi
@@ -161,194 +243,122 @@ _handle_wireless() {
local bcm_id
bcm_id=$(echo "$dev" | grep -oP '14e4:[0-9a-fA-F]+' || true)
[ -z "$bcm_id" ] && continue
local dev_id
dev_id=$(echo "$bcm_id" | cut -d: -f2 | tr '[:upper:]' '[:lower:]')
# --- Dependencies verification ---
if ! is_installed "linux-headers-amd64" || ! is_installed "dkms"; then
if ! apt-cache show linux-headers-amd64 dkms >/dev/null 2>&1; then
_msg "Broadcom Error" "linux-headers-amd64 or dkms are not available in your repositories. Cannot compile Broadcom driver.\n\nEnsure repositories are enabled and run:\n sudo apt install linux-headers-amd64 dkms"
if _is_broadcom_brcm "$dev_id"; then
if ! is_installed "firmware-brcm80211"; then
_run_cmd "Broadcom" "sudo DEBIAN_FRONTEND=noninteractive apt install -y firmware-brcm80211" \
"Installing firmware-brcm80211..."
else
echo " --> firmware-brcm80211 already installed."
fi
installed_any=true
elif _is_broadcom_b43 "$dev_id"; then
if ! dpkg -l firmware-b43-installer >/dev/null 2>&1; then
_run_install_pkg firmware-b43-installer
fi
if [ ! -d /lib/firmware/b43 ] || [ -z "$(ls -A /lib/firmware/b43 2>/dev/null)" ]; then
_msg "b43 Firmware Warning" \
"The firmware-b43-installer package was installed, but the\n\
proprietary firmware download appears to have failed\n\
(no files found in /lib/firmware/b43).\n\n\
To fix this, connect a wired network and run:\n\
sudo dpkg-reconfigure firmware-b43-installer\n\n\
After the firmware is downloaded, reboot the system." 14 75
_pause
else
installed_any=true
fi
elif _is_broadcom_b43legacy "$dev_id"; then
_run_install_pkg firmware-b43legacy-installer
installed_any=true
else
local bcm_ver header_ver
bcm_ver=$(apt-cache policy broadcom-sta-dkms 2>/dev/null | awk 'NR==3 {print $2; exit}')
header_ver=$(apt-cache policy linux-headers-$(uname -r) 2>/dev/null | awk 'NR==3 {print $2; exit}')
if ! apt-cache policy "linux-headers-$(uname -r)" 2>/dev/null | grep -q "Candidate: [^ (none)]"; then
_msg "Broadcom Error" \
"linux-headers-$(uname -r) not available.\n\nCannot compile broadcom-sta-dkms without kernel headers." 10 60
_pause
continue
fi
fi
# --- Confirmation ---
if ! _confirm "Broadcom WiFi" "Detected Broadcom wireless device.\n\nInstall broadcom-sta-dkms, dkms, and wireless-tools?"; then
continue
fi
if _confirm "Broadcom WiFi" "Install Broadcom driver?\n\nRequired for this chipset. Compiles a kernel module.\n\n broadcom-sta-dkms ${bcm_ver:-unknown}\n linux-headers-$(uname -r) ${header_ver:-unknown}\n\nProceed?"; then
_run_cmd "Broadcom" "sudo DEBIAN_FRONTEND=noninteractive apt install -y linux-headers-$(uname -r) broadcom-sta-dkms" \
"Installing Broadcom driver..."
# --- Step-by-step installation ---
_run_cmd "Broadcom Deps" "sudo DEBIAN_FRONTEND=noninteractive apt install -y dkms wireless-tools linux-headers-amd64" || true
_run_cmd "Broadcom Driver" "sudo DEBIAN_FRONTEND=noninteractive apt install -y broadcom-sta-dkms" || true
# --- Persist blacklist of conflicting modules ---
local blacklist_conf="/etc/modprobe.d/blacklist-broadcom.conf"
local blacklist_content="blacklist b43\nblacklist b43legacy\nblacklist brcmsmac\nblacklist bcma\nblacklist ssb"
echo -e "$blacklist_content" | sudo tee "$blacklist_conf" >/dev/null || true
# --- Update initramfs and load module ---
_run_cmd "Initramfs" "sudo update-initramfs -u" || true
_run_cmd "Modprobe" "sudo modprobe -r b43 b43legacy b44 bcma brcmsmac brcmfmac ssb wl 2>/dev/null || true" "Removing conflicting modules" || true
_run_cmd "Modprobe" "sudo modprobe wl" || true
# --- Combo BT (unchanged) ---
local has_broadcom_bt=false
local btdev
for btdev in "${PCI_BT_DEVS[@]}"; do
if echo "$btdev" | grep -qi 'broadcom'; then
has_broadcom_bt=true
break
fi
done
if $has_broadcom_bt; then
echo -e "${YELLOW}[+] Broadcom WiFi+BT combo card detected.${NC}"
sudo mkdir -p /etc/modprobe.d
printf 'softdep wl post: btusb\n' | sudo tee /etc/modprobe.d/broadcom-combo.conf >/dev/null || true
echo -e "${YELLOW} A reboot may be required for Bluetooth support.${NC}"
fi
# --- Post-DKMS verification (Fix 5, unchanged) ---
if ! compgen -G "/lib/modules/$(uname -r)/updates/dkms/wl.ko*" >/dev/null; then
_msg "Broadcom DKMS Build Failed" "The wl module was not built by DKMS.\n\nPossible causes:\n- Missing build tools (build-essential, dkms)\n- Kernel update without headers\n- Incompatible kernel version\n\nTry: sudo dpkg-reconfigure broadcom-sta-dkms"
if _confirm "Broadcom" "Rebuild the Broadcom driver now?"; then
_run_cmd "Broadcom" "sudo dpkg-reconfigure broadcom-sta-dkms" || true
if compgen -G "/lib/modules/$(uname -r)/updates/dkms/wl.ko*" >/dev/null; then
:
else
local dmesg_out
dmesg_out=$(sudo dmesg | tail -20 2>/dev/null || echo "(dmesg unavailable)")
_msg "Broadcom DKMS Still Failed" "dpkg-reconfigure did not produce wl.ko either.\n\nLast kernel messages:\n${dmesg_out}\n\nYou may need to check build logs or report a bug against broadcom-sta-dkms."
_pause
wl_build_failed=true
continue
local has_broadcom_bt=false
for btdev in "${PCI_BT_DEVS[@]}"; do
if echo "$btdev" | grep -qi 'broadcom'; then
has_broadcom_bt=true
break
fi
done
if $has_broadcom_bt; then
echo -e "${YELLOW}Broadcom combo card (WiFi + Bluetooth) detected.${NC}"
cat > /etc/modprobe.d/broadcom-combo.conf <<'EOF'
# Broadcom combo: ensure btusb loads after wl
softdep wl post: btusb
EOF
echo -e "${YELLOW}A reboot may be required for Bluetooth to work correctly.${NC}"
fi
else
wl_build_failed=true
continue
echo "Broadcom proprietary driver installed. A reboot may be required."
_pause
installed_any=true
fi
fi
done
_msg "Network Warning" "The script is about to unload current WiFi kernel modules to load the Broadcom driver.\n\nIf you are connected via SSH over WiFi, YOUR CONNECTION WILL DROP. Please reconnect after a few seconds."
sudo modprobe -r b43 b43legacy b44 bcma brcmsmac brcmfmac ssb wl 2>/dev/null || true
sudo modprobe wl 2>/dev/null || true
# --- Load verification ---
if lsmod | grep -q '^wl '; then
echo -e "${GREEN}[+] Broadcom WiFi activated (wl module loaded).${NC}"
_pause
installed_any=true
else
echo -e "${YELLOW}[+] Broadcom driver installed but wl module did not load.${NC}"
echo -e "${YELLOW} A system reboot is required.${NC}"
_pause
installed_any=true
for dev in "${USB_WIFI_DEVS[@]}"; do
if echo "$dev" | grep -qi '0a5c'; then
_msg "USB Broadcom" "USB Broadcom adapter detected:\n\n ${dev}\n\nIf the adapter is not recognized after base firmware\ninstallation, ndiswrapper may be needed as a last\nresort.\n\nNote: Linux 5.15+ has improved support for many\nUSB Broadcom adapters." 12 65
fi
done
# --- USB Broadcom (unchanged) ---
local usb_dev
for usb_dev in "${USB_WIFI_DEVS[@]}"; do
if echo "$usb_dev" | grep -qi '0a5c'; then
_msg "USB Broadcom" "USB Broadcom device detected (0a5c).\n\nLinux does not have native drivers for most USB Broadcom WiFi chips.\nndiswrapper may be needed as a last resort."
_pause
fi
done
# --- Mensaje final (unchanged) ---
if ! $installed_any && ! $wl_build_failed; then
if ! $installed_any; then
echo "No special WiFi firmware needed -- base firmware-linux-nonfree covers this system."
_pause
fi
}
# ── 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 ] && _has_nonfree_component /etc/apt/sources.list; then
if [ -f /etc/apt/sources.list ] && grep -Eq '^[^#]*\bnon-free\b' /etc/apt/sources.list 2>/dev/null; then
nonfree_found=true
fi
if ! $nonfree_found && [ -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_found=true
break
fi
done
if grep -qr 'Components:.*\bnon-free\b' /etc/apt/sources.list.d/*.sources 2>/dev/null; then
nonfree_found=true
fi
fi
if $nonfree_found; then
return 0
fi
if ! _confirm "non-free Repository" "Component 'non-free' (and 'non-free-firmware') is required for WiFi/Bluetooth/GPU firmware.\n\nAdd them to your APT repositories?"; then
return 1
fi
# No active sources at all → bootstrap a complete configuration
if ! has_active_deb_sources; then
backup_current_repos
if ! bootstrap_repositories "main contrib non-free non-free-firmware"; then
return 1
fi
else
if _confirm "non-free Repository" "Component 'non-free' (and 'non-free-firmware') is required for WiFi/Bluetooth/GPU firmware.\n\nAdd them to your APT repositories?"; then
if [ -f /etc/apt/sources.list ]; then
sudo cp /etc/apt/sources.list "/etc/apt/sources.list.backup.$(date +%Y%m%d_%H%M%S)"
# Add each missing component after the space-delimited "main"
# component; a bare s/main/ would corrupt mirror URLs that
# contain "main" (e.g. https://main.example.com).
sudo sed -i -E '/^deb / { /(^|[[:space:]])non-free([[:space:]]|$)/! s/ main([[:space:]]|$)/ main non-free\1/ }' /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:]]|$)/ main non-free-firmware\1/ }' /etc/apt/sources.list
fi
sudo sed -i '/^deb / { /non-free/! s/\(main[^ ]*\)/\1 non-free non-free-firmware/ }' /etc/apt/sources.list
fi
if [ -d /etc/apt/sources.list.d ]; then
for f in /etc/apt/sources.list.d/*.sources; do
[ -f "$f" ] || continue
sudo cp "$f" "${f}.backup.$(date +%Y%m%d_%H%M%S)"
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
sudo sed -i '/^Components:/ { /non-free/! s/$/ non-free non-free-firmware/ }' "$f"
done
fi
sudo apt update
echo -e "${GREEN}non-free repository enabled.${NC}"
return 0
fi
# Verify the component was actually added before reporting success
local nonfree_ok=false
[ -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
fi
_ensure_apt_updated
echo -e "${GREEN}non-free repository enabled.${NC}"
return 0
return 1
}
# ── Main entry point ──
install_firmware() {
# Hardware state is detected on demand; the device
# arrays and DESKTOP_ENV/AUDIO_SERVER are read below.
_ensure_state_detected
echo -e "${YELLOW}Base firmware check...${NC}"
if ! _ensure_nonfree_repo; then
@@ -374,17 +384,18 @@ install_firmware() {
# 4. Install base firmware meta-package (unchanged logic)
local fw_pkg="firmware-linux-nonfree"
local fw_bpo
fw_bpo=$(_get_backports_version "$fw_pkg")
fw_bpo=$(apt-cache madison "$fw_pkg" 2>/dev/null | \
grep "${DEBIAN_CODENAME}-backports" | awk '{print $3}' | head -1)
local fw_stable
fw_stable=$(_get_pkg_version "$fw_pkg")
fw_stable=$(apt-cache policy "$fw_pkg" 2>/dev/null | awk 'NR==3 {print $2; exit}')
if is_installed "$fw_pkg"; then
if [ -n "$fw_bpo" ]; then
local current_ver
current_ver=$(_get_installed_version "$fw_pkg")
current_ver=$(dpkg -l "$fw_pkg" 2>/dev/null | awk '/^ii/{print $3}')
if _confirm "Firmware" "firmware-linux-nonfree ${current_ver} already installed.\n\nUpgrade to backports version ${fw_bpo}?\n\nBackports often includes newer hardware support."; then
_run_cmd "Firmware" "sudo apt install -y -t ${DEBIAN_CODENAME}-backports $fw_pkg" "Upgrading firmware..." || true
_run_cmd "Firmware" "sudo apt install -y -t ${DEBIAN_CODENAME}-backports $fw_pkg" "Upgrading firmware..."
fi
else
echo "$fw_pkg already installed."
@@ -401,15 +412,15 @@ install_firmware() {
msg+=" 2025/2026: recent GPUs, processors, WiFi.\n\n"
msg+="Choose version:"
if _confirm_custom "Firmware" "$msg" "Backports" "Stable"; then
_run_cmd "Firmware" "sudo apt install -y -t ${DEBIAN_CODENAME}-backports $fw_pkg" "Installing firmware from backports..." || true
_run_cmd "Firmware" "sudo apt install -y -t ${DEBIAN_CODENAME}-backports $fw_pkg" "Installing firmware from backports..."
else
_run_cmd "Firmware" "sudo apt install -y $fw_pkg" "Installing firmware from stable..." || true
_run_cmd "Firmware" "sudo apt install -y $fw_pkg" "Installing firmware from stable..."
fi
else
msg+=" Version: ${fw_stable}\n\n"
msg+="Install it?"
if _confirm "Firmware" "$msg"; then
_run_cmd "Firmware" "sudo apt install -y $fw_pkg" "Installing firmware..." || true
_run_cmd "Firmware" "sudo apt install -y $fw_pkg" "Installing firmware..."
fi
fi
echo -e "${GREEN}Base firmware installed.${NC}"
+42 -88
View File
@@ -27,23 +27,9 @@ ensure_contrib_repo() {
return 0
fi
if ! _confirm "contrib Repository" "Component 'contrib' is required for Steam.\n\nAdd 'contrib' to your APT repositories?"; then
echo -e "${YELLOW}contrib repository not enabled. Steam installation may fail.${NC}"
return 1
fi
# No active sources at all → bootstrap a complete configuration
if ! has_active_deb_sources; then
backup_current_repos
if ! bootstrap_repositories "main contrib"; then
return 1
fi
else
if _confirm "contrib Repository" "Component 'contrib' is required for Steam.\n\nAdd 'contrib' to your APT repositories?"; then
if [ -f /etc/apt/sources.list ]; then
sudo cp /etc/apt/sources.list "/etc/apt/sources.list.backup.$(date +%Y%m%d_%H%M%S)"
# Anchor to the space-delimited "main" component so mirror URLs
# containing "main" (e.g. https://main.example.com) are untouched.
sudo sed -i -E '/^deb / { /\bcontrib\b/! s/ main([[:space:]]|$)/ main contrib\1/ }' /etc/apt/sources.list
sudo sed -i '/^deb / { /contrib/! s/main/main contrib/ }' /etc/apt/sources.list
fi
if [ -d /etc/apt/sources.list.d ]; then
for f in /etc/apt/sources.list.d/*.sources; do
@@ -51,43 +37,31 @@ ensure_contrib_repo() {
sudo sed -i '/^Components:/ { /contrib/! s/$/ contrib/ }' "$f"
done
fi
sudo apt update
echo -e "${GREEN}contrib repository enabled.${NC}"
return 0
fi
# Verify the component was actually added before reporting success
local contrib_ok=false
[ -f /etc/apt/sources.list ] && grep -Eq '^[^#]*\bcontrib\b' /etc/apt/sources.list 2>/dev/null && contrib_ok=true
[ -d /etc/apt/sources.list.d ] && grep -qr 'Components:.*\bcontrib\b' /etc/apt/sources.list.d/*.sources 2>/dev/null && contrib_ok=true
[ -d /etc/apt/sources.list.d ] && grep -qrE '^[^#]*\bcontrib\b' /etc/apt/sources.list.d/*.list 2>/dev/null && contrib_ok=true
if ! $contrib_ok; then
echo -e "${RED}Failed to enable contrib repository. Check your APT sources.${NC}"
return 1
fi
_ensure_apt_updated
echo -e "${GREEN}contrib repository enabled.${NC}"
return 0
echo -e "${YELLOW}contrib repository not enabled. Steam installation may fail.${NC}"
return 1
}
install_gaming() {
# Hardware state is detected on demand; GPU_TYPE is
# consulted below for the NVIDIA hints.
_ensure_state_detected
echo -e "${YELLOW}Gaming setup...${NC}"
# 1. Single checklist with ALL options (including i386 toggle)
local choices
choices=$(_checklist "Gaming Setup" \
"Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
"i386" "Enable 32-bit (i386) architecture" ON \
"steam" "Steam (requires 32-bit support)" ON \
"i386" "Enable 32-bit (i386) architecture" ON \
"steam" "Steam (requires 32-bit support)" ON \
"mangohud" "Performance overlay (Vulkan/OpenGL)" ON \
"gamemode" "Game performance optimization" OFF \
"goverlay" "MangoHud config GUI" ON \
"heroic" "Heroic Launcher (Epic/GOG)" OFF \
"java" "Minecraft Java Runtime" OFF \
"openrgb" "OpenRGB (RGB lighting control)" OFF \
"lutris" "Lutris + Wine (requires 32-bit support)" OFF \
"heroic" "Heroic Launcher (Epic/GOG)" OFF \
"java" "Minecraft Java Runtime" OFF \
"openrgb" "OpenRGB (RGB lighting control)" OFF \
"lutris" "Lutris + Wine (requires 32-bit support)" OFF \
"retroarch" "RetroArch Emulator Frontend" OFF)
if [ -z "$choices" ]; then
@@ -101,37 +75,21 @@ install_gaming() {
# 2. Determine if 32-bit is needed (steam, lutris, or explicit i386 toggle)
local need_32bit=false
local -a install_pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && install_pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for p in "${install_pkgs[@]}"; do
case "$p" in steam | lutris) need_32bit=true ;; esac
for p in $cleaned; do
case $p in steam|lutris) need_32bit=true ;; esac
done
echo "$cleaned" | grep -qw i386 && need_32bit=true
# Strip pseudo-entry "i386" from the install list
local -a install_list=()
while IFS= read -r _pkg; do
[ "$_pkg" = "i386" ] && continue
[ -n "$_pkg" ] && install_list+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Pre-apply summary: confirm the selection before any
# system mutation (i386 architecture, apt update,
# package installation). Skipped when only the i386
# pseudo-entry was selected.
if [ ${#install_list[@]} -gt 0 ]; then
_confirm_install_list "Gaming Setup" "${install_list[@]}" || return
fi
local install_list
install_list=$(echo "$cleaned" | tr ' ' '\n' | grep -v '^i386$' | tr '\n' ' ')
install_list=${install_list% }
# 3. Enable i386 architecture if needed
if $need_32bit && ! dpkg --print-foreign-architectures 2>/dev/null | grep -q i386; then
echo -e "${YELLOW}Enabling i386 architecture (required by selection)...${NC}"
sudo dpkg --add-architecture i386
_ensure_apt_updated
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
fi
# 4. Install 32-bit graphics drivers only if 32-bit is needed
@@ -145,37 +103,33 @@ install_gaming() {
fi
# 5. Install selected packages
for pkg in "${install_list[@]}"; do
for pkg in $install_list; do
case $pkg in
steam)
if ensure_contrib_repo; then
install_steam
else
echo -e "${YELLOW}Skipping Steam installation (contrib repository not enabled).${NC}"
fi
;;
heroic) install_heroic ;;
java) install_minecraft_java ;;
mangohud) install_mangohud ;;
gamemode) install_gamemode ;;
goverlay) install_goverlay ;;
openrgb)
if [ "$DEBIAN_VERSION" = "11" ]; then
echo "OpenRGB requires Debian 12+."
continue
fi
install_openrgb
;;
lutris) install_lutris ;;
retroarch) install_retroarch ;;
*) _run_install "$pkg" ;;
steam)
if ensure_contrib_repo; then
install_steam
else
echo -e "${YELLOW}Skipping Steam installation (contrib repository not enabled).${NC}"
fi
;;
heroic) install_heroic ;;
java) install_minecraft_java ;;
mangohud) install_mangohud ;;
gamemode) install_gamemode ;;
goverlay) install_goverlay ;;
openrgb)
if [ "$DEBIAN_VERSION" = "11" ]; then
echo "OpenRGB requires Debian 12+."
continue
fi
install_openrgb
;;
lutris) install_lutris ;;
retroarch) install_retroarch ;;
*) _run_install "$pkg" ;;
esac
done
# Selection confirmed once above; release the
# per-package prompt suppression.
_SELECTION_CONFIRMED=0
echo -e "${GREEN}Gaming setup complete.${NC}"
_pause
}
+1 -45
View File
@@ -29,51 +29,7 @@ _install_mesa_32bit() {
return
fi
# ── Resolve versions for display ──
local rep_pkg="${base_pkgs[0]}"
local stable_ver=""
stable_ver=$(apt-cache policy "$rep_pkg" 2>/dev/null | awk 'NR==3 {print $2; exit}') || true
local bpo_ver=""
if [ "$DEBIAN_VERSION" = "13" ] && [ "$(is_backports_enabled)" = "true" ]; then
bpo_ver=$(apt-cache madison "$rep_pkg" 2>/dev/null | \
grep "${DEBIAN_CODENAME}-backports" | awk '{print $3}' | head -1) || true
fi
local check_pkg="libgl1-mesa-dri:i386"
# ── Already installed branch ──
if is_installed "$check_pkg"; then
if [ -n "$bpo_ver" ]; then
local current_ver
current_ver=$(dpkg -l "$check_pkg" 2>/dev/null | awk '/^ii/{print $3}') || true
if _confirm "Mesa 32-bit" \
"Mesa 32-bit libraries ${current_ver:+v${current_ver} }already installed.\n\nReinstall from backports v${bpo_ver}?"; then
echo "Reinstalling Mesa 32-bit from backports..."
_run_cmd "Mesa 32-bit" \
"sudo apt install -y --reinstall -t ${DEBIAN_CODENAME}-backports ${install_list[*]}" \
"Reinstalling Mesa 32-bit from backports..."
echo -e "${GREEN}Mesa 32-bit libraries reinstalled.${NC}"
return
fi
fi
echo "Mesa 32-bit libraries already installed."
return
fi
# ── Not installed branch ──
local apt_target=""
if [ -n "$bpo_ver" ]; then
local msg="Backports repository is enabled.\n\n"
msg+="Available versions for Mesa 32-bit:\n"
msg+=" - Stable: v${stable_ver:-unknown}\n"
msg+=" - Backports: v${bpo_ver}\n\n"
msg+="Install from Backports or Stable?"
if _confirm_custom "Mesa 32-bit" "$msg" "Backports" "Stable" 14 70; then
apt_target="-t ${DEBIAN_CODENAME}-backports"
fi
fi
_run_cmd "Mesa 32-bit" "sudo apt install -y $apt_target ${install_list[*]}" \
_run_cmd "Mesa 32-bit" "sudo apt install -y ${install_list[*]}" \
"Installing Mesa drivers (${#install_list[@]} packages)..."
echo -e "${GREEN}Mesa 32-bit libraries installed.${NC}"
}
+5 -22
View File
@@ -2,20 +2,13 @@
# Heroic Games Launcher installation from GitHub releases
install_heroic() {
local heroic_deb
heroic_deb=$(mktemp "${TMPDIR:-/tmp}/heroic-XXXXXX.deb") || return 1
# Remove the temp file on every exit path (RETURN
# traps are not inherited by called functions).
trap 'rm -f "$heroic_deb"' RETURN
local heroic_deb="/tmp/heroic.deb"
local ua="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
_run_cmd "Heroic" "sudo apt install -y curl jq" "Installing dependencies..."
local json
# -f: HTTP >= 400 must fail; --max-time bounds the
# whole transfer (connect-timeout only covers the
# connection phase).
json=$(curl -fsS --connect-timeout 10 --max-time 30 -H "User-Agent: $ua" \
json=$(curl -s --connect-timeout 10 -H "User-Agent: $ua" \
"https://api.github.com/repos/Heroic-Games-Launcher/HeroicGamesLauncher/releases/latest") || {
_msg "Heroic Error" "Could not fetch release data from GitHub API." 8 60
return 1
@@ -31,26 +24,16 @@ install_heroic() {
return 1
fi
# Strict URL validation before interpolating into a shell command (_run_cmd uses bash -c)
[[ "$deb_url" =~ ^https://[A-Za-z0-9./_-]+\.deb$ ]] || { _msg "Error" "Invalid download URL: $deb_url"; return 1; }
_run_cmd "Heroic" "curl -sL -H 'User-Agent: $ua' -o '$heroic_deb' '$deb_url'" "Downloading Heroic..."
_run_cmd "Heroic" "curl -fsSL --max-time 300 -H 'User-Agent: $ua' -o '$heroic_deb' '$deb_url'" "Downloading Heroic..."
# --info reads only the control archive; --contents
# also walks the data archive, catching deb files
# truncated after the control section.
if ! dpkg-deb --info "$heroic_deb" >/dev/null 2>&1 || \
! dpkg-deb --contents "$heroic_deb" >/dev/null 2>&1; then
if ! dpkg-deb --info "$heroic_deb" >/dev/null 2>&1; then
_msg "Heroic Error" "Downloaded .deb is corrupted or truncated.\n\nRemoving file." 10 60
rm -f "$heroic_deb"
return 1
fi
echo -e "${GREEN}Package integrity verified.${NC}"
if ! _run_cmd "Heroic" "sudo apt install -y '$heroic_deb'" "Installing Heroic..."; then
rm -f "$heroic_deb"
return 1
fi
_run_cmd "Heroic" "sudo apt install -y '$heroic_deb'" "Installing Heroic..."
rm -f "$heroic_deb"
echo -e "${GREEN}Heroic Games Launcher installed.${NC}"
}
+9 -28
View File
@@ -26,7 +26,7 @@ install_lutris() {
}
install_openrgb() {
local deb_suffix=""
local deb_suffix
case "$DEBIAN_CODENAME" in
bookworm) deb_suffix="bookworm" ;;
trixie) deb_suffix="trixie" ;;
@@ -36,24 +36,19 @@ install_openrgb() {
;;
esac
local deb_path
deb_path=$(mktemp "${TMPDIR:-/tmp}/openrgb-XXXXXX.deb") || return 1
# Remove the temp file on every exit path.
trap 'rm -f "$deb_path"' RETURN
local deb_path="/tmp/openrgb.deb"
local ua="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
_run_cmd "OpenRGB" "sudo apt install -y curl jq" "Installing dependencies..."
local json=""
# -f: HTTP >= 400 must fail; --max-time bounds the
# whole transfer.
json=$(curl -fsS --connect-timeout 10 --max-time 30 \
local json
json=$(curl -s --connect-timeout 10 \
"https://codeberg.org/api/v1/repos/OpenRGB/OpenRGB/releases?limit=1") || {
echo -e "${RED}Could not fetch OpenRGB releases from Codeberg API.${NC}"
return 1
}
local deb_url="" sha256=""
local deb_url sha256
while IFS=' ' read -r url hash; do
deb_url="$url"
sha256="$hash"
@@ -67,10 +62,7 @@ install_openrgb() {
return 1
fi
# Strict URL validation before interpolating into a shell command (_run_cmd uses bash -c)
[[ "$deb_url" =~ ^https://[A-Za-z0-9./_-]+\.deb$ ]] || { _msg "Error" "Invalid download URL: $deb_url"; return 1; }
_run_cmd "OpenRGB" "curl -fsSL --max-time 300 -o '${deb_path}' -A '${ua}' '${deb_url}'" "Downloading OpenRGB..."
_run_cmd "OpenRGB" "curl -L -o '${deb_path}' -A '${ua}' '${deb_url}'" "Downloading OpenRGB..."
if [ -n "$sha256" ]; then
if ! echo "$sha256 $deb_path" | sha256sum -c --strict; then
@@ -80,11 +72,7 @@ install_openrgb() {
fi
echo -e "${GREEN}SHA256 verified.${NC}"
else
# --contents also walks the data archive,
# catching deb files truncated after the
# control section.
if ! dpkg-deb --info "$deb_path" >/dev/null 2>&1 || \
! dpkg-deb --contents "$deb_path" >/dev/null 2>&1; then
if ! dpkg-deb --info "$deb_path" >/dev/null 2>&1; then
echo -e "${RED}Downloaded .deb is corrupted. Removing.${NC}"
rm -f "$deb_path"
return 1
@@ -92,21 +80,14 @@ install_openrgb() {
echo -e "${YELLOW}No SHA256 in API, validated via dpkg-deb.${NC}"
fi
# The post-install steps (i2c group, udev rules)
# must only run if the package was actually
# installed; apt can fail on missing dependencies.
if ! sudo apt install -y "$deb_path"; then
rm -f "$deb_path"
echo -e "${RED}OpenRGB installation failed (dependency or dpkg error).${NC}"
return 1
fi
sudo apt install -y "$deb_path"
rm -f "$deb_path"
sudo modprobe i2c-dev
if ! grep -q "^i2c-dev" /etc/modules 2>/dev/null; then
echo "i2c-dev" | sudo tee -a /etc/modules >/dev/null
fi
sudo usermod -aG i2c "${USER:-}"
sudo usermod -aG i2c "$USER"
sudo udevadm control --reload-rules && sudo udevadm trigger
sudo setcap cap_sys_rawio=ep /usr/bin/openrgb 2>/dev/null || true
+46 -144
View File
@@ -5,40 +5,18 @@ _GPU_DIR="${MODULES_DIR}/gpu"
source "${_GPU_DIR}/_helpers.sh"
source "${_GPU_DIR}/amd_intel.sh"
source "${_GPU_DIR}/nvidia.sh"
source "${_GPU_DIR}/nvidia_manage.sh"
# Consumed by gaming.sh to know which NVIDIA driver path was taken
NVIDIA_DRIVER_MODE=""
# Set by _show_nvidia_version_menu(): "470" | "535" | "550" | "590" | "595" | "auto"
NVIDIA_SELECTED_VERSION=""
# Informational post-install notice: NVIDIA Debian bug #1109409
# (GDM3 + NVIDIA + Wayland → black screen). Only informs, never
# changes GDM3 configuration nor aborts installation.
_warn_nvidia_gnome_wayland() {
command -v gdm3 &>/dev/null || return 0
gnome-shell --version 2>/dev/null | grep -qi "shell 4[0-9]" || return 0
case "$NVIDIA_DRIVER_MODE" in
stable | backports | cuda-repo | extrepo) ;;
*) return 0 ;;
esac
echo -e "${YELLOW}WARNING: Debian bug #1109409 may affect GDM3 + NVIDIA + Wayland.${NC}"
echo -e "${YELLOW}If you see a black screen after reboot, select 'GNOME on Xorg' at login.${NC}"
echo -e "${YELLOW}Or temporarily disable Wayland: sudo nano /etc/gdm3/daemon.conf${NC}"
}
_install_amd_intel_stack() {
# Hardware state is detected on demand; GPU_TYPE and
# LSPCI_OUTPUT must be populated before planning.
_ensure_state_detected
if [ "$GPU_TYPE" = "unknown" ] || [ -z "$GPU_TYPE" ]; then
local mesa_pkgs=(mesa-vulkan-drivers libgl1-mesa-dri libglx-mesa0 libegl-mesa0 mesa-va-drivers)
local ref_ver
ref_ver=$(apt-cache policy mesa-vulkan-drivers 2>/dev/null | awk 'NR==3 {print $2; exit}')
local ref_bpo_ver
ref_bpo_ver=$(apt-cache madison mesa-vulkan-drivers 2>/dev/null |
grep "${DEBIAN_CODENAME}-backports" | awk '{print $3}' | head -1 || true)
ref_bpo_ver=$(apt-cache madison mesa-vulkan-drivers 2>/dev/null | \
grep "${DEBIAN_CODENAME}-backports" | awk '{print $3}' | head -1)
local comp_line="Components: Vulkan, OpenGL, GLX, EGL, VA-API (64-bit)"
if [ -n "$ref_bpo_ver" ] && [ "$(is_backports_enabled)" == "true" ]; then
@@ -83,13 +61,11 @@ _install_amd_intel_stack() {
local desc
desc=$(echo "$gpu_line" | sed -E 's/.*: //; s/ *\(rev.*//')
plan+=" GPU ${gpu_count}: ${desc}\n"
done < <(echo "$LSPCI_OUTPUT" | grep -E "VGA|3D" || true)
done < <(timeout 2 lspci -nn | grep -E "VGA|3D" || true)
plan+="\nPlanned components:\n"
if $HAS_INTEL; then
local _gen
_gen=$(get_intel_generation)
local _va
[ "$_gen" = "gen7-" ] && _va="i965-va-driver-shaders" || _va="intel-media-va-driver-non-free"
local _gen; _gen=$(get_intel_generation)
local _va; [ "$_gen" = "gen7-" ] && _va="i965-va-driver-shaders" || _va="intel-media-va-driver-non-free"
plan+=" [+] Intel firmware + ${_va}\n"
fi
if $HAS_AMD; then
@@ -150,8 +126,7 @@ _install_amd_intel_stack() {
_apply_amd_gcn_grub_fix() {
local file="/etc/default/grub"
local backup
backup="${file}.backup.gcn.$(date +%Y%m%d_%H%M%S)"
local backup="${file}.backup.gcn.$(date +%Y%m%d_%H%M%S)"
local params="radeon.si_support=0 radeon.cik_support=0 amdgpu.si_support=1 amdgpu.cik_support=1"
if grep -q "amdgpu.si_support=1" "$file" 2>/dev/null; then
@@ -161,9 +136,7 @@ _apply_amd_gcn_grub_fix() {
sudo cp "$file" "$backup"
if ! sudo grep -q "amdgpu.si_support=1" "$file"; then
sudo sed -i -E "/^GRUB_CMDLINE_LINUX_DEFAULT=/ s/\"([^\"]*)\"/\"${params} \1\"/" "$file"
fi
sudo sed -i "/^GRUB_CMDLINE_LINUX_DEFAULT=/ s/\"$*/${params}&/" "$file"
if _confirm "AMD GCN — GRUB" "Parameters added:\n\n ${params}\n\nRun update-grub now?" 12 65; then
if sudo update-grub >/dev/null 2>&1; then
@@ -179,10 +152,6 @@ _apply_amd_gcn_grub_fix() {
}
_install_nvidia_stack() {
# Hardware state is detected on demand; HAS_NVIDIA and
# LSPCI_OUTPUT must be populated before planning.
_ensure_state_detected
if ! $HAS_NVIDIA; then
_msg "NVIDIA Not Found" "No NVIDIA GPU was detected.\n\nPlease check your hardware and try again." 10 60
return
@@ -196,137 +165,70 @@ _install_nvidia_stack() {
local desc
desc=$(echo "$gpu_line" | sed -E 's/.*: //; s/ *\(rev.*//')
plan+=" GPU ${gpu_count}: ${desc}\n"
done < <(echo "$LSPCI_OUTPUT" | grep -E "VGA|3D" || true)
done < <(timeout 2 lspci -nn | grep -E "VGA|3D" || true)
plan+="\nPlanned:\n [+] NVIDIA proprietary driver"
_msg "NVIDIA Stack — Plan" "$plan" 14 65
_pause "Press Enter to continue..."
if ! _confirm "NVIDIA Stack" "Install the NVIDIA proprietary driver?"; then
echo "Skipping NVIDIA driver installation."
return
fi
NVIDIA_DRIVER_MODE=""
if _nvidia_driver_installed; then
if ! _nvidia_manage_menu; then
return
fi
fi
if [ "$DEBIAN_VERSION" = "11" ]; then
if type install_nvidia_bullseye &>/dev/null; then
install_nvidia_bullseye
else
_msg "NVIDIA — Bullseye" "The Bullseye NVIDIA module is not available.\n\nNo NVIDIA driver was installed." 10 60
NVIDIA_DRIVER_MODE=""
fi
install_nvidia_bullseye
else
if ! _show_nvidia_version_menu; then
echo "Skipping NVIDIA driver installation."
return
fi
local nv_arch=""
case "$NVIDIA_SELECTED_VERSION" in
470)
# Legacy 470 (Kepler/Tesla) — forced, regardless of auto-detection
_install_nvidia_bookworm_kepler
;;
535)
if [ "$(is_nvidia_kepler)" = "true" ]; then
elif [ "$DEBIAN_VERSION" = "12" ]; then
if [ "$(is_nvidia_kepler)" = "true" ]; then
if type _install_nvidia_bookworm_kepler &>/dev/null; then
_install_nvidia_bookworm_kepler
elif [ "$(is_nvidia_fermi)" = "true" ]; then
_msg "NVIDIA Fermi — Bookworm" \
"Fermi GPUs (GF1xx) are not supported\nin Debian 12 (Bookworm).\nThe nvidia-legacy-390xx driver is\nnot available in this version.\n\nNo NVIDIA driver will be installed."
NVIDIA_DRIVER_MODE=""
else
_install_nvidia_standard
fi
;;
550)
nv_arch=$(detect_nvidia_arch "$NVIDIA_GPU_DEVICE_ID")
case "$nv_arch" in
legacy)
_msg "NVIDIA — Trixie" \
"Kepler and Fermi GPUs are not supported\nin Debian 13 (Trixie).\n\nThe nvidia-legacy drivers are not available\nin this version of Debian.\n\nNo NVIDIA driver will be installed."
NVIDIA_DRIVER_MODE=""
;;
blackwell)
_msg "NVIDIA — Blackwell (v550)" \
"Your Blackwell GPU is NOT supported by the official\nDebian v550 driver.\n\nPlease select v590 or v595 (NVIDIA CUDA Repo)\nin the driver menu." 14 65
NVIDIA_DRIVER_MODE=""
;;
maxwell | pascal | volta)
if [ "$(is_backports_kernel)" = "true" ]; then
local gpu_gen="Maxwell"
[ "$nv_arch" = "pascal" ] && gpu_gen="Pascal"
[ "$nv_arch" = "volta" ] && gpu_gen="Volta"
_msg "NVIDIA — Trixie + Backports" \
"INCOMPATIBILITY DETECTED: Your NVIDIA ${gpu_gen} GPU\n\
elif [ "$(is_nvidia_fermi)" = "true" ]; then
_msg "NVIDIA Fermi — Bookworm" \
"Fermi GPUs (GF1xx) are not supported\nin Debian 12 (Bookworm).\nThe nvidia-legacy-390xx driver is\nnot available in this version.\n\nNo NVIDIA driver will be installed."
NVIDIA_DRIVER_MODE=""
elif [ "$(is_backports_kernel)" = "true" ]; then
_install_nvidia_bookworm_bpo
else
_install_nvidia_standard
fi
elif [ "$DEBIAN_VERSION" = "13" ]; then
if [ "$(is_nvidia_blackwell)" = "true" ]; then
_install_nvidia_cuda_repo
elif [ "$(is_nvidia_kepler)" = "true" ] || [ "$(is_nvidia_fermi)" = "true" ]; then
_msg "NVIDIA — Trixie" \
"Kepler and Fermi GPUs are not supported\nin Debian 13 (Trixie).\n\nThe nvidia-legacy drivers are not available\nin this version of Debian.\n\nNo NVIDIA driver will be installed."
NVIDIA_DRIVER_MODE=""
elif [ "$(is_backports_kernel)" = "true" ]; then
if [ "$(is_nvidia_maxwell)" = "true" ] || [ "$(is_nvidia_pascal)" = "true" ]; then
local gpu_gen="Maxwell"
[ "$(is_nvidia_pascal)" = "true" ] && gpu_gen="Pascal"
_msg "NVIDIA — Trixie + Backports" \
"INCOMPATIBILITY DETECTED: Your NVIDIA ${gpu_gen} GPU\n\
is NOT supported by the modern v590 driver.\n\n\
To run NVIDIA safely on Debian 13 (Trixie), you MUST use\n\
the official Debian v550 driver, which requires the\n\
standard STABLE Kernel.\n\n\
Forcing the stable driver path." 14 70
if ! _install_nvidia_standard; then
NVIDIA_DRIVER_MODE=""
return 1
fi
NVIDIA_DRIVER_MODE="stable"
else
_install_nvidia_standard
fi
;;
turing | ampere | ada)
_install_nvidia_standard
;;
*)
if [ "$(is_backports_kernel)" = "true" ]; then
_install_nvidia_cuda_repo
else
_install_nvidia_standard
fi
;;
esac
;;
590 | 595)
nv_arch=$(detect_nvidia_arch "$NVIDIA_GPU_DEVICE_ID")
if [ "$nv_arch" = "maxwell" ] || [ "$nv_arch" = "pascal" ] || [ "$nv_arch" = "legacy" ]; then
local gpu_gen="Kepler/Fermi"
[ "$nv_arch" = "maxwell" ] && gpu_gen="Maxwell"
[ "$nv_arch" = "pascal" ] && gpu_gen="Pascal"
_msg "NVIDIA — v${NVIDIA_SELECTED_VERSION}" \
"Your NVIDIA ${gpu_gen} GPU is NOT supported by\nNVIDIA driver v${NVIDIA_SELECTED_VERSION}.\n\n\
Only Turing, Ampere, Ada and Blackwell GPUs are supported.\n\n\
No NVIDIA driver will be installed." 14 65
NVIDIA_DRIVER_MODE=""
NVIDIA_DRIVER_MODE="stable"
else
if ! _enable_cuda_repo; then
NVIDIA_DRIVER_MODE=""
_msg "CUDA Repo — Error" "Failed to enable the official NVIDIA CUDA repository.\n\nNo NVIDIA driver was installed." 10 60
return 1
fi
if _install_nvidia_cuda_repo "$NVIDIA_SELECTED_VERSION"; then
NVIDIA_DRIVER_MODE="cuda-repo"
else
NVIDIA_DRIVER_MODE=""
fi
_install_nvidia_cuda_repo
fi
;;
*)
else
_install_nvidia_standard
;;
esac
fi
else
_install_nvidia_standard
fi
if [ -n "$NVIDIA_DRIVER_MODE" ]; then
_configure_nvidia_wayland "$NVIDIA_SELECTED_VERSION"
if _is_hybrid_laptop; then
echo -e "${CYAN}[INFO] Hybrid GPU (Optimus) detected.${NC}"
echo -e "PRIME offload is auto-configured by X.Org 1.20.7+ when using X11."
echo -e "After reboot, verify with: ${YELLOW}xrandr --listproviders | grep NVIDIA-G0${NC}"
echo -e "If not found, ensure BIOS boots on iGPU and nvidia-drm is loaded."
fi
offer_generic_tools
_warn_nvidia_gnome_wayland
local summary="NVIDIA: ${NVIDIA_DRIVER_MODE}\n"
summary+="Tools: nvtop + vainfo"
_msg "NVIDIA Stack — Complete" "$summary" 10 55
+112 -96
View File
@@ -1,82 +1,121 @@
#!/usr/bin/env bash
# Shared helpers for GPU submodules
declare -A NVIDIA_FAMILY_MAP=(
# Fermi / Kepler (Legacy: the shim separates them)
["06"]="legacy" ["0D"]="legacy" ["0E"]="legacy"
["0F"]="legacy" ["10"]="legacy" ["11"]="legacy" ["12"]="legacy"
# Maxwell
["13"]="maxwell" ["14"]="maxwell" ["16"]="maxwell" ["17"]="maxwell"
# Pascal + Volta (same classic driver policy)
["15"]="pascal" ["1B"]="pascal" ["1C"]="pascal" ["1D"]="pascal"
# Turing
["1E"]="turing" ["1F"]="turing" ["21"]="turing"
# Ampere
["20"]="ampere" ["22"]="ampere" ["24"]="ampere" ["25"]="ampere"
# Hopper
["23"]="hopper"
# Ada Lovelace
["26"]="ada" ["27"]="ada" ["28"]="ada"
# Blackwell
["29"]="blackwell" ["2B"]="blackwell" ["2C"]="blackwell"
["2D"]="blackwell" ["2E"]="blackwell" ["31"]="blackwell"
)
is_nvidia_kepler() {
local dev_id
dev_id=$(timeout 2 lspci -nn | grep -iE "VGA|3D" | grep -i nvidia | grep -oP '10de:\K[0-9a-fA-F]+' | head -n1)
[ -z "$dev_id" ] && { echo false; return; }
detect_nvidia_arch() {
[ -z "$1" ] && {
echo "unknown"
return
}
local pci_id="${1^^}"
local prefix="${pci_id:0:2}"
echo "${NVIDIA_FAMILY_MAP[$prefix]:-unknown}"
local dev_int
dev_int=$((16#${dev_id,,}))
# Bloque 1: GK107 (escritorio + móvil) — 0x0FC0..0x0FFF
if [ "$dev_int" -ge $((16#0FC0)) ] && [ "$dev_int" -le $((16#0FFF)) ]; then echo true; return; fi
# Bloque 2: GK110/GK110B/GK210 acotado puro — 0x1000..0x103F
if [ "$dev_int" -ge $((16#1000)) ] && [ "$dev_int" -le $((16#103F)) ]; then echo true; return; fi
# Bloque 3: GK104/GK106 (completo) — 0x1180..0x11FF
if [ "$dev_int" -ge $((16#1180)) ] && [ "$dev_int" -le $((16#11FF)) ]; then echo true; return; fi
# Bloque 4: GK208/GK208B (completo) — 0x1280..0x12BF
if [ "$dev_int" -ge $((16#1280)) ] && [ "$dev_int" -le $((16#12BF)) ]; then echo true; return; fi
echo false
}
_is_nvidia_kepler_id() {
local dev_id="$1"
[ -z "$dev_id" ] && return 1
local dev_int=$((16#${dev_id,,}))
[ "$dev_int" -ge $((16#0FC0)) ] && [ "$dev_int" -le $((16#0FFF)) ] && return 0
[ "$dev_int" -ge $((16#1000)) ] && [ "$dev_int" -le $((16#103F)) ] && return 0
[ "$dev_int" -ge $((16#1180)) ] && [ "$dev_int" -le $((16#11FF)) ] && return 0
[ "$dev_int" -ge $((16#1280)) ] && [ "$dev_int" -le $((16#12BF)) ] && return 0
return 1
is_nvidia_fermi() {
local dev_id
dev_id=$(timeout 2 lspci -nn | grep -iE "VGA|3D" | grep -i nvidia | grep -oP '10de:\K[0-9a-fA-F]+' | head -n1)
[ -z "$dev_id" ] && { echo false; return; }
local dev_int
dev_int=$((16#${dev_id,,}))
# GF100 / GF110 — GTX 480, GTX 580, Quadro 6000, Tesla C2050
if [ "$dev_int" -ge $((16#06C0)) ] && [ "$dev_int" -le $((16#06DF)) ]; then echo true; return; fi
# GF104 / GF114 — GTS 450, GTX 460M, GT 555M
if [ "$dev_int" -ge $((16#0DC0)) ] && [ "$dev_int" -le $((16#0DCF)) ]; then echo true; return; fi
# GF104 / GF108 — GT 445M, GT 435M, GT 550M
if [ "$dev_int" -ge $((16#0DD0)) ] && [ "$dev_int" -le $((16#0DDF)) ]; then echo true; return; fi
# GF108 — GT 440, GT 430, GT 520, GT 610, GT 620M, NVS 5400M
if [ "$dev_int" -ge $((16#0DE0)) ] && [ "$dev_int" -le $((16#0DEF)) ]; then echo true; return; fi
# GF108 — GT 525M, GT 540M, GT 550M, Quadro 600, Quadro 500M
if [ "$dev_int" -ge $((16#0DF0)) ] && [ "$dev_int" -le $((16#0DFF)) ]; then echo true; return; fi
# GF104 / GF114 — GTX 460, GTX 470M, GTX 485M
if [ "$dev_int" -ge $((16#0E22)) ] && [ "$dev_int" -le $((16#0E31)) ]; then echo true; return; fi
# GF119 — GT 520M, GT 610M, NVS 4200M
if [ "$dev_int" -ge $((16#1050)) ] && [ "$dev_int" -le $((16#105F)) ]; then echo true; return; fi
# GF110 — GTX 580, GTX 570, GTX 560 Ti, GTX 590
if [ "$dev_int" -ge $((16#1080)) ] && [ "$dev_int" -le $((16#108F)) ]; then echo true; return; fi
# GF110 — Tesla M2090, Quadro 5010M, Quadro 7000
if [ "$dev_int" -ge $((16#1090)) ] && [ "$dev_int" -le $((16#109F)) ]; then echo true; return; fi
# GF116 / GF119 — GTX 560, GTX 460 v2, GTX 555, GT 645
if [ "$dev_int" -ge $((16#1200)) ] && [ "$dev_int" -le $((16#120F)) ]; then echo true; return; fi
# GF116 / GF108 — GTX 550 Ti, GTS 450 rev, GT 545, GT 640 (Fermi)
if [ "$dev_int" -ge $((16#1240)) ] && [ "$dev_int" -le $((16#124F)) ]; then echo true; return; fi
echo false
}
# Normalizes the NVIDIA architecture family, disambiguating Kepler/Fermi
# (detect_nvidia_arch groups them as "legacy" via device-ID range)
_get_nvidia_arch_family() {
local fam
fam=$(detect_nvidia_arch "$NVIDIA_GPU_DEVICE_ID")
if [ "$fam" = "legacy" ]; then
if _is_nvidia_kepler_id "$NVIDIA_GPU_DEVICE_ID"; then
echo "kepler"
else
echo "fermi"
fi
else
echo "$fam"
fi
is_nvidia_maxwell() {
local dev_id
dev_id=$(timeout 2 lspci -nn | grep -iE "VGA|3D" | grep -i nvidia | grep -oP '10de:\K[0-9a-fA-F]+' | head -n1)
[ -z "$dev_id" ] && { echo false; return; }
local dev_int
dev_int=$((16#${dev_id,,}))
# GM108/GM107/GM204 — gama media/baja + Quadros M (incluye 980M/970M)
if [ "$dev_int" -ge $((16#1340)) ] && [ "$dev_int" -le $((16#13FF)) ]; then echo true; return; fi
# GM206 — GTX 960, GTX 950, Quadro M2000
if [ "$dev_int" -ge $((16#1400)) ] && [ "$dev_int" -le $((16#14FF)) ]; then echo true; return; fi
# GM200 — TITAN X, GTX 980 Ti
if [ "$dev_int" -ge $((16#17C0)) ] && [ "$dev_int" -le $((16#17CF)) ]; then echo true; return; fi
# GM200 profesional — Quadro M6000, Tesla M40
if [ "$dev_int" -ge $((16#17F0)) ] && [ "$dev_int" -le $((16#17FF)) ]; then echo true; return; fi
echo false
}
is_nvidia_kepler() { [ -n "$NVIDIA_GPU_DEVICE_ID" ] && [[ "$(detect_nvidia_arch "$NVIDIA_GPU_DEVICE_ID")" == "legacy" ]] && _is_nvidia_kepler_id "$NVIDIA_GPU_DEVICE_ID" && echo true || echo false; }
is_nvidia_fermi() { [ -n "$NVIDIA_GPU_DEVICE_ID" ] && [[ "$(detect_nvidia_arch "$NVIDIA_GPU_DEVICE_ID")" == "legacy" ]] && ! _is_nvidia_kepler_id "$NVIDIA_GPU_DEVICE_ID" && echo true || echo false; }
is_nvidia_legacy() { [ -n "$NVIDIA_GPU_DEVICE_ID" ] && [[ "$(detect_nvidia_arch "$NVIDIA_GPU_DEVICE_ID")" == "legacy" ]] && echo true || echo false; }
is_nvidia_maxwell() { [ -n "$NVIDIA_GPU_DEVICE_ID" ] && [[ "$(detect_nvidia_arch "$NVIDIA_GPU_DEVICE_ID")" == "maxwell" ]] && echo true || echo false; }
is_nvidia_pascal() { [ -n "$NVIDIA_GPU_DEVICE_ID" ] && [[ "$(detect_nvidia_arch "$NVIDIA_GPU_DEVICE_ID")" == "pascal" ]] && echo true || echo false; }
is_nvidia_volta() { [ -n "$NVIDIA_GPU_DEVICE_ID" ] && [[ "$(detect_nvidia_arch "$NVIDIA_GPU_DEVICE_ID")" == "volta" ]] && echo true || echo false; }
is_nvidia_turing() { [ -n "$NVIDIA_GPU_DEVICE_ID" ] && [[ "$(detect_nvidia_arch "$NVIDIA_GPU_DEVICE_ID")" == "turing" ]] && echo true || echo false; }
is_nvidia_ampere() { [ -n "$NVIDIA_GPU_DEVICE_ID" ] && [[ "$(detect_nvidia_arch "$NVIDIA_GPU_DEVICE_ID")" == "ampere" ]] && echo true || echo false; }
is_nvidia_ada() { [ -n "$NVIDIA_GPU_DEVICE_ID" ] && [[ "$(detect_nvidia_arch "$NVIDIA_GPU_DEVICE_ID")" == "ada" ]] && echo true || echo false; }
is_nvidia_blackwell() { [ -n "$NVIDIA_GPU_DEVICE_ID" ] && [[ "$(detect_nvidia_arch "$NVIDIA_GPU_DEVICE_ID")" == "blackwell" ]] && echo true || echo false; }
is_nvidia_pascal() {
local dev_id
dev_id=$(timeout 2 lspci -nn | grep -iE "VGA|3D" | grep -i nvidia | grep -oP '10de:\K[0-9a-fA-F]+' | head -n1)
[ -z "$dev_id" ] && { echo false; return; }
local dev_int
dev_int=$((16#${dev_id,,}))
# GP100 — Tesla P100, Quadro GP100
if [ "$dev_int" -ge $((16#15F0)) ] && [ "$dev_int" -le $((16#15FF)) ]; then echo true; return; fi
# GP102 — TITAN Xp, GTX 1080 Ti, Tesla P40, Quadro P6000
if [ "$dev_int" -ge $((16#1B00)) ] && [ "$dev_int" -le $((16#1B3F)) ]; then echo true; return; fi
# GP104 — GTX 1080, GTX 1070, Quadro P5000/P4000/P5200/P4200
if [ "$dev_int" -ge $((16#1B80)) ] && [ "$dev_int" -le $((16#1BBF)) ]; then echo true; return; fi
# GP106 + GP107 — GTX 1060, GTX 1050 Ti, GTX 1050, Quadro P2000/P1000/P620/P600
if [ "$dev_int" -ge $((16#1C00)) ] && [ "$dev_int" -le $((16#1CFF)) ]; then echo true; return; fi
# GP108 — GT 1030, GT 1010, MX150/250/350/330, Quadro P520
if [ "$dev_int" -ge $((16#1D00)) ] && [ "$dev_int" -le $((16#1D7F)) ]; then echo true; return; fi
echo false
}
is_nvidia_blackwell() {
local dev_id
dev_id=$(timeout 2 lspci -nn | grep -iE "VGA|3D" | grep -i nvidia | grep -oP '10de:\K[0-9a-fA-F]+' | head -n1)
[ -z "$dev_id" ] && { echo false; return; }
local dev_int
dev_int=$((16#${dev_id,,}))
# Blackwell (GB20x) rango bajo: 0x2900 – 0x29BF
if [ "$dev_int" -ge $((16#2900)) ] && [ "$dev_int" -le $((16#29BF)) ]; then echo true; return; fi
# Blackwell (GB20x) rango alto: 0x2B80 – 0x31DF
if [ "$dev_int" -ge $((16#2B80)) ] && [ "$dev_int" -le $((16#31DF)) ]; then echo true; return; fi
echo false
}
is_amd_legacy_gcn() {
local dev_id
dev_id=$(echo "$LSPCI_OUTPUT" | grep -iE "VGA|3D" | grep -i amd | grep -oP '1002:\K[0-9a-fA-F]{4}' | head -n1)
[ -z "$dev_id" ] && {
echo false
return
}
dev_id=$(timeout 2 lspci -nn | grep -iE "VGA|3D" | grep -i amd | grep -oP '1002:\K[0-9a-fA-F]{4}' | head -n1)
[ -z "$dev_id" ] && { echo false; return; }
local legacy_ids
legacy_ids="6660|6664|6665|6667|6780|6784|6788|678a|6798|679a|679e|679f|3000|3001|6808|6809|6810|6811|6816|6817|6818|6819|6828|6829|682b|682c|6835|6837|683d|683f|6608|6609|6610|6611|6613|6617|1dcf|983d|6646|6649|664d|6650|6651|6658|665c|665d|67a0|67a1|67a2|67a8|67a9|67aa|67b0|67b1|67b8|67be|9830|9831|9832|9833|9834|9835|9836|9837|9838|9839|1304|1305|1306|1307|1309|130a|130b|130c|130d|130e|130f|1310|1311|1312|1313|1315|1316|1317|1318|131b|131c|131d|9850|9851|9852|9853|9854|9855|9856|9857|9858|9859|985a|985b|985c|985d|985e|985f"
@@ -100,7 +139,7 @@ _install_mesa_backports() {
for mpkg in "${mesa_pkgs[@]}"; do
local bpo_ver
bpo_ver=$(apt-cache madison "$mpkg" 2>/dev/null |
bpo_ver=$(apt-cache madison "$mpkg" 2>/dev/null | \
grep "${DEBIAN_CODENAME}-backports" | awk '{print $3}' | head -1)
if [ -n "$bpo_ver" ]; then
bpo_pkgs+=("$mpkg")
@@ -109,8 +148,8 @@ _install_mesa_backports() {
fi
done
# Mesa >= 25.3.3 unified VA-API into mesa-libgallium.
# mesa-va-drivers from backports breaks with the new mesa-libgallium.
# Mesa >= 25.3.3 unificó VA-API dentro de mesa-libgallium.
# mesa-va-drivers desde backports rompe con la nueva mesa-libgallium.
local _bpo_filtered=()
for _p in "${bpo_pkgs[@]}"; do
[ "$_p" != "mesa-va-drivers" ] && _bpo_filtered+=("$_p")
@@ -120,7 +159,7 @@ _install_mesa_backports() {
local ref_ver
ref_ver=$(apt-cache policy mesa-vulkan-drivers 2>/dev/null | awk 'NR==3 {print $2; exit}')
local ref_bpo_ver
ref_bpo_ver=$(apt-cache madison mesa-vulkan-drivers 2>/dev/null |
ref_bpo_ver=$(apt-cache madison mesa-vulkan-drivers 2>/dev/null | \
grep "${DEBIAN_CODENAME}-backports" | awk '{print $3}' | head -1)
local comp_line="Components: Vulkan, OpenGL, GLX, EGL, VA-API (64-bit)"
@@ -172,33 +211,10 @@ offer_generic_tools() {
local tool_pkgs
tool_pkgs=$(pkg_versions nvtop vainfo)
if _confirm "GPU Tools" "Install monitoring and info tools?\n\n${tool_pkgs}"; then
_run_cmd "GPU Tools" "sudo apt install -y nvtop vainfo" "Installing GPU tools..." || true
if command -v vainfo &>/dev/null; then
vainfo
_pause "vainfo output shown above."
else
echo -e "${YELLOW}vainfo not available, skipping report.${NC}"
fi
_run_cmd "GPU Tools" "sudo apt install -y nvtop vainfo" "Installing GPU tools..."
vainfo
_pause "vainfo output shown above."
else
echo "Skipping GPU monitoring tools."
fi
}
_is_hybrid_laptop() {
local gpu_count nvidia_count chassis
gpu_count=$(echo "$LSPCI_OUTPUT" | grep -ciE "VGA compatible|3D controller") || true
[ "$gpu_count" -lt 2 ] && return 1
# Defense in depth: if ALL GPUs are NVIDIA (SLI or dual-GPU
# desktop), there is no Intel/AMD iGPU → not hybrid.
nvidia_count=$(echo "$LSPCI_OUTPUT" | grep -iE "VGA compatible|3D controller" | grep -c "10de:") || true
[ "$nvidia_count" -ge "$gpu_count" ] && return 1
chassis=$(cat /sys/class/dmi/id/chassis_type 2>/dev/null || echo "0")
case "$chassis" in
8 | 9 | 10 | 11 | 14 | 30 | 31 | 32) return 0 ;;
esac
compgen -G "/sys/class/power_supply/BAT*" >/dev/null && return 0
return 1
}
+10 -40
View File
@@ -5,9 +5,7 @@ install_amd_firmware() {
local fw_info
fw_info=$(pkg_versions firmware-amd-graphics)
if _confirm "AMD Firmware" "Install AMD GPU firmware?\n\n${fw_info}"; then
if ! _run_cmd "AMD" "sudo apt install -y firmware-amd-graphics" "Installing AMD GPU firmware..."; then
_msg_red "AMD Firmware" "Failed to install AMD GPU firmware."
fi
_run_cmd "AMD" "sudo apt install -y firmware-amd-graphics" "Installing AMD GPU firmware..."
fi
}
@@ -26,30 +24,15 @@ offer_amd_tools() {
return
fi
local tools_failed=false
if [ "$DEBIAN_VERSION" = "11" ]; then
if ! _run_cmd "AMD Tools" "sudo apt install -y ${amd_tools[*]} vainfo" "Installing AMD tools..."; then
_msg_red "AMD Tools" "Failed to install AMD monitoring tools."
tools_failed=true
fi
_run_cmd "AMD Tools" "sudo apt install -y ${amd_tools[*]} vainfo" "Installing AMD tools..."
else
if ! _run_cmd "AMD Tools" "sudo apt install -y ${amd_tools[*]} nvtop vainfo" "Installing AMD tools..."; then
_msg_red "AMD Tools" "Failed to install AMD monitoring tools."
tools_failed=true
fi
fi
if command -v vainfo &>/dev/null; then
vainfo
_pause "vainfo output shown above."
else
echo -e "${YELLOW}vainfo not available, skipping report.${NC}"
_run_cmd "AMD Tools" "sudo apt install -y ${amd_tools[*]} nvtop vainfo" "Installing AMD tools..."
fi
vainfo
_pause "vainfo output shown above."
if $tools_failed; then
echo -e "${RED}AMD tools installation failed.${NC}"
else
echo -e "${GREEN}AMD tools installed.${NC}"
fi
echo -e "${GREEN}AMD tools installed.${NC}"
}
install_intel_firmware() {
@@ -65,9 +48,7 @@ install_intel_firmware() {
local fw_info
fw_info=$(pkg_versions firmware-intel-graphics "$va_driver")
if _confirm "Intel Firmware" "Install Intel GPU firmware?\n\n${fw_info}"; then
if ! _run_cmd "Intel" "sudo apt install -y firmware-intel-graphics $va_driver" "Installing Intel GPU firmware..."; then
_msg_red "Intel Firmware" "Failed to install Intel GPU firmware."
fi
_run_cmd "Intel" "sudo apt install -y firmware-intel-graphics $va_driver" "Installing Intel GPU firmware..."
fi
}
@@ -106,20 +87,9 @@ offer_intel_tools() {
pkg_info=$(pkg_versions "${pkg_list[@]}" vainfo)
if _confirm "Intel Tools" "Intel GPU monitoring tools\n\n${driver_info}\n\nPackages:\n${pkg_info}"; then
local intel_failed=false
if ! _run_cmd "Intel Tools" "sudo apt install -y ${pkg_list[*]} vainfo" "Installing Intel monitoring tools..."; then
_msg_red "Intel Tools" "Failed to install Intel monitoring tools."
intel_failed=true
fi
if command -v vainfo &>/dev/null; then
vainfo
_pause "vainfo output shown above."
else
echo -e "${YELLOW}vainfo not available, skipping report.${NC}"
fi
if $intel_failed; then
echo -e "${RED}Intel monitoring tools installation failed.${NC}"
fi
_run_cmd "Intel Tools" "sudo apt install -y ${pkg_list[*]} vainfo" "Installing Intel monitoring tools..."
vainfo
_pause "vainfo output shown above."
else
echo "Skipping Intel monitoring tools."
fi
+292 -318
View File
@@ -1,386 +1,360 @@
#!/usr/bin/env bash
# NVIDIA GPU driver installation
# NVIDIA GPU driver installation — 3-CASE dispatch
#
# CASE A : Trixie + backports kernel → Official NVIDIA CUDA Repo (Pinned v590)
# CASE B : Kernel stable (any distro) → Debian stable
#
# Note: Debian 12 (Bookworm) backports reached EOL (2026-08-09);
# the NVIDIA flow only uses the stable repo for Debian 12.
# CASE B : Bookworm + backports kernel → Debian backports (-t bookworm-backports)
# CASE C : Kernel stable (any distro) → Debian stable (optional backports)
# --- DEPRECATED (Replaced by _install_nvidia_stack in gpu.sh) ---
install_nvidia_driver() {
echo -e "${YELLOW}NVIDIA GPU detected.${NC}"
NVIDIA_DRIVER_MODE=""
local is_bpo_kernel; is_bpo_kernel=$(is_backports_kernel)
local is_kepler; is_kepler=$(is_nvidia_kepler)
local is_maxwell; is_maxwell=$(is_nvidia_maxwell)
local is_pascal; is_pascal=$(is_nvidia_pascal)
local is_blackwell; is_blackwell=$(is_nvidia_blackwell)
# ── Blackwell: v550 no soporta GB20x → CUDA repo v590 ──
if [ "$DEBIAN_CODENAME" = "trixie" ] && [ "$is_blackwell" = "true" ]; then
_msg "NVIDIA Blackwell" \
"Your GPU is NVIDIA Blackwell architecture.\n\nDebian 13's nvidia-driver (v550) does not\nsupport Blackwell GPUs.\n\n\
The script will enable the official NVIDIA CUDA\nrepository and install the v590 production branch,\nwhich fully supports Blackwell (GB20x)." 14 65
_install_nvidia_cuda_repo
return
fi
# ── Veto: Kepler en Trixie no tiene driver disponible ──
if [ "$is_kepler" = "true" ] && [ "$DEBIAN_CODENAME" = "trixie" ]; then
_msg "NVIDIA Kepler" \
"Your GPU is NVIDIA Kepler architecture.\n\nThe nvidia-tesla-470 driver is not available\nin Debian 13 (Trixie).\n\nNo NVIDIA driver will be installed." 14 65
return 1
fi
# ── Bloqueo: Maxwell/Pascal no son compatibles con v590 ──
if [ "$DEBIAN_CODENAME" = "trixie" ] && [ "$is_bpo_kernel" = "true" ]; then
if [ "$is_maxwell" = "true" ] || [ "$is_pascal" = "true" ]; then
local gpu_gen="Maxwell"
[ "$is_pascal" = "true" ] && gpu_gen="Pascal"
local block_msg="INCOMPATIBILITY DETECTED: Your NVIDIA ${gpu_gen} GPU\n"
block_msg+="is NOT supported by the modern v590 driver.\n\n"
block_msg+="To run NVIDIA safely on Debian 13 (Trixie), you MUST use\n"
block_msg+="the official Debian v550 driver, which requires the\n"
block_msg+="standard STABLE Kernel.\n\n"
block_msg+="The script will automatically downgrade your path to\n"
block_msg+="Stable Kernel mode for NVIDIA."
_msg "NVIDIA — Trixie + Backports" "$block_msg" 14 70
is_bpo_kernel=false
fi
fi
# ── Dispatch por casos ──
if [ "$DEBIAN_CODENAME" = "trixie" ] && [ "$is_bpo_kernel" = "true" ]; then
_install_nvidia_cuda_repo
elif [ "$DEBIAN_CODENAME" = "bookworm" ] && [ "$is_bpo_kernel" = "true" ]; then
_install_nvidia_bookworm_bpo
else
_install_nvidia_standard
fi
}
# -------------------------------------------------------------------
# Shared helper: enable NVIDIA CUDA repo
# Debian 13 (Trixie): cuda-keyring (official method — broken extrepo)
# Debian 12 (Bookworm): extrepo nvidia-cuda (works)
# Shared helper: enable NVIDIA CUDA repo via extrepo
# -------------------------------------------------------------------
_enable_cuda_repo() {
_is_cuda_repo_ready && return 0
if [ "$DEBIAN_VERSION" = "13" ]; then
# Official NVIDIA method: cuda-keyring (extrepo does not
# configure the repo correctly on Trixie)
if dpkg -s cuda-keyring &>/dev/null; then
return 0 # already installed → its .list already exists
if [ ! -f /etc/apt/sources.list.d/extrepo_nvidia-cuda.sources ] && \
! grep -qr 'developer.download.nvidia.com' /etc/apt/sources.list.d/ 2>/dev/null; then
if ! command -v extrepo &>/dev/null; then
_run_cmd "extrepo" "sudo apt install -y extrepo" "Installing extrepo..."
fi
local tmp_deb
tmp_deb=$(mktemp "${TMPDIR:-/tmp}/cuda-keyring.XXXXXX.deb") || return 1
if ! wget -q "https://developer.download.nvidia.com/compute/cuda/repos/debian13/x86_64/cuda-keyring_1.1-1_all.deb" \
-O "$tmp_deb"; then
rm -f "$tmp_deb"
_msg "CUDA Repo — Error" "Failed to download cuda-keyring.\n\nNo NVIDIA driver was installed." 10 60
return 1
fi
if ! sudo dpkg -i "$tmp_deb"; then
rm -f "$tmp_deb"
_msg "CUDA Repo — Error" "Failed to install cuda-keyring.\n\nNo NVIDIA driver was installed." 10 60
return 1
fi
rm -f "$tmp_deb"
return 0
_run_cmd "CUDA Repo" \
"sudo extrepo enable nvidia-cuda" \
"Enabling official NVIDIA CUDA repository..."
fi
# Debian 12 (y otros): extrepo nvidia-cuda
if ! command -v extrepo &>/dev/null; then
_run_cmd "extrepo" "sudo apt install -y extrepo" "Installing extrepo..." || return 1
fi
_run_cmd "CUDA Repo" \
"sudo extrepo enable nvidia-cuda" \
"Enabling official NVIDIA CUDA repository..." || return 1
}
# -------------------------------------------------------------------
# Shared DKMS helpers: verify the NVIDIA module compiled for the
# currently running kernel; repair via dpkg-reconfigure if not
# -------------------------------------------------------------------
# Returns: 0 if the NVIDIA DKMS module shows "installed" for $(uname -r)
_nvidia_dkms_installed() {
local kernel line
kernel=$(uname -r)
line=$(dkms status 2>/dev/null | grep "^nvidia" | grep -F "$kernel" | grep ": installed" | head -1)
[ -n "$line" ]
}
# Returns 0 if Secure Boot is active (mokutil present and enabled)
_nvidia_secure_boot_enabled() {
command -v mokutil &>/dev/null || return 1
mokutil --sb-state 2>/dev/null | grep -q "SecureBoot enabled"
}
# Non-fatal notice: the DKMS module is compiled but NOT signed.
# Advanced users can sign it with MOK; novices need to know why
# they will see a black screen after reboot. If mokutil is missing,
# nothing is shown (mokutil is not preinstalled on Debian).
_warn_secure_boot() {
if _nvidia_secure_boot_enabled; then
echo -e "${RED}WARNING: Secure Boot is enabled. The NVIDIA DKMS module is compiled but NOT signed.${NC}"
echo -e "${RED}You MUST sign the module with MOK or disable Secure Boot in BIOS before rebooting.${NC}"
echo -e "${RED}See: https://wiki.debian.org/SecureBoot#Signing_kernel_modules${NC}"
fi
}
# Verify the DKMS build for the current kernel and repair it if needed.
# $@ = candidate dkms packages to reconfigure, in priority order
# (the first installed one is used for dpkg-reconfigure).
_verify_nvidia_dkms_build() {
local kernel
kernel=$(uname -r)
echo ""
echo "──────────────────────────────────────────────"
echo "Verifying DKMS module compilation for ${kernel}:"
if ! command -v dkms &>/dev/null; then
echo -e "${RED}(dkms not installed — DKMS build cannot be verified)${NC}"
echo "──────────────────────────────────────────────"
return 1
fi
dkms status 2>/dev/null | grep "^nvidia" || echo "(no nvidia DKMS module found)"
if _nvidia_dkms_installed; then
_warn_secure_boot
echo -e "${GREEN}DKMS module compiled for ${kernel}. Reboot required.${NC}"
echo "──────────────────────────────────────────────"
return 0
fi
echo -e "${YELLOW}DKMS module NOT compiled for ${kernel}. Repairing...${NC}"
local pkg repaired=false
for pkg in "$@"; do
if dpkg -l "$pkg" 2>/dev/null | grep -q "^ii"; then
_run_cmd "NVIDIA" "sudo dpkg-reconfigure $pkg" "Reconfiguring $pkg..."
repaired=true
break
fi
done
if ! $repaired; then
echo -e "${RED}No DKMS package found to reconfigure.${NC}"
fi
if _nvidia_dkms_installed; then
_warn_secure_boot
echo -e "${GREEN}DKMS module compiled successfully for ${kernel}. Reboot required.${NC}"
echo "──────────────────────────────────────────────"
return 0
else
echo -e "${RED}DKMS module still not compiled for ${kernel}.${NC}"
echo "Check the build log manually:"
echo " /var/lib/dkms/nvidia*/.../build/make.log"
echo " dmesg | grep nvidia"
echo "──────────────────────────────────────────────"
return 1
fi
}
# -------------------------------------------------------------------
# NVIDIA driver version selection (Debian 12/13)
# Sets the global NVIDIA_SELECTED_VERSION; first option is the default.
# Returns: 0 if a version was chosen, 1 if the user cancelled
# -------------------------------------------------------------------
_is_cuda_repo_ready() {
[ -f /etc/apt/sources.list.d/extrepo_nvidia-cuda.sources ] ||
grep -qr 'developer.download.nvidia.com' /etc/apt/sources.list.d/ 2>/dev/null
}
_show_nvidia_version_menu() {
local choice
if [ "$DEBIAN_VERSION" = "12" ]; then
choice=$(_menu "Select NVIDIA Driver for Debian 12 (Bookworm):" "Choose the NVIDIA driver version:" 12 70 3 \
"535" "v535 — Debian Official (Recommended)" \
"470" "v470 — Debian Legacy (Kepler Support)")
elif [ "$DEBIAN_VERSION" = "13" ]; then
choice=$(_menu "Select NVIDIA Driver for Debian 13 (Trixie):" "Choose the NVIDIA driver version:" 14 70 5 \
"550" "v550 — Debian Official (Recommended)" \
"590" "v590 — NVIDIA CUDA Repo (Production Branch)" \
"595" "v595 — NVIDIA CUDA Repo (Newest Stable)")
else
NVIDIA_SELECTED_VERSION="auto"
return 0
fi
[ -z "$choice" ] && return 1
NVIDIA_SELECTED_VERSION="$choice"
return 0
}
# -------------------------------------------------------------------
# NVIDIA Wayland/KMS base configuration (/etc/modprobe.d/nvidia-wayland.conf)
# Applies on Debian 12/13, any driver version (535/550/590/595).
# Architecture and hybrid detection are ORTHOGONAL:
# - Architecture → affects ONLY fbdev (kepler: the 470 does not
# support it) and the color/info message.
# - Hybrid vs desktop → affects NVreg (Preserve / kernel suspend
# notifier), INDEPENDENT of architecture.
# 590/595 add the kernel suspend notifier (complements, never
# replaces, NVreg_PreserveVideoMemoryAllocations).
# -------------------------------------------------------------------
_configure_nvidia_wayland() {
local ver="${1:-$NVIDIA_SELECTED_VERSION}"
local conf="/etc/modprobe.d/nvidia-wayland.conf"
local arch
local content=""
local color="${GREEN}"
local msg="Wayland config (desktop): KMS + video memory preservation enabled."
arch=$(_get_nvidia_arch_family)
# ── Architecture: fbdev only and color message ──
case "$arch" in
kepler)
color="${RED}"
msg="WARNING: Wayland not supported on Kepler. Use X11 (Xorg)."
;;
maxwell | pascal)
color="${YELLOW}"
msg="Wayland support on ${arch} is experimental. X11 recommended."
;;
esac
# ── Hybrid vs desktop: NVreg independent of architecture ──
if _is_hybrid_laptop; then
content="options nvidia-drm modeset=1"$'\n'
[ "$arch" != "kepler" ] && content+="options nvidia-drm fbdev=1"$'\n'
color="${GREEN}"
msg="Wayland config (hybrid laptop): KMS enabled — NVreg omitted."
else
content="options nvidia NVreg_PreserveVideoMemoryAllocations=1"$'\n'
case "$ver" in
590 | 595) content+="options nvidia NVreg_UseKernelSuspendNotifiers=1"$'\n' ;;
esac
content+="options nvidia-drm modeset=1"$'\n'
[ "$arch" != "kepler" ] && content+="options nvidia-drm fbdev=1"$'\n'
fi
printf "%b" "$content" | sudo tee "$conf" >/dev/null || return 1
echo -e "${color}${msg}${NC}"
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
}
# -------------------------------------------------------------------
# CASE A: Trixie + Backports Kernel → Official CUDA Repo (Pinned v590)
# -------------------------------------------------------------------
_install_nvidia_cuda_repo() {
local ver="${1:-590}"
local warn="WARNING: You are about to install NVIDIA v${ver} from\n"
warn+="the official NVIDIA CUDA repository.\n\n"
warn+="Source: Official NVIDIA CUDA Repo\n"
warn+="Driver: Production Branch v${ver} (unified metapackage)\n"
warn+="[+] nvidia-driver-pinning-${ver} (version lock)\n"
warn+="[+] nvidia-open (driver + open kernel modules)\n\n"
local warn="WARNING: Official Debian NVIDIA driver (v550)\n"
warn+="fails to compile on Trixie Backports Kernels.\n\n"
warn+="The script will enable the official NVIDIA CUDA\n"
warn+="repository and install the production branch v590\n"
warn+="using NVIDIA's unified driver pinning packages.\n\n"
warn+="Source: Official NVIDIA CUDA Repo (Pinned v590.*)\n"
warn+="Driver: Production Branch v590 (unified metapackage)\n"
warn+="[+] nvidia-driver (full 64-bit compute + graphics)\n"
warn+="[+] firmware-nvidia-gsp\n"
warn+="[+] nvidia-driver-pinning-590 (branch locking)\n"
warn+="[+] APT Pinning (version 590.*)\n\n"
warn+="Do you want to proceed at your own risk?"
if ! _confirm_custom "NVIDIA Driver — v${ver}" "$warn" "Proceed" "Abort" 18 70; then
if ! _confirm_custom "NVIDIA Driver — Trixie + Backports" "$warn" "Proceed" "Abort" 18 70; then
echo -e "${YELLOW}NVIDIA installation aborted by user.${NC}"
return 1
fi
# Step 1: Enable CUDA repo (cuda-keyring Trixie / extrepo Bookworm)
if ! _enable_cuda_repo; then
_msg "CUDA Repo — Error" "Failed to enable the official NVIDIA CUDA repository.\n\nNo NVIDIA driver was installed." 10 60
return 1
fi
# Step 1: Enable CUDA repo via extrepo
_enable_cuda_repo
# Step 2: explicit apt update — without an updated index the repo
# is invisible and apt would resolve the Debian candidate (v550)
# instead of v${ver}.
if ! _run_cmd "CUDA Repo" "sudo apt update" \
"Updating package lists after enabling CUDA repository..."; then
NVIDIA_DRIVER_MODE=""
_msg "CUDA Repo — Error" "Failed to update APT after enabling CUDA repo.\n\nNo NVIDIA driver was installed." 10 60
return 1
fi
# Step 2: Create APT pinning to lock v590
_run_cmd "APT Pinning" \
'printf "%s\n" "Package: *nvidia*" "Package: *cuda*" "Package: libcuda1" "Package: firmware-nvidia-gsp" "Pin: version 590.*" "Pin-Priority: 1001" | sudo tee /etc/apt/preferences.d/block-nvidia > /dev/null' \
"Creating APT pinning to lock NVIDIA to v590 branch..."
# Step 3: Official pinning — INDEPENDENT and mandatory APT
# transaction. APT reads /etc/apt/preferences.d/ at startup, not
# during the transaction: the pinning must be installed BEFORE
# installing the driver. If the repo does not publish it, it is
# an NVIDIA repo problem: abort cleanly instead of installing a
# version the user did not choose.
if ! _run_cmd "NVIDIA Pinning" \
"sudo apt install -y nvidia-driver-pinning-${ver}" \
"Installing NVIDIA version pinning (${ver})..."; then
NVIDIA_DRIVER_MODE=""
_msg "NVIDIA — Error" "Failed to install NVIDIA driver pinning ${ver}.\n\nNo NVIDIA driver was installed." 10 60
return 1
fi
# Step 4: Install the metapackage (pinning already active). If it
# fails, the pinning remains installed (config only, not a problem).
# firmware-nvidia-gsp arrives as a mandatory transitive dependency
# (nvidia-open → nvidia-kernel-open-dkms → firmware-nvidia-gsp),
# aligned with the official docs: apt -V install nvidia-open.
if ! _run_cmd "NVIDIA CUDA" \
"sudo apt install -y nvidia-open" \
"Installing NVIDIA driver from CUDA repository..."; then
NVIDIA_DRIVER_MODE=""
_msg "NVIDIA — Error" "NVIDIA driver installation FAILED.\n\nNo NVIDIA driver was installed." 10 60
return 1
fi
# Post-install: the installed DKMS module must match the ${ver} branch
local dkms_ver
dkms_ver=$(dpkg -l nvidia-kernel-dkms nvidia-kernel-open-dkms 2>/dev/null | awk '$1=="ii" {print $3; exit}')
if [[ "$dkms_ver" == ${ver}.* ]]; then
echo -e "${GREEN}DKMS module ${dkms_ver} matches branch v${ver}.${NC}"
else
echo -e "${RED}WARNING: DKMS package (${dkms_ver:-none}) does not match v${ver}.*${NC}"
fi
# Step 3: Install NVIDIA unified metapackages (driver pinning)
_run_cmd "NVIDIA CUDA" \
"sudo apt install -y nvidia-driver-pinning-590 nvidia-driver firmware-nvidia-gsp" \
"Installing NVIDIA v590 production driver via unified metapackages..."
NVIDIA_DRIVER_MODE="cuda-repo"
echo -e "${GREEN}NVIDIA Production Driver v${ver} installed from CUDA repo. Reboot required.${NC}"
echo -e "${GREEN}NVIDIA Production Driver v590 installed from CUDA repo. Reboot required.${NC}"
_verify_nvidia_dkms_build nvidia-kernel-open-dkms nvidia-kernel-dkms || true
echo ""
echo "──────────────────────────────────────────────"
echo "Verifying DKMS module compilation:"
if command -v dkms &>/dev/null; then
dkms status 2>/dev/null | grep nvidia || echo "(no nvidia DKMS module found)"
else
echo "(dkms not installed)"
fi
echo ""
echo "If the line ends with 'installed' → module is OK."
echo "Otherwise check: dmesg | grep nvidia"
echo "──────────────────────────────────────────────"
}
# -------------------------------------------------------------------
# Bookworm Kepler interception — forces nvidia-legacy-470xx-driver
# CASE B: Bookworm + Backports Kernel → Debian backports
# -------------------------------------------------------------------
_install_nvidia_bookworm_bpo() {
local nv_pkg=""
local is_kepler
is_kepler=$(is_nvidia_kepler)
if [ "$is_kepler" = "true" ]; then
nv_pkg="nvidia-tesla-470-driver"
else
local nd_ver
nd_ver=$(apt-cache policy nvidia-detect 2>/dev/null | awk 'NR==3 {print $2; exit}')
if _confirm "NVIDIA Detect" "Install nvidia-detect to determine the correct driver?\n\n nvidia-detect ${nd_ver:-unknown}" 12 70; then
_run_cmd "NVIDIA" "sudo apt install -y nvidia-detect" "Installing nvidia-detect..."
else
echo "Skipping NVIDIA driver detection."
NVIDIA_DRIVER_MODE=""
return 0
fi
local recommended
recommended=$(nvidia-detect 2>/dev/null | grep -oP 'nvidia[\w-]+(?= package)')
if [ -z "$recommended" ]; then
echo -e "${RED}nvidia-detect could not determine a suitable driver.${NC}"
return 1
fi
if [[ "$recommended" =~ legacy-390|legacy-340 ]]; then
echo -e "${RED}Your GPU requires $recommended, which is not available.${NC}"
return 1
fi
nv_pkg="$recommended"
fi
local nv_ver
nv_ver=$(apt-cache policy "$nv_pkg" 2>/dev/null | awk 'NR==3 {print $2; exit}')
local msg="Source: Debian Bookworm-Backports\n"
msg+="NVIDIA Driver: ${nv_pkg} ${nv_ver:-unknown}\n"
msg+=" (Compatible with Kernel v6.12+)\n"
msg+="[+] firmware-misc-nonfree\n"
msg+="[+] nvidia-vaapi-driver"
if ! _confirm "NVIDIA Driver — Backports" "$msg" 14 70; then
echo "Skipping NVIDIA driver installation."
return 0
fi
_run_cmd "NVIDIA" "sudo apt install -y -t bookworm-backports $nv_pkg firmware-misc-nonfree nvidia-vaapi-driver" \
"Installing NVIDIA driver from backports..."
NVIDIA_DRIVER_MODE="backports"
echo -e "${GREEN}NVIDIA driver installed from backports. Reboot required.${NC}"
echo ""
echo "──────────────────────────────────────────────"
echo "Verifying DKMS module compilation:"
if command -v dkms &>/dev/null; then
dkms status 2>/dev/null | grep nvidia || echo "(no nvidia DKMS module found)"
else
echo "(dkms not installed)"
fi
echo ""
echo "If the line ends with 'installed' → module is OK."
echo "Otherwise check: dmesg | grep nvidia"
echo "──────────────────────────────────────────────"
}
# -------------------------------------------------------------------
# Bookworm Kepler intercepción — fuerza nvidia-legacy-470xx-driver
# sin pasar por nvidia-detect (evita falsa recomendación rama 535)
# -------------------------------------------------------------------
_install_nvidia_bookworm_kepler() {
local nv_pkg="nvidia-tesla-470-driver"
local nv_pkg="nvidia-legacy-470xx-driver"
local nv_ver
nv_ver=$(apt-cache policy "$nv_pkg" 2>/dev/null | awk 'NR==3 {print $2; exit}') || true
nv_ver=$(apt-cache policy "$nv_pkg" 2>/dev/null | awk 'NR==3 {print $2; exit}')
echo -e "${YELLOW}Kepler GPU detected — forcing ${nv_pkg}.${NC}"
local msg="Kepler GPU detected (GKxxx).\n\n"
msg+="On Debian 12 Bookworm, Kepler requires the legacy\n"
msg+="driver instead of the modern one. Using package:\n"
local msg="Kepler GPU detectada (GKxxx).\n\n"
msg+="En Debian 12 Bookworm, Kepler requiere el driver legacy\n"
msg+="en lugar del moderno. Se usará el paquete:\n"
msg+=" ${nv_pkg} ${nv_ver:-unknown}\n"
msg+="to avoid black-screen failures.\n\n"
msg+="para evitar fallos de pantalla negra.\n\n"
msg+=" [SKIP] nvidia-detect (omitido — evita rama 535)\n"
msg+=" [USE] ${nv_pkg}\n"
msg+=" [+] linux-headers-amd64\n"
msg+=" [+] firmware-misc-nonfree\n"
msg+=" [+] nvidia-settings\n\n"
msg+="Install the legacy driver for Kepler?"
msg+="Instalar driver legacy para Kepler?"
if ! _confirm_custom "NVIDIA Kepler — Bookworm" "$msg" "Install" "Skip" 14 70; then
echo "Skipping Kepler driver."
echo "Omitiendo driver Kepler."
NVIDIA_DRIVER_MODE=""
return 0
fi
if ! _run_cmd "NVIDIA Kepler" \
"sudo apt install -y linux-headers-amd64 $nv_pkg firmware-misc-nonfree nvidia-settings" \
"Installing nvidia-legacy-470xx-driver..."; then
NVIDIA_DRIVER_MODE=""
_msg "NVIDIA Kepler — Error" "Kepler driver installation FAILED.\n\nNo NVIDIA driver was installed." 10 60
return 1
_run_cmd "NVIDIA Kepler" \
"sudo apt install -y $nv_pkg firmware-misc-nonfree nvidia-settings" \
"Instalando nvidia-legacy-470xx-driver..."
# Si backports está habilitado, ofrecer actualización
if [ "$(is_backports_enabled)" == "true" ]; then
local bpo_ver
bpo_ver=$(apt-cache madison "$nv_pkg" 2>/dev/null | \
grep "bookworm-backports" | awk '{print $3}' | head -1)
if [ -n "$bpo_ver" ]; then
local msg="Hay una versión en backports: ${bpo_ver}\n"
msg+="Instalar desde bookworm-backports?"
if _confirm "Kepler Backports" "$msg"; then
_run_cmd "NVIDIA Kepler" \
"sudo apt install -y -t bookworm-backports $nv_pkg" \
"Actualizando Kepler driver desde backports..."
NVIDIA_DRIVER_MODE="backports"
echo -e "${GREEN}Kepler driver actualizado desde backports.${NC}"
fi
fi
fi
NVIDIA_DRIVER_MODE="${NVIDIA_DRIVER_MODE:-stable}"
echo -e "${GREEN}Kepler driver (${nv_pkg}) installed. Reboot required.${NC}"
_verify_nvidia_dkms_build nvidia-tesla-470-kernel-dkms || true
echo ""
echo "──────────────────────────────────────────────"
echo "Verifying DKMS module compilation:"
if command -v dkms &>/dev/null; then
dkms status 2>/dev/null | grep nvidia || echo "(no nvidia DKMS module found)"
else
echo "(dkms not installed)"
fi
echo ""
echo "If the line ends with 'installed' → module is OK."
echo "Otherwise check: dmesg | grep nvidia"
echo "──────────────────────────────────────────────"
}
# -------------------------------------------------------------------
# CASE B: Kernel stable (any distro) → Debian stable
# CASE C: Kernel stable (any distro) → Debian stable, optional backports
# -------------------------------------------------------------------
_install_nvidia_standard() {
# --- 1. ARCHITECTURE → KERNEL MODULE ---
# Only Turing+ (known) uses the open module. Unknown/empty or
# old architecture (Kepler/Fermi/Maxwell/Pascal/Volta) →
# closed module as safe fallback.
local fam
fam=$(_get_nvidia_arch_family)
local kernel_pkg="nvidia-kernel-dkms"
case "$fam" in
turing | ampere | ada | blackwell) kernel_pkg="nvidia-open-kernel-dkms" ;;
esac
local nv_pkg=""
local use_bpo=false
local is_kepler
is_kepler=$(is_nvidia_kepler)
# --- 2. PACKAGES — a single apt install ---
local extra_pkgs="linux-headers-amd64 nvidia-driver firmware-nvidia-gsp nvidia-vaapi-driver"
[ "$DEBIAN_VERSION" = "12" ] && extra_pkgs+=" mesa-vdpau-drivers"
local install_pkgs="$kernel_pkg $extra_pkgs"
if [ "$is_kepler" = "true" ]; then
if [ "$DEBIAN_CODENAME" = "trixie" ]; then
_msg "NVIDIA Kepler" \
"Your GPU is NVIDIA Kepler architecture.\n\nThe nvidia-tesla-470 driver is not available\nin Debian 13 (Trixie).\n\nOptions:\n 1. Use Debian 12 (Bookworm) with nvidia-tesla-470\n 2. Use open-source Nouveau driver (limited)\n\nNo NVIDIA driver will be installed." 14 65
return 1
fi
nv_pkg="nvidia-tesla-470-driver"
echo -e "${YELLOW}Kepler GPU detected. Will use ${nv_pkg}.${NC}"
else
local nd_ver
nd_ver=$(apt-cache policy nvidia-detect 2>/dev/null | awk 'NR==3 {print $2; exit}')
if _confirm "NVIDIA Detect" "Install nvidia-detect to determine the correct driver?\n\n nvidia-detect ${nd_ver:-unknown}" 12 70; then
_run_cmd "NVIDIA" "sudo apt install -y nvidia-detect" "Installing nvidia-detect..."
else
echo "Skipping NVIDIA driver detection."
return 0
fi
local recommended
recommended=$(nvidia-detect 2>/dev/null | grep -oP 'nvidia[\w-]+(?= package)')
if [ -z "$recommended" ]; then
echo -e "${RED}nvidia-detect could not determine a suitable driver.${NC}"
return 1
fi
if [[ "$recommended" =~ legacy-390|legacy-340 ]]; then
echo -e "${RED}Your GPU requires $recommended, which is not available.${NC}"
return 1
fi
nv_pkg="$recommended"
fi
# --- 3. CONFIRMATION MESSAGE ---
local kernel_ver msg
kernel_ver=$(apt-cache policy "$kernel_pkg" 2>/dev/null | awk 'NR==3 {print $2; exit}') || true
# Check for backports (optional, if repo enabled)
local stable_nv_ver
stable_nv_ver=$(apt-cache policy "$nv_pkg" 2>/dev/null | awk 'NR==3 {print $2; exit}')
if [ "$(is_backports_enabled)" == "true" ]; then
local bpo_nv_ver
bpo_nv_ver=$(apt-cache madison "$nv_pkg" 2>/dev/null | \
grep "${DEBIAN_CODENAME}-backports" | awk '{print $3}' | head -1)
if [ -n "$bpo_nv_ver" ]; then
local msg="Source: Debian ${DEBIAN_CODENAME^} (Backports available)\n"
msg+="NVIDIA Driver: ${nv_pkg}\n\n"
msg+=" Backports: ${bpo_nv_ver}\n"
msg+=" Stable: ${stable_nv_ver:-unknown}\n\n"
msg+="Choose version:"
if _confirm_custom "NVIDIA Driver" "$msg" "Backports" "Stable" 14 70; then
use_bpo=true
fi
fi
fi
msg="Source: Debian ${DEBIAN_CODENAME^} Stable\n"
msg+="Kernel Module: ${kernel_pkg} ${kernel_ver:-unknown} (arch: ${fam:-unknown})\n"
msg+="[+] linux-headers-amd64\n"
msg+="[+] firmware-nvidia-gsp\n"
msg+="[+] nvidia-vaapi-driver"
[ "$DEBIAN_VERSION" = "12" ] && msg+="\n[+] mesa-vdpau-drivers"
local src_label="Debian ${DEBIAN_CODENAME^} Stable"
$use_bpo && src_label="Debian ${DEBIAN_CODENAME^}-Backports"
local msg="Source: ${src_label}\n"
msg+="NVIDIA Driver: ${nv_pkg} ${stable_nv_ver:-unknown}\n"
msg+="[+] firmware-misc-nonfree\n"
msg+="[+] nvidia-vaapi-driver\n"
msg+="[+] mesa-vdpau-drivers"
if ! _confirm "NVIDIA Driver" "$msg" 14 70; then
echo "Skipping NVIDIA driver installation."
return 0
fi
# --- 4. EXECUTION ---
if ! _run_cmd "NVIDIA" "sudo apt install -y $install_pkgs" \
"Installing NVIDIA driver from stable..."; then
NVIDIA_DRIVER_MODE=""
_msg "NVIDIA — Error" "NVIDIA driver installation FAILED.\n\nNo NVIDIA driver was installed." 10 60
return 1
fi
NVIDIA_DRIVER_MODE="stable"
# Mandatory fix for Debian 12 with the open module
if [ "$DEBIAN_VERSION" = "12" ] && [[ "$kernel_pkg" == *"open"* ]]; then
echo "options nvidia NVreg_OpenRmEnableUnsupportedGpus=1" | sudo tee /etc/modprobe.d/nvidia-open.conf >/dev/null
echo "Applied required Open RM parameter for Debian 12."
local extra_pkgs="firmware-misc-nonfree nvidia-vaapi-driver mesa-vdpau-drivers"
if $use_bpo; then
_run_cmd "NVIDIA" "sudo apt install -y -t ${DEBIAN_CODENAME}-backports $nv_pkg $extra_pkgs" \
"Installing NVIDIA driver from backports..."
NVIDIA_DRIVER_MODE="backports"
else
_run_cmd "NVIDIA" "sudo apt install -y $nv_pkg $extra_pkgs" \
"Installing NVIDIA driver from stable..."
NVIDIA_DRIVER_MODE="stable"
fi
# --- 5. DKMS POST-INSTALL VERIFICATION ---
echo -e "${GREEN}NVIDIA driver installed. Reboot required.${NC}"
_verify_nvidia_dkms_build "$kernel_pkg" || true
echo ""
echo "──────────────────────────────────────────────"
echo "Verifying DKMS module compilation:"
if command -v dkms &>/dev/null; then
dkms status 2>/dev/null | grep nvidia || echo "(no nvidia DKMS module found)"
else
echo "(dkms not installed)"
fi
echo ""
echo "If the line ends with 'installed' → module is OK."
echo "Otherwise check: dmesg | grep nvidia"
echo "──────────────────────────────────────────────"
}
-60
View File
@@ -1,60 +0,0 @@
#!/usr/bin/env bash
# nvidia_manage.sh — NVIDIA management menu: install/change or remove
_nvidia_driver_installed() {
dpkg -l 2>/dev/null | grep -qE "^ii (nvidia-driver|nvidia-tesla-470-driver|nvidia-open|nvidia-kernel-dkms|nvidia-kernel-open-dkms|nvidia-open-kernel-dkms)"
}
_nvidia_manage_menu() {
local choice
choice=$(_menu "NVIDIA Driver" "Manage NVIDIA Driver" 12 70 4 \
"1" "Install / Change NVIDIA Driver Version" \
"2" "Remove NVIDIA Driver and Restore Nouveau")
[ -z "$choice" ] && {
echo "Skipping NVIDIA installation."
return 1
}
case "$choice" in
2)
_nvidia_remove
return 1
;;
*) return 0 ;;
esac
}
_nvidia_remove() {
if ! _nvidia_driver_installed; then
echo -e "${YELLOW}NVIDIA driver is not installed on this system.${NC}"
return 0
fi
if ! _confirm "Remove NVIDIA" "This will:\n - Purge the NVIDIA driver packages\n - Remove script-generated configs\n - Remove the CUDA repo sources\n - Regenerate initramfs\n\nProceed?"; then
echo "NVIDIA removal cancelled."
return 0
fi
echo "Removing NVIDIA driver packages..."
sudo apt purge -y nvidia-driver nvidia-kernel-dkms nvidia-open-kernel-dkms nvidia-open nvidia-kernel-open-dkms nvidia-tesla-470-driver nvidia-settings nvidia-vaapi-driver firmware-nvidia-gsp mesa-vdpau-drivers nvidia-driver-pinning-* || true
echo "Cleaning up dependencies..."
sudo apt autoremove --purge -y || true
echo "Removing script-generated configuration..."
sudo rm -f /etc/modprobe.d/nvidia-wayland.conf || true
sudo rm -f /etc/modprobe.d/nvidia-open.conf || true
sudo rm -f /etc/apt/preferences.d/block-nvidia || true
sudo rm -f /etc/X11/xorg.conf.d/20-nvidia.conf || true
echo "Removing NVIDIA repository sources..."
sudo rm -f /etc/apt/sources.list.d/cuda-*.list || true
sudo rm -f /etc/apt/sources.list.d/extrepo_nvidia-cuda.sources || true
echo "Regenerating initramfs..."
sudo update-initramfs -u || true
echo -e "${GREEN}NVIDIA driver removed. Please reboot your system.${NC}"
return 0
}
-74
View File
@@ -1,74 +0,0 @@
#!/usr/bin/env bash
# hw_detect.sh — Shared hardware device detection (PCI + USB)
# License GPL v3
# ── Global arrays (populated by _detect_all_network_devices) ──
PCI_NET_DEVS=()
USB_WIFI_DEVS=()
PCI_BT_DEVS=()
USB_BT_DEVS=()
_FW_PLAN_HW_LINES=()
# ── Network device detection (PCI + USB) ──
_detect_all_network_devices() {
! is_installed pciutils && _install_pkg pciutils
! is_installed usbutils && _install_pkg usbutils
PCI_NET_DEVS=()
while IFS= read -r line; do
PCI_NET_DEVS+=("$line")
done < <(echo "$LSPCI_OUTPUT" | grep -iE 'network controller|ethernet controller' || true)
USB_WIFI_DEVS=()
while IFS= read -r line; do
# Exclude Bluetooth dongles: many report e.g. "Bluetooth wireless
# interface" and would otherwise be classified as WiFi and mapped
# to firmware-iwlwifi.
if echo "$line" | grep -qiE 'wireless|wifi|802\.11|wlan' &&
! echo "$line" | grep -qi 'bluetooth'; then
USB_WIFI_DEVS+=("$line")
fi
done < <(lsusb 2>/dev/null || true)
PCI_BT_DEVS=()
while IFS= read -r line; do
PCI_BT_DEVS+=("$line")
done < <(echo "$LSPCI_OUTPUT" | grep -i 'Bluetooth controller' || true)
USB_BT_DEVS=()
while IFS= read -r line; do
# All Bluetooth dongles belong here (the WiFi filter above now
# excludes anything containing "bluetooth").
if echo "$line" | grep -qi 'bluetooth'; then
USB_BT_DEVS+=("$line")
fi
done < <(lsusb 2>/dev/null || true)
_FW_PLAN_HW_LINES=()
for dev in "${PCI_NET_DEVS[@]}"; do
local desc dev_type
desc=$(echo "$dev" | sed -E 's/^[^ ]+ [^:]+: //; s/ \[[0-9a-fA-F]{4}:[0-9a-fA-F]{4}\]//; s/ \(rev.*\)//')
if echo "$dev" | grep -qiE 'network controller|wireless|wi-fi|wlan|802\.11'; then
dev_type="WiFi PCI"
else
dev_type="Ethernet PCI"
fi
_FW_PLAN_HW_LINES+=(" \xe2\x97\x8f ${desc} (${dev_type})")
done
for dev in "${USB_WIFI_DEVS[@]}"; do
local desc
desc=$(echo "$dev" | sed 's/^.*ID //')
_FW_PLAN_HW_LINES+=(" \xe2\x97\x8f ${desc} (USB)")
done
for dev in "${PCI_BT_DEVS[@]}"; do
local desc
desc=$(echo "$dev" | sed -E 's/^[^ ]+ [^:]+: //; s/ \[[0-9a-fA-F]{4}:[0-9a-fA-F]{4}\]//; s/ \(rev.*\)//')
_FW_PLAN_HW_LINES+=(" \xe2\x97\x8f ${desc} (Bluetooth PCI)")
done
for dev in "${USB_BT_DEVS[@]}"; do
local desc
desc=$(echo "$dev" | sed 's/^.*ID //')
_FW_PLAN_HW_LINES+=(" \xe2\x97\x8f ${desc} (Bluetooth USB)")
done
}
+17 -23
View File
@@ -4,7 +4,7 @@
show_kernel_menu() {
while true; do
local items=("stable" "Install linux-image-amd64")
[ "$DEBIAN_VERSION" = "13" ] && items+=("backports" "Install from backports")
[ "$DEBIAN_VERSION" != "11" ] && items+=("backports" "Install from backports")
items+=("rt" "Install linux-image-rt-amd64 (Preempt-RT)")
items+=("cloud" "Install linux-image-cloud-amd64")
items+=("back" "Return to main menu")
@@ -16,27 +16,23 @@ show_kernel_menu() {
clear
case "$choice" in
stable) _install_kernel_package "linux-image-amd64" "Stable" "" ;;
rt) _install_kernel_package "linux-image-rt-amd64" "RT" "" ;;
cloud) _install_kernel_package "linux-image-cloud-amd64" "Cloud" "" ;;
backports)
if [ "$(is_backports_enabled)" != "true" ]; then
_msg "Kernel" "Backports repository is not enabled.\n\nUse option 3 (Configure repositories) to enable backports\nbefore installing the backports kernel."
else
_install_kernel_package "linux-image-amd64" "Backports" \
"-t ${DEBIAN_CODENAME}-backports"
fi
;;
back) break ;;
stable) _install_kernel_package "linux-image-amd64" "Stable" "" ;;
rt) _install_kernel_package "linux-image-rt-amd64" "RT" "" ;;
cloud) _install_kernel_package "linux-image-cloud-amd64" "Cloud" "" ;;
backports)
if [ "$(is_backports_enabled)" != "true" ]; then
_msg "Kernel" "Backports repository is not enabled.\n\nUse option 3 (Configure repositories) to enable backports\nbefore installing the backports kernel."
else
_install_kernel_package "linux-image-amd64" "Backports" \
"-t ${DEBIAN_CODENAME}-backports"
fi
;;
back) break ;;
esac
done
}
_install_kernel_package() {
# Hardware state is detected on demand; GPU_TYPE drives
# the NVIDIA DKMS/RT warnings below.
_ensure_state_detected
local pkg_base="$1"
local flavor="$2"
local bpo_flag="$3"
@@ -48,8 +44,7 @@ _install_kernel_package() {
if [ "$flavor" = "Backports" ] && [ "$GPU_TYPE" = "nvidia" ]; then
if ! _confirm "Kernel" "WARNING: Backports kernel changes the kernel version.\nYour NVIDIA driver will need recompilation (DKMS).\n\nProceed?"; then
echo "Skipping."
return
echo "Skipping."; return
fi
fi
if [ "$flavor" = "RT" ] && [ "$GPU_TYPE" = "nvidia" ]; then
@@ -59,8 +54,8 @@ _install_kernel_package() {
local headers_pkg="${pkg_base/linux-image-/linux-headers-}"
local ver headers_ver
if [ -n "$bpo_flag" ]; then
ver=$(apt-cache madison "$pkg_base" 2>/dev/null | grep "${DEBIAN_CODENAME}-backports" | awk '{print $3}' | head -1 || true)
headers_ver=$(apt-cache madison "$headers_pkg" 2>/dev/null | grep "${DEBIAN_CODENAME}-backports" | awk '{print $3}' | head -1 || true)
ver=$(apt-cache madison "$pkg_base" 2>/dev/null | grep "${DEBIAN_CODENAME}-backports" | awk '{print $3}' | head -1)
headers_ver=$(apt-cache madison "$headers_pkg" 2>/dev/null | grep "${DEBIAN_CODENAME}-backports" | awk '{print $3}' | head -1)
else
ver=$(apt-cache show "$pkg_base" 2>/dev/null | sed -n 's/^Version: //p' | grep -v '~bpo' | head -1)
headers_ver=$(apt-cache show "$headers_pkg" 2>/dev/null | sed -n 's/^Version: //p' | grep -v '~bpo' | head -1)
@@ -71,8 +66,7 @@ _install_kernel_package() {
[ -n "$bpo_flag" ] && summary+="\n From: ${DEBIAN_CODENAME^}-backports"
if ! _confirm "Kernel — ${flavor}" "$summary"; then
echo "Skipping."
return
echo "Skipping."; return
fi
_run_cmd "Kernel" "sudo apt install -y ${bpo_flag} ${pkg_base} ${headers_pkg}" \
+60 -205
View File
@@ -6,18 +6,12 @@ source "${MODULES_DIR}/repos/migrate.sh" 2>/dev/null || true
REPO_BACKUP_DIR=""
backup_current_repos() {
cleanup_repo_backup # never orphan a previous backup by overwriting the pointer
REPO_BACKUP_DIR=$(mktemp -d) || {
REPO_BACKUP_DIR=""
return 1
}
REPO_BACKUP_DIR=$(mktemp -d)
for f in /etc/apt/sources.list /etc/apt/sources.list.d/debian.sources \
/etc/apt/sources.list.d/debian-backports.list /etc/apt/sources.list.d/debian-backports.sources; do
/etc/apt/sources.list.d/debian-backports.list /etc/apt/sources.list.d/debian-backports.sources; do
if [ -f "$f" ]; then
local rel="${f#/etc/apt/}"
mkdir -p "$REPO_BACKUP_DIR/$(dirname "$rel")" || return 1
cp "$f" "$REPO_BACKUP_DIR/$rel" || return 1
touch "$REPO_BACKUP_DIR/.backed_up_$(basename "$rel")"
mkdir -p "$REPO_BACKUP_DIR/$(dirname "${f#/etc/apt/}")"
cp "$f" "$REPO_BACKUP_DIR/$(dirname "${f#/etc/apt/}")/$(basename "$f")"
fi
done
}
@@ -29,24 +23,14 @@ restore_previous_repos() {
echo -e "${RED}Restoring previous repository configuration...${NC}"
local found=false
for f in /etc/apt/sources.list /etc/apt/sources.list.d/debian.sources \
/etc/apt/sources.list.d/debian-backports.list /etc/apt/sources.list.d/debian-backports.sources; do
/etc/apt/sources.list.d/debian-backports.list /etc/apt/sources.list.d/debian-backports.sources; do
local rel="${f#/etc/apt/}"
local backup_file="$REPO_BACKUP_DIR/$rel"
if [ -f "$backup_file" ]; then
if sudo cp "$backup_file" "$f"; then
found=true
else
echo -e "${RED}Failed to restore $f${NC}"
fi
elif [ -f "$f" ] && [ ! -f "$REPO_BACKUP_DIR/.backed_up_$(basename "$f")" ]; then
# Only delete a live file if the manifest proves it did not exist
# when the backup was taken (protects against a failed cp).
# SECURITY: Verify file is not a symlink to prevent TOCTOU attack.
if [ -L "$f" ]; then
echo -e "${RED}[$f] is a symlink. Aborting to prevent TOCTOU attack.${NC}" >&2
continue
fi
sudo rm -f "$f" || true
sudo cp "$backup_file" "$f"
found=true
elif [ -f "$f" ]; then
sudo rm -f "$f"
found=true
fi
done
@@ -71,8 +55,8 @@ cleanup_repo_backup() {
_clean_embedded_backports_classic() {
local codename="$1"
local file="/etc/apt/sources.list"
[ ! -f "$file" ] && return 0
grep -qE "^[^#]*${codename}-backports\b" "$file" 2>/dev/null || return 0
[ ! -f "$file" ] && return
grep -qE "^[^#]*${codename}-backports\b" "$file" 2>/dev/null || return
if _confirm "Clean Classic Sources" "Remove backports line from ${file}?"; then
sudo sed -i "/${codename}-backports/d" "$file"
echo "Removed backports from ${file}"
@@ -82,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 0
[ ! -f "$file" ] && return
# Only proceed if the Suites line contains the backports token
grep -qE "^Suites:.*${codename}-backports\b" "$file" 2>/dev/null || return 0
grep -qE "^Suites:.*${codename}-backports\b" "$file" 2>/dev/null || return
if _confirm "Clean deb822 Sources" "Remove backports suite from ${file}?"; then
# Remove only the backports token, leaving other suites intact
@@ -119,7 +103,7 @@ _write_deb822() {
if content_differs "$main_file" "$main_content"; then
if _confirm "Deb822 Sources" "Write main deb822 configuration to ${main_file}?"; then
sudo mkdir -p /etc/apt/sources.list.d
echo -e "$main_content" | sudo tee "$main_file" >/dev/null || return 1
echo -e "$main_content" | sudo tee "$main_file" > /dev/null
echo "Wrote ${main_file}"
else
echo "Main repository configuration skipped."
@@ -129,13 +113,13 @@ _write_deb822() {
# Backports: always in separate file
if [ "$bp_enabled" = true ]; then
_write_deb822_backports "$codename" || true
_write_deb822_backports "$codename"
else
_remove_deb822_backports "$codename" || true
_remove_deb822_backports "$codename"
fi
# On migration from classic, disable the old file
if [ "$action" = "migrate" ] && { [ "$bp_location" = "embedded-classic" ] || [ "$bp_location" = "standalone-classic" ]; }; then
if [ "$action" = "migrate" ] && [ "$bp_location" = "embedded-classic" -o "$bp_location" = "standalone-classic" ]; then
if [ -f /etc/apt/sources.list ]; then
if _confirm "Disable Classic" "Migrating to deb822. Disable /etc/apt/sources.list?"; then
sudo mv /etc/apt/sources.list /etc/apt/sources.list.disabled
@@ -162,16 +146,14 @@ _write_deb822_backports() {
if content_differs "$bp_file" "$bp_content"; then
if _confirm "Deb822 Backports" "Write backports to ${bp_file}?"; then
sudo mkdir -p /etc/apt/sources.list.d
echo -e "$bp_content" | sudo tee "$bp_file" >/dev/null
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" || true
grep -qE "^Suites:.*${codename}-backports\b" /etc/apt/sources.list.d/debian.sources 2>/dev/null && \
_clean_embedded_backports_deb822 "$codename"
}
_remove_deb822_backports() {
@@ -182,13 +164,11 @@ _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
# Also clean any embedded backports in main debian.sources
grep -qE "^Suites:.*${codename}-backports\b" /etc/apt/sources.list.d/debian.sources 2>/dev/null &&
grep -qE "^Suites:.*${codename}-backports\b" /etc/apt/sources.list.d/debian.sources 2>/dev/null && \
_clean_embedded_backports_deb822 "$codename"
# Clean embedded backports from classic file too (safety net)
@@ -215,7 +195,7 @@ _write_classic() {
if content_differs "$main_file" "$main_content"; then
if _confirm "Classic Sources" "Write main classic configuration to ${main_file}?"; then
echo -e "$main_content" | sudo tee "$main_file" >/dev/null || return 1
echo -e "$main_content" | sudo tee "$main_file" > /dev/null
echo "Wrote ${main_file}"
else
echo "Main repository configuration skipped."
@@ -225,9 +205,9 @@ _write_classic() {
# Backports: always in separate file
if [ "$bp_enabled" = true ]; then
_write_classic_backports "$codename" || true
_write_classic_backports "$codename"
else
_remove_classic_backports "$codename" || true
_remove_classic_backports "$codename"
fi
# On migration from deb822, disable the old file
@@ -256,16 +236,14 @@ _write_classic_backports() {
if content_differs "$bp_file" "$bp_content"; then
if _confirm "Classic Backports" "Write backports to ${bp_file}?"; then
sudo mkdir -p /etc/apt/sources.list.d
echo -e "$bp_content" | sudo tee "$bp_file" >/dev/null
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" || true
grep -qE "^[^#]*${codename}-backports\b" /etc/apt/sources.list 2>/dev/null && \
_clean_embedded_backports_classic "$codename"
}
_remove_classic_backports() {
@@ -276,13 +254,11 @@ _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
# Also clean any embedded backports in main sources.list
grep -qE "^[^#]*${codename}-backports\b" /etc/apt/sources.list 2>/dev/null &&
grep -qE "^[^#]*${codename}-backports\b" /etc/apt/sources.list 2>/dev/null && \
_clean_embedded_backports_classic "$codename"
# Clean embedded backports from deb822 file too (safety net)
@@ -325,17 +301,17 @@ configure_repos() {
clear
case "$repo_choice" in
1) _repos_enable_components ;;
2) _repos_migrate_format ;;
3)
if [ "$DEBIAN_CODENAME" = "sid" ]; then
break
else
_repos_setup_backports
fi
;;
4) _branch_migration || true ;;
5) break ;;
1) _repos_enable_components ;;
2) _repos_migrate_format ;;
3)
if [ "$DEBIAN_CODENAME" = "sid" ]; then
break
else
_repos_setup_backports
fi
;;
4) _branch_migration || true ;;
5) break ;;
esac
done
}
@@ -362,15 +338,10 @@ _components_enabled() {
_repos_offer_upgrade() {
local upgradable
upgradable=$(apt list --upgradable 2>/dev/null | grep -c / || true)
# BH-005: grep -c / returns rc=1 if apt list produces no output
# (0 upgradable packages or network failure). pipefail propagates rc=1.
upgradable=${upgradable:-0}
[[ "$upgradable" =~ ^[0-9]+$ ]] || upgradable=0
# Validate that the result is a positive integer.
# If apt list fails, upgradable stays as "0" and the upgrade is skipped.
upgradable=$(apt list --upgradable 2>/dev/null | grep -c /)
if [ "$upgradable" -gt 0 ]; then
if _confirm "Upgrade System" "$upgradable packages can be upgraded. Upgrade now?"; then
sudo apt-mark hold tzdata 2>/dev/null || true
_run_cmd "Upgrade" "sudo apt upgrade -y" "Upgrading system..."
sudo apt-mark unhold tzdata 2>/dev/null || true
sudo apt autoremove -y
@@ -382,98 +353,18 @@ _repos_offer_upgrade() {
fi
}
# Bootstrap a complete repository configuration from scratch when no active
# sources exist. Caller must run backup_current_repos() first so that a failed
# apt update can restore the previous state.
# Returns: 0 on success, 1 if skipped, declined or failed
bootstrap_repositories() {
local components="$1"
local bp_enabled="${2:-false}"
local bp_location="${3:-none}"
if [ -z "$DEBIAN_CODENAME" ]; then
_msg "Error" "Cannot bootstrap repositories: Debian codename unknown." 10 65
return 1
fi
# non-free-firmware does not exist on Bullseye
if [ "$DEBIAN_VERSION" = "11" ]; then
components=$(echo "$components" | sed 's/ non-free-firmware//g')
fi
if ! _confirm "Bootstrap Repositories" \
"No active APT sources were found (empty or missing sources.list).\n\n\
Configure repositories from scratch?" 10 65; then
echo "Repository configuration skipped."
cleanup_repo_backup
return 1
fi
if has_active_deb_sources; then
if ! _confirm "Custom Sources Found" \
"Active sources exist in other files (e.g. sources.list.d/*.sources).\n\n\
Adding deb.debian.org may duplicate your current mirror configuration. Continue?" 10 65; then
cleanup_repo_backup
return 1
fi
fi
# DEB822 is Trixie-only; Debian 11/12 always use classic
local use_deb822=false
if [ "$DEBIAN_VERSION" = "13" ]; then
local choice
choice=$(_menu "Repo Format" "Choose the repository format:" 12 60 3 \
"deb822" "Native DEB822 format (sources.list.d/debian.sources)" \
"classic" "Classic format (/etc/apt/sources.list)")
if [ "$choice" = "deb822" ]; then
use_deb822=true
fi
fi
if $use_deb822; then
_write_deb822 "$DEBIAN_CODENAME" "write" "$bp_enabled" "$bp_location" "$components" || {
cleanup_repo_backup
return 1
}
else
_write_classic "$DEBIAN_CODENAME" "write" "$bp_enabled" "$bp_location" "$components" || {
cleanup_repo_backup
return 1
}
fi
# Tidy: with deb822 chosen, an empty classic file is no longer needed
if $use_deb822 && [ -f /etc/apt/sources.list ] && ! grep -qE '^[^#]*\bdeb\b' /etc/apt/sources.list 2>/dev/null; then
if _confirm "Disable Classic" "An empty /etc/apt/sources.list is no longer needed. Move it aside (sources.list.disabled)?"; then
sudo mv /etc/apt/sources.list /etc/apt/sources.list.disabled
echo "Empty sources.list renamed to sources.list.disabled"
fi
fi
echo "Updating package lists..."
if sudo apt update; then
cleanup_repo_backup
echo -e "${GREEN}Repository components configured.${NC}"
return 0
else
restore_previous_repos
echo -e "${RED}apt update failed. Previous configuration restored.${NC}"
return 1
fi
}
_repos_enable_components() {
local current_format bp_enabled bp_location components
current_format=$(detect_repo_format)
if _components_enabled; then
if _confirm "Disable Components" \
if ! _confirm "Disable Components" \
"Contrib and non-free are already enabled. Disable them?"; then
components="main"
else
components=$(detect_active_components)
echo "Keeping current components: $components"
echo "No changes made."
_pause
return
fi
components="main"
else
if ! _confirm "Enable Components" \
"Enable contrib and non-free components?\n\n\
@@ -499,47 +390,15 @@ popular software (like gaming platforms and proprietary tools)." 12 60; then
backup_current_repos
if [ "$current_format" = "none" ]; then
if ! bootstrap_repositories "$components" "$bp_enabled" "$bp_location"; then
_pause
return
fi
_repos_offer_upgrade
echo -e "${GREEN}Cleaning old packages...${NC}"
sudo apt autoremove -y
sudo apt autoclean
echo -e "${GREEN}System packages cleaned.${NC}"
_pause
return
fi
echo "Verifying repository configuration (main, updates, security)..."
if [ "$current_format" = "deb822" ]; then
# Mixed-state guard: active classic lines alongside deb822 would duplicate sources
if [ -f /etc/apt/sources.list ] && grep -qE '^[^#]*\bdeb\b' /etc/apt/sources.list 2>/dev/null; then
if ! _confirm "Duplicate Config" \
"Active classic sources were also found in /etc/apt/sources.list.\n\n\
Writing debian.sources may duplicate your configuration. Continue?" 10 65; then
echo "No changes made."
_pause
return
fi
fi
if ! _write_deb822 "$DEBIAN_CODENAME" "write" "$bp_enabled" "$bp_location" "$components"; then
echo "No changes made."
_pause
return
fi
if [ "$current_format" = "deb822" ] || [ "$current_format" = "none" ]; then
_write_deb822 "$DEBIAN_CODENAME" "write" "$bp_enabled" "$bp_location" "$components"
else
if ! _write_classic "$DEBIAN_CODENAME" "write" "$bp_enabled" "$bp_location" "$components"; then
echo "No changes made."
_pause
return
fi
_write_classic "$DEBIAN_CODENAME" "write" "$bp_enabled" "$bp_location" "$components"
fi
echo "Updating package lists..."
if sudo apt update; then
REPOS_CONFIGURED=true
cleanup_repo_backup
echo -e "${GREEN}Repository components configured.${NC}"
_repos_offer_upgrade
@@ -583,6 +442,7 @@ _repos_migrate_format() {
echo "Updating package lists..."
if sudo apt update; then
REPOS_CONFIGURED=true
cleanup_repo_backup
echo -e "${GREEN}Repository format migrated to DEB822.${NC}"
else
@@ -613,8 +473,8 @@ Answer NO to disable or remove backports if they are currently enabled." 16 70;
fi
# Nothing to do — already in desired state
if { $enable_backports && [ "$bp_status" = "enabled" ]; } ||
{ ! $enable_backports && [ "$bp_status" = "disabled" ]; }; then
if { $enable_backports && [ "$bp_status" = "enabled" ]; } || \
{ ! $enable_backports && [ "$bp_status" = "disabled" ]; }; then
echo "Backports are already configured as requested."
_pause
return
@@ -622,23 +482,18 @@ 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_ok=false
_write_deb822_backports "$DEBIAN_CODENAME"
else
_write_classic_backports "$DEBIAN_CODENAME" || write_ok=false
_write_classic_backports "$DEBIAN_CODENAME"
fi
elif [ "$current_format" = "deb822" ]; then
_remove_deb822_backports "$DEBIAN_CODENAME" || write_ok=false
else
_remove_classic_backports "$DEBIAN_CODENAME" || write_ok=false
fi
if ! $write_ok; then
echo "No changes made."
_pause
return
if [ "$current_format" = "deb822" ]; then
_remove_deb822_backports "$DEBIAN_CODENAME"
else
_remove_classic_backports "$DEBIAN_CODENAME"
fi
fi
echo "Updating package lists..."
+31 -123
View File
@@ -13,13 +13,7 @@ _persistent_backup_repos() {
[ -f /etc/apt/sources.list ] && files+=("/etc/apt/sources.list")
[ -d /etc/apt/sources.list.d ] && files+=("/etc/apt/sources.list.d")
[ ${#files[@]} -eq 0 ] && return 1
# The rollback promise depends on this archive:
# a failed backup must abort before anything is
# deleted or rewritten.
if ! sudo tar czf "$_MIGRATE_BACKUP" "${files[@]}" 2>/dev/null; then
rm -f "$_MIGRATE_BACKUP"
return 1
fi
sudo tar czf "$_MIGRATE_BACKUP" "${files[@]}" 2>/dev/null
echo "Backup: $_MIGRATE_BACKUP"
}
@@ -29,45 +23,31 @@ _restore_backup() {
return 1
fi
echo -e "${YELLOW}Restoring repository backup...${NC}"
if ! sudo tar xzf "$_MIGRATE_BACKUP" -C / 2>/dev/null; then
_msg_red "Restore Failed" \
"Extraction of $_MIGRATE_BACKUP failed.\n\
/etc/apt sources may be missing or inconsistent.\n\n\
Restore manually:\n sudo tar xzf $_MIGRATE_BACKUP -C /" 12 70
return 1
fi
sudo tar xzf "$_MIGRATE_BACKUP" -C / 2>/dev/null
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"
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+="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"
@@ -76,60 +56,20 @@ _write_deb822_branch() {
fi
sudo mkdir -p /etc/apt/sources.list.d
# Stage to a temp file and move atomically: an
# interrupted write never leaves a partial sources.
if ! echo -e "$main_content" | sudo tee "${main_file}.tmp" >/dev/null; then
return 1
fi
if ! sudo mv "${main_file}.tmp" "$main_file"; then
return 1
fi
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
# Stage to a temp file and move atomically: an
# interrupted write never leaves a partial sources.
if ! echo -e "$main_content" | sudo tee "${main_file}.tmp" >/dev/null; then
return 1
fi
if ! sudo mv "${main_file}.tmp" "$main_file"; then
return 1
fi
echo -e "$main_content" | sudo tee "$main_file" > /dev/null
echo "Wrote $main_file"
}
_branch_migration() {
# Unattended guard: branch migration is the one operation
# that can leave the system unbootable, so --yes alone is
# not enough — it also requires --allow-destructive even
# when every other prompt auto-accepts.
if [ "${AUTO_YES:-0}" = "1" ] && [ "${ALLOW_DESTRUCTIVE:-0}" != "1" ]; then
echo "Branch migration is destructive. Re-run with --allow-destructive, or drop --yes to confirm interactively." >&2
return 1
fi
# ── Screen 1: Risk warning ──
_msg_red "WARNING: Branch Migration" \
"Migrating from Debian Stable to Testing or SID is a\n\
"Migrating from Debian Stable to Testing or SID is a\n\
MAJOR change and CAN make your system UNBOOTABLE.\n\n\
Risks include:\n\
• NVIDIA / DKMS drivers may break\n\
• System may fail to boot after reboot\n\
• Some packages may be removed or replaced\n\
• SID (unstable) has no release cycle; security\n\
fixes arrive via package uploads, not DSAs\n\n\
• SID (unstable) receives NO security updates\n\n\
A full persistent backup will be saved to /var/backups/\n\
so you can restore if things go wrong." 16 70
@@ -142,7 +82,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 sources for the target branch\n"
plan+=" 3. Write new DEB822 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"
@@ -161,10 +101,7 @@ so you can restore if things go wrong." 16 70
branch=$(_inputbox "Target Branch" \
"Type exactly TESTING or SID (case-sensitive):" 10 60 "")
[ -z "$branch" ] && {
echo "Migration cancelled."
return
}
[ -z "$branch" ] && { echo "Migration cancelled."; return; }
if [ "$branch" != "TESTING" ] && [ "$branch" != "SID" ]; then
_msg "Invalid Branch" "You typed: $branch\n\nExpected: TESTING or SID (exact, case-sensitive).\nAborting." 10 60
@@ -190,19 +127,11 @@ so you can restore if things go wrong." 16 70
[ -f /etc/apt/sources.list.d/debian-backports.sources ] && sudo rm -f /etc/apt/sources.list.d/debian-backports.sources
[ -f /etc/apt/sources.list.d/debian-backports.list ] && sudo rm -f /etc/apt/sources.list.d/debian-backports.list
# 4c. Write new sources
if ! _write_branch_sources "$target"; then
echo -e "${RED}[-]${NC} Failed to write new sources. Restoring backup..."
_restore_backup || true
return 1
fi
# 4c. Remove any old classic source files to avoid conflicts
[ -f /etc/apt/sources.list ] && sudo rm -f /etc/apt/sources.list
# 4d. Remove the old classic file AFTER the new
# sources are live: an interrupt between steps
# leaves both files (recoverable), not none.
# Only needed on the deb822 path; the classic
# writer replaced sources.list atomically.
[ "$DEBIAN_VERSION" = "13" ] && [ -f /etc/apt/sources.list ] && sudo rm -f /etc/apt/sources.list
# 4d. Write new sources
_write_deb822_branch "$target"
# 4e. SID guardrails: install bug alerts before upgrade
if [ "$target" = "sid" ]; then
@@ -215,40 +144,19 @@ so you can restore if things go wrong." 16 70
echo -e "${YELLOW}Running apt update...${NC}"
if ! sudo apt update; then
echo -e "${RED}apt update failed. Restoring backup...${NC}"
if _restore_backup; then
_msg_red "Migration Failed" \
"apt update failed. Backup has been restored from:\n\
_restore_backup
_msg_red "Migration Failed" \
"apt update failed. Backup has been restored from:\n\
$_MIGRATE_BACKUP\n\n\
Your system should be back to its previous state.\n\
Run 'sudo apt update' manually to verify." 12 70
else
_msg_red "Migration Failed" \
"apt update failed AND the backup could not be\n\
restored from $_MIGRATE_BACKUP.\n\n\
Restore it manually:\n sudo tar xzf $_MIGRATE_BACKUP -C /\n\
then run: sudo apt update" 12 70
fi
return 1
return
fi
# 4g. Full upgrade
# A failed upgrade leaves packages half-installed.
# Restoring only the sources would be worse (stable
# sources + testing packages), so stop with an
# honest report and manual-recovery instructions.
if ! _run_cmd "Upgrade" "sudo apt upgrade -y" "Upgrading packages..."; then
_msg_red "Migration Incomplete" \
"Package upgrade failed. The system is partially\n\
upgraded to ${target}. Do NOT reboot yet.\n\n\
Repair with:\n sudo apt --fix-broken install\n sudo dpkg --configure -a\n\n\
To roll back the branch manually, restore:\n sudo tar xzf $_MIGRATE_BACKUP -C /" 14 70
return 1
fi
if ! _run_cmd "Full-Upgrade" "sudo apt full-upgrade -y" "Running full-upgrade..."; then
_msg_red "Migration Incomplete" "full-upgrade failed. Repair with:\n sudo apt --fix-broken install\nBackup: $_MIGRATE_BACKUP" 10 70
return 1
fi
_run_cmd "Autoremove" "sudo apt autoremove -y" "Removing obsolete packages..." || true
_run_cmd "Upgrade" "sudo apt upgrade -y" "Upgrading packages..."
_run_cmd "Full-Upgrade" "sudo apt full-upgrade -y" "Running full-upgrade..."
_run_cmd "Autoremove" "sudo apt autoremove -y" "Removing obsolete packages..."
# 4h. Re-run detection to reflect new branch
echo -e "${YELLOW}Re-running system detection for new branch...${NC}"
@@ -261,7 +169,7 @@ To roll back the branch manually, restore:\n sudo tar xzf $_MIGRATE_BACKUP -C /
# ── Screen 5: Reboot reminder ──
_msg "Migration Complete" \
"System has been migrated to ${target}.\n\n\
"System has been migrated to ${target}.\n\n\
Backup saved at:\n $_MIGRATE_BACKUP\n\n\
REBOOT your system.\nIf it fails to boot, restore the backup manually:\n\
sudo tar xzf $_MIGRATE_BACKUP -C /\n sudo apt update\n sudo apt upgrade" 16 70
+21 -76
View File
@@ -2,80 +2,34 @@
# repo_detect.sh – Detection-only helpers for idempotent repository configuration.
# Part A of the two-part architecture. No user dialogs, no writes.
# Any active (non-commented) source of any format, in any file?
# Returns: 0 if at least one active source exists, 1 otherwise
has_active_deb_sources() {
local apt_dir="${APT_DIR:-/etc/apt}"
local f
for f in "$apt_dir/sources.list" "$apt_dir/sources.list.d/"*.list; do
[ -f "$f" ] || continue
grep -qE '^[^#]*\bdeb\b' "$f" 2>/dev/null && return 0
done
for f in "$apt_dir/sources.list.d/"*.sources; do
[ -f "$f" ] || continue
grep -qE '^Types:.*\bdeb\b' "$f" 2>/dev/null && \
grep -qE '^URIs:' "$f" 2>/dev/null && return 0
done
return 1
}
# Detect the format of the main repo file (content-aware).
# DEB822 is only valid on Debian 13 (Trixie); Debian 11/12 are classic-only.
# Detect the format of the main repo file
# Returns: "deb822", "classic", or "none"
detect_repo_format() {
local apt_dir="${APT_DIR:-/etc/apt}"
if [ "$DEBIAN_VERSION" = "13" ] && [ -f "$apt_dir/sources.list.d/debian.sources" ] && \
grep -qE '^Types:.*\bdeb\b' "$apt_dir/sources.list.d/debian.sources" 2>/dev/null; then
if [ -f /etc/apt/sources.list.d/debian.sources ]; then
echo "deb822"
elif [ -f "$apt_dir/sources.list" ] && grep -qE '^[^#]*\bdeb\b' "$apt_dir/sources.list" 2>/dev/null; then
elif [ -f /etc/apt/sources.list ]; then
echo "classic"
else
echo "none"
fi
}
# Detect the components currently active in the main repo file
# Returns: components list (e.g. "main contrib non-free non-free-firmware")
detect_active_components() {
local apt_dir="${APT_DIR:-/etc/apt}"
if [ "$DEBIAN_VERSION" = "13" ] && [ -f "$apt_dir/sources.list.d/debian.sources" ]; then
local comps
comps=$(grep "^Components:" "$apt_dir/sources.list.d/debian.sources" 2>/dev/null | head -1 | cut -d: -f2- | sed 's/^[[:space:]]*//')
if [ -n "$comps" ]; then
echo "$comps"
return
fi
fi
if [ -f "$apt_dir/sources.list" ]; then
local comps
comps=$(grep "^[^#]*deb .* main" "$apt_dir/sources.list" 2>/dev/null | head -1 | sed 's/.*main\s*//')
if [ -n "$comps" ]; then
echo "main $comps"
return
fi
fi
if [ "$DEBIAN_VERSION" = "11" ]; then
echo "main contrib non-free"
else
echo "main contrib non-free non-free-firmware"
fi
}
# Check whether backports are currently enabled (any format, any file)
# Check whether backports are currently enabled (any format)
# Returns: 0 if enabled, 1 otherwise
detect_backports_status() {
local apt_dir="${APT_DIR:-/etc/apt}"
local codename="$1"
if [ "$DEBIAN_VERSION" = "13" ] && [ -f "$apt_dir/sources.list.d/debian.sources" ]; then
grep -qE "^Suites:.*${codename}-backports" "$apt_dir/sources.list.d/debian.sources" 2>/dev/null && return 0
if [ -f /etc/apt/sources.list.d/debian.sources ]; then
grep -qE "^Suites:.*${codename}-backports" /etc/apt/sources.list.d/debian.sources 2>/dev/null && return 0
fi
if [ -f "$apt_dir/sources.list" ]; then
grep -qE "^[^#]*${codename}-backports" "$apt_dir/sources.list" 2>/dev/null && return 0
if [ -f /etc/apt/sources.list ]; then
grep -qE "^[^#]*${codename}-backports" /etc/apt/sources.list 2>/dev/null && return 0
fi
if [ -d "$apt_dir/sources.list.d" ]; then
grep -qrE "^Suites:.*${codename}-backports" "$apt_dir/sources.list.d/"*.sources 2>/dev/null && return 0
grep -qrE "^[^#]*${codename}-backports" "$apt_dir/sources.list.d/"*.list 2>/dev/null && return 0
if [ -f /etc/apt/sources.list.d/debian-backports.sources ]; then
grep -qE "^Suites:.*${codename}-backports" /etc/apt/sources.list.d/debian-backports.sources 2>/dev/null && return 0
fi
if [ -f /etc/apt/sources.list.d/debian-backports.list ]; then
grep -qE "^[^#]*${codename}-backports" /etc/apt/sources.list.d/debian-backports.list 2>/dev/null && return 0
fi
return 1
@@ -88,29 +42,20 @@ detect_backports_status() {
# "embedded-classic" (inside sources.list),
# "none"
detect_backports_location() {
local apt_dir="${APT_DIR:-/etc/apt}"
local codename="$1"
if [ "$DEBIAN_VERSION" = "13" ] && [ -f "$apt_dir/sources.list.d/debian-backports.sources" ] && \
grep -qE "^Suites:.*${codename}-backports" "$apt_dir/sources.list.d/debian-backports.sources" 2>/dev/null; then
if [ -f /etc/apt/sources.list.d/debian-backports.sources ] && \
grep -qE "^Suites:.*${codename}-backports" /etc/apt/sources.list.d/debian-backports.sources 2>/dev/null; then
echo "standalone-deb822"
elif [ -f "$apt_dir/sources.list.d/debian-backports.list" ] && \
grep -qE "^[^#]*${codename}-backports" "$apt_dir/sources.list.d/debian-backports.list" 2>/dev/null; then
elif [ -f /etc/apt/sources.list.d/debian-backports.list ] && \
grep -qE "^[^#]*${codename}-backports" /etc/apt/sources.list.d/debian-backports.list 2>/dev/null; then
echo "standalone-classic"
elif [ "$DEBIAN_VERSION" = "13" ] && [ -f "$apt_dir/sources.list.d/debian.sources" ] && \
grep -qE "^Suites:.*${codename}-backports" "$apt_dir/sources.list.d/debian.sources" 2>/dev/null; then
elif [ -f /etc/apt/sources.list.d/debian.sources ] && \
grep -qE "^Suites:.*${codename}-backports" /etc/apt/sources.list.d/debian.sources 2>/dev/null; then
echo "embedded-deb822"
elif [ -f "$apt_dir/sources.list" ] && \
grep -qE "^[^#]*${codename}-backports" "$apt_dir/sources.list" 2>/dev/null; then
elif [ -f /etc/apt/sources.list ] && \
grep -qE "^[^#]*${codename}-backports" /etc/apt/sources.list 2>/dev/null; then
echo "embedded-classic"
elif [ -d "$apt_dir/sources.list.d" ]; then
if grep -qrE "^Suites:.*${codename}-backports" "$apt_dir/sources.list.d/"*.sources 2>/dev/null; then
echo "embedded-deb822"
elif grep -qrE "^[^#]*${codename}-backports" "$apt_dir/sources.list.d/"*.list 2>/dev/null; then
echo "embedded-classic"
else
echo "none"
fi
else
echo "none"
fi
+1 -2
View File
@@ -71,8 +71,7 @@ _set_grub_var() {
_apply_grub_setting() {
local timeout="$1" style="$2" recordfail="$3" os_prober="$4"
local file="/etc/default/grub"
local backup
backup="${file}.backup.$(date +%Y%m%d_%H%M%S)"
local backup="${file}.backup.$(date +%Y%m%d_%H%M%S)"
local override="/etc/default/grub.d/99_script_override.cfg"
sudo cp "$file" "$backup"
+30 -47
View File
@@ -2,10 +2,6 @@
# sudo_config.sh — User Privileges & Feedback submenu
# License GPL v3
# Resolve the invoking user once. USER may be unset in minimal
# environments (SSH sessions, cron), which would abort under set -u.
TARGET_USER="${SUDO_USER:-${USER:-$(id -un)}}"
config_sudo() {
echo -e "${YELLOW}User Privileges & Feedback${NC}"
@@ -23,23 +19,23 @@ config_sudo() {
clear
case "$choice" in
1) _check_sudo_group ;;
2) _configure_nopasswd ;;
3) _repair_home_ownership ;;
4) _toggle_pwfeedback ;;
5) return ;;
1) _check_sudo_group ;;
2) _configure_nopasswd ;;
3) _repair_home_ownership ;;
4) _toggle_pwfeedback ;;
5) return ;;
esac
done
}
# ── Option 1: Sudo Group Membership ──
_check_sudo_group() {
if groups "$TARGET_USER" | grep -qE '\bsudo\b'; then
_msg "Sudo Group" "User '$TARGET_USER' is already in the sudo group."
if groups "$USER" | grep -qE '\bsudo\b'; then
_msg "Sudo Group" "User '$USER' is already in the sudo group."
else
if _confirm "Sudo Group" \
"User '$TARGET_USER' is NOT in the sudo group.\n\nAdd to sudo group?"; then
if sudo usermod -aG sudo "$TARGET_USER"; then
"User '$USER' is NOT in the sudo group.\n\nAdd to sudo group?"; then
if sudo usermod -aG sudo "$USER"; then
_msg "Sudo Group" \
"User added to sudo group.\n\nLog out and back in for\ngroup changes to take effect." 10 60
else
@@ -52,10 +48,7 @@ _check_sudo_group() {
# ── Option 2: Passwordless Sudo (NOPASSWD) ──
_configure_nopasswd() {
# sudo silently ignores /etc/sudoers.d/ files whose name contains
# '.' or '~' (package manager / editor backup guards).
local safe_user="${TARGET_USER//./_}"
local nopasswd_file="/etc/sudoers.d/${safe_user}-nopasswd"
local nopasswd_file="/etc/sudoers.d/${USER}-nopasswd"
if [ -f "$nopasswd_file" ]; then
if _confirm "NOPASSWD" \
@@ -75,40 +68,30 @@ Useful for automation but reduces security." 14 70; then
local choices
choices=$(_checklist "NOPASSWD Commands" \
"Select commands to allow without password:" 12 60 3 \
"apt" "APT package management" ON \
"apt" "APT package management" ON \
"systemctl" "Systemd service management" ON \
"power" "Shutdown, reboot, halt" ON)
"power" "Shutdown, reboot, halt" ON)
clear
[ -z "$choices" ] && {
echo "No commands selected."
return
}
local cleaned
cleaned=$(echo "$choices" | tr -d '"')
[ -z "$choices" ] && { echo "No commands selected."; return; }
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
local content=""
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
for cmd in "${_pkgs[@]}"; do
for cmd in $cleaned; do
case $cmd in
apt)
content+="${TARGET_USER} ALL=(root) NOPASSWD: /usr/bin/apt, /usr/bin/apt-get, /bin/apt, /bin/apt-get\n"
;;
systemctl)
content+="${TARGET_USER} ALL=(root) NOPASSWD: /usr/bin/systemctl, /bin/systemctl\n"
;;
power)
content+="${TARGET_USER} ALL=(root) NOPASSWD: /usr/sbin/shutdown, /sbin/shutdown, /usr/sbin/reboot, /sbin/reboot, /usr/sbin/halt, /sbin/halt\n"
;;
apt)
content+="${USER} ALL=(root) NOPASSWD: /usr/bin/apt, /usr/bin/apt-get, /bin/apt, /bin/apt-get\n"
;;
systemctl)
content+="${USER} ALL=(root) NOPASSWD: /usr/bin/systemctl, /bin/systemctl\n"
;;
power)
content+="${USER} ALL=(root) NOPASSWD: /usr/sbin/shutdown, /sbin/shutdown, /usr/sbin/reboot, /sbin/reboot, /usr/sbin/halt, /sbin/halt\n"
;;
esac
done
local content_str
content_str=$(echo -e "$content")
local content_str; content_str=$(echo -e "$content")
if _validate_sudoers "$content_str" "$nopasswd_file"; then
echo -e "${GREEN}Passwordless sudo configured for selected commands.${NC}"
else
@@ -120,7 +103,7 @@ Useful for automation but reduces security." 14 70; then
# ── Option 3: Repair Home Directory Ownership ──
_repair_home_ownership() {
local home
home=$(getent passwd "$TARGET_USER" | cut -d: -f6)
home=$(eval echo "~$USER")
if [ ! -d "$home" ]; then
_msg "Home Directory" "Home directory '$home' does not exist." 8 60
@@ -128,15 +111,15 @@ _repair_home_ownership() {
fi
local uid uid_owner
uid=$(id -u "$TARGET_USER" 2>/dev/null)
uid=$(id -u "$USER" 2>/dev/null)
uid_owner=$(stat -c '%u' "$home" 2>/dev/null || echo "0")
if [ "$uid_owner" != "$uid" ]; then
local expected_user
expected_user=$(id -nu "$uid_owner" 2>/dev/null || echo "UID $uid_owner")
if _confirm "Home Permissions" \
"Home directory '$home' is owned by\n'$expected_user' (expected: '$TARGET_USER').\n\nRepair ownership?" 12 65; then
if sudo chown -R "$TARGET_USER:$TARGET_USER" "$home"; then
"Home directory '$home' is owned by\n'$expected_user' (expected: '$USER').\n\nRepair ownership?" 12 65; then
if sudo chown -R "$USER:$USER" "$home"; then
echo -e "${GREEN}Home directory ownership repaired.${NC}"
else
echo -e "${RED}Failed to repair home directory ownership.${NC}"
@@ -144,7 +127,7 @@ _repair_home_ownership() {
fi
fi
else
_msg "Home Permissions" "Home directory ownership is correct\n(owner: $TARGET_USER)." 8 60
_msg "Home Permissions" "Home directory ownership is correct\n(owner: $USER)." 8 60
fi
}
+1 -6
View File
@@ -117,8 +117,7 @@ _swap_create_file() {
_swap_remove_file() {
local has_tag
has_tag=$(grep -c "$SWAP_FSTAB_TAG" /etc/fstab 2>/dev/null || true)
has_tag=${has_tag:-0}
has_tag=$(grep -c "$SWAP_FSTAB_TAG" /etc/fstab 2>/dev/null || echo 0)
[ "$has_tag" -eq 0 ] && [ ! -f "$SWAP_FILE" ] && {
_msg "Swap" "No managed swapfile found." 8 50
return
@@ -154,10 +153,6 @@ _swap_set_swappiness() {
# ── Entry point ──
manage_swap() {
# Hardware state is detected on demand; RAM_GB
# drives the size recommendation.
_ensure_state_detected
exec 9>"$SWAP_LOCK"
flock -n 9 || {
_msg "Busy" "Another swap operation is already running." 8 55
+169 -53
View File
@@ -1,10 +1,6 @@
#!/usr/bin/env bash
_show_sysinfo() {
# Hardware state is detected on demand; populate it
# before reading any DETECT_* global.
_ensure_state_detected
local msg=""
# ── OS Block ──
@@ -19,67 +15,188 @@ _show_sysinfo() {
msg+="Storage: ${STORAGE_SUMMARY}\n"
msg+="\n"
# ── GPU Block (cached by detect_gpu) ──
if [ -n "${GPU_DESC:-}" ] && [ "$GPU_DESC" != "No GPU detected" ]; then
msg+="GPU: ${GPU_DESC}\n"
msg+=" Driver: ${GPU_VERSION:-unknown}\n"
else
# ── GPU Block ──
local found_gpu=false
if command -v lspci &>/dev/null; then
local gpu_count=0
while IFS= read -r gpu_line; do
found_gpu=true
gpu_count=$((gpu_count + 1))
local desc
desc=$(echo "$gpu_line" | sed -E 's/.*: //; s/ *\(rev.*//')
if echo "$gpu_line" | grep -qi "nvidia"; then
local nv_ver=""
nv_ver=$(timeout 3 nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -1)
[ -z "$nv_ver" ] && nv_ver=$(dpkg -l nvidia-driver 2>/dev/null | awk '/^ii/ {print $3}' | sed 's/-.*//')
msg+="GPU ${gpu_count}: ${desc}\n"
msg+=" Driver: ${nv_ver:+NVIDIA }${nv_ver:-not installed}\n"
else
local mesa_ver=""
mesa_ver=$(dpkg -l libgl1-mesa-dri 2>/dev/null | awk '/^ii/ {print $3; exit}' | sed 's/-.*//')
msg+="GPU ${gpu_count}: ${desc}\n"
msg+=" Driver: ${mesa_ver:+Mesa }${mesa_ver:-unknown}\n"
fi
done < <(timeout 2 lspci -nn | grep -E "VGA|3D" || true)
fi
if ! $found_gpu; then
msg+="GPU: No GPU detected\n"
fi
msg+="\n"
# ── Network Block (cached by detect_network) ──
# ── Network Block (PCI class + live interfaces) ──
msg+="─── Network ───\n"
# ── 1. Detect ALL Ethernet chipsets (class 0x0200) ──
declare -a pci_eth_lines=()
while IFS= read -r line; do
pci_eth_lines+=("$line")
done < <(timeout 2 lspci -d ::0200 2>/dev/null || true)
# ── 2. Detect ALL WiFi chipsets (class 0x0280 + vendor fallbacks) ──
declare -a pci_wifi_lines=()
while IFS= read -r line; do
pci_wifi_lines+=("$line")
done < <(timeout 2 lspci -d ::0280 2>/dev/null || true)
# Broadcom vendor-ID fallback (14e4) — include only if not already captured
while IFS= read -r line; do
if echo "$line" | grep -qi '14e4:'; then
local already=false
for existing in "${pci_wifi_lines[@]}"; do
[ "$existing" = "$line" ] && already=true && break
done
! $already && pci_wifi_lines+=("$line")
fi
done < <(timeout 2 lspci -nn 2>/dev/null || true)
# USB WiFi fallback
local usb_wifi_lines=()
while IFS= read -r line; do
usb_wifi_lines+=("$line")
done < <(lsusb 2>/dev/null | grep -iE 'wireless|wifi|wlan|802\.11' || true)
# ── 3. Build chipset description arrays ──
local -a eth_descs=()
local -a wifi_descs=()
local line desc
for line in "${pci_eth_lines[@]}"; do
desc=$(echo "$line" | sed -E 's/^.*\]: //; s/ \[[0-9a-fA-F]{4}:[0-9a-fA-F]{4}\]//; s/ \(rev [0-9a-fA-F]+\)//')
eth_descs+=("$desc")
done
for line in "${pci_wifi_lines[@]}"; do
desc=$(echo "$line" | sed -E 's/^.*\]: //; s/ \[[0-9a-fA-F]{4}:[0-9a-fA-F]{4}\]//; s/ \(rev [0-9a-fA-F]+\)//')
wifi_descs+=("$desc")
done
for line in "${usb_wifi_lines[@]}"; do
desc=$(echo "$line" | sed 's/^.*ID //')
wifi_descs+=("$desc")
done
# ── 4. Enumerate live interfaces ──
local -a shown_eth_descs=()
local -a shown_wifi_descs=()
local has_eth=false has_wifi=false has_other=false
if ! command -v ip &>/dev/null; then
msg+="(install iproute2 for interface details)\n"
else
local i desc
local has_net=false
while IFS= read -r line; do
local iface state ip4 ssid
iface=$(echo "$line" | awk -F': ' '{print $2}' | sed 's/@.*//')
state=$(echo "$line" | awk '{print $9}')
# Ethernet interfaces (chipset from ETH_DESCS, parallel arrays)
for i in "${!ETH_NAMES[@]}"; do
has_net=true
desc="${ETH_DESCS[$i]:-Unknown Ethernet chipset}"
if [ "${ETH_STATES[$i]}" = "UP" ]; then
msg+="${ETH_NAMES[$i]}: ${desc}\n ↑ ${ETH_IPS[$i]:-no IP}\n"
else
msg+="${ETH_NAMES[$i]}: ${desc}\n ↓\n"
fi
done
case "$iface" in
lo|docker*|veth*|br-*|virbr*|tun*|tap*|bond*) continue ;;
esac
# WiFi interfaces (chipset from WIFI_DESCS, parallel arrays)
for i in "${!WIFI_NAMES[@]}"; do
has_net=true
desc="${WIFI_DESCS[$i]:-Unknown WiFi chipset}"
if [ "${WIFI_STATES[$i]}" = "UP" ]; then
msg+="${WIFI_NAMES[$i]}: ${desc}\n ↑ ${WIFI_IPS[$i]:-no IP}"
[ -n "${WIFI_SSIDS[$i]:-}" ] && msg+=" \"${WIFI_SSIDS[$i]}\""
ip4=$(timeout 2 ip -4 -o addr show "$iface" 2>/dev/null | awk '{print $4}')
# Determine type by PCI class (most reliable)
local pci_class=""
pci_class=$(cat "/sys/class/net/${iface}/device/class" 2>/dev/null || true)
case "$pci_class" in
0x0200*)
has_eth=true
desc="${eth_descs[0]:-Unknown Ethernet chipset}"
shown_eth_descs+=("${eth_descs[0]}")
;;
0x0280*)
has_wifi=true
desc="${wifi_descs[0]:-Unknown WiFi chipset}"
shown_wifi_descs+=("${wifi_descs[0]}")
ssid=""
[ "$state" = "UP" ] && ssid=$(timeout 2 iwgetid -r "$iface" 2>/dev/null || true)
;;
*)
# Fallback: classify by interface name pattern
case "$iface" in
wl*|wlp*|wlo*|wlan*)
has_wifi=true
desc="${wifi_descs[0]:-Unknown WiFi chipset}"
shown_wifi_descs+=("${wifi_descs[0]}")
ssid=""
[ "$state" = "UP" ] && ssid=$(timeout 2 iwgetid -r "$iface" 2>/dev/null || true)
;;
eth*|enp*|ens*|enx*|eno*)
has_eth=true
desc="${eth_descs[0]:-Unknown Ethernet chipset}"
shown_eth_descs+=("${eth_descs[0]}")
;;
*)
has_other=true
desc="(network interface)"
;;
esac
;;
esac
if [ "$state" = "UP" ]; then
msg+="${iface}: ${desc}\n ↑ ${ip4:-no IP}"
if [ "$pci_class" = "0x0280" ] || [[ "$iface" == wl* ]]; then
[ -n "${ssid:-}" ] && msg+=" \"${ssid}\""
fi
msg+="\n"
else
msg+="${WIFI_NAMES[$i]}: ${desc}\n ↓\n"
msg+="${iface}: ${desc}\n ↓\n"
fi
done
done < <(timeout 2 ip -o link show 2>/dev/null)
fi
# Chipsets with no live interface
local chip shown
for chip in "${ETH_DESCS[@]}"; do
shown=false
for i in "${!ETH_NAMES[@]}"; do
[ "${ETH_DESCS[$i]}" = "$chip" ] && shown=true && break
done
$shown || msg+="Ethernet: ${chip}\n (no active interface)\n"
done
for chip in "${WIFI_DESCS[@]}"; do
shown=false
for i in "${!WIFI_NAMES[@]}"; do
[ "${WIFI_DESCS[$i]}" = "$chip" ] && shown=true && break
done
$shown || msg+="WiFi: ${chip}\n (no active interface)\n"
done
# ── 5. Show chipsets without an active interface ──
local chip
if ! $has_net && [ ${#ETH_DESCS[@]} -eq 0 ] && [ ${#WIFI_DESCS[@]} -eq 0 ]; then
for chip in "${eth_descs[@]}"; do
local found=false
for shown in "${shown_eth_descs[@]}"; do
[ "$chip" = "$shown" ] && found=true && break
done
if ! $found; then
msg+="Ethernet: ${chip}\n (no active interface)\n"
shown_eth_descs+=("$chip")
fi
done
for chip in "${wifi_descs[@]}"; do
local found=false
for shown in "${shown_wifi_descs[@]}"; do
[ "$chip" = "$shown" ] && found=true && break
done
if ! $found; then
msg+="WiFi: ${chip}\n (no active interface)\n"
shown_wifi_descs+=("$chip")
fi
done
# ── 6. Nothing at all ──
if ! $has_eth && ! $has_wifi && ! $has_other && [ ${#eth_descs[@]} -eq 0 ] && [ ${#wifi_descs[@]} -eq 0 ]; then
if command -v ip &>/dev/null; then
msg+="No network interfaces detected\n"
else
msg+="(install iproute2 for interface details)\n"
fi
fi
@@ -91,7 +208,7 @@ _show_sysinfo() {
term_cols=$(tput cols 2>/dev/null || echo 80)
[ "$term_cols" -lt 50 ] && term_cols=50
local max_pct=$((term_cols * 95 / 100))
local max_pct=$(( term_cols * 95 / 100 ))
[ "$max_pct" -lt 50 ] && max_pct=50
local longest=0
@@ -100,7 +217,7 @@ _show_sysinfo() {
[ "$len" -gt "$longest" ] && longest=$len
done < <(echo -e "$msg")
local width=$((longest + 6))
local width=$(( longest + 6 ))
[ "$width" -lt 80 ] && width=80
[ "$width" -gt "$max_pct" ] && width=$max_pct
@@ -115,9 +232,8 @@ _show_sysinfo() {
local lines
lines=$(echo -e "$truncated" | wc -l)
local height=$((lines + 6))
local max_height=$((${LINES:-24} - 4 > 10 ? ${LINES:-24} - 4 : 10))
[ "$height" -gt "$max_height" ] && height=$max_height
local height=$(( lines + 6 ))
[ "$height" -gt 22 ] && height=22
[ "$height" -lt 10 ] && height=10
_msg "System Information" "$truncated" "$height" "$width"
-200
View File
@@ -1,200 +0,0 @@
#!/usr/bin/env bash
# audio.sh — Audio & Sound (PipeWire, ALSA, PulseAudio tools)
_prefs_audio_menu() {
local headless=false
_is_headless && headless=true
local -a items=()
local pw_label="PipeWire Audio Stack (Bluetooth Hi-Res)"
# 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"
)
local alsa_state; alsa_state=$(_state "alsa-utils")
items+=(
"alsa-utils" "ALSA Utilities (alsa-utils, alsa-ucm-conf)" "$alsa_state"
)
if ! $headless; then
local pavu_state; pavu_state=$(_state "pavucontrol")
items+=(
"pavucontrol" "Volume Control GUI (pavucontrol)" "$pavu_state"
)
fi
local pulsemixer_state; pulsemixer_state=$(_state "pulsemixer")
local playerctl_state; playerctl_state=$(_state "playerctl")
items+=(
"pulsemixer" "Terminal audio mixer (pulsemixer)" "$pulsemixer_state"
"playerctl" "Multimedia key controller (playerctl)" "$playerctl_state"
)
local item_count=${#items[@]}
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
local choices
choices=$(_checklist "Audio & Sound" "Check [*] the packages you want installed.\n" \
$TUI_ALTO $TUI_ANCHO $lista_alto "${items[@]}")
clear
[ -z "$choices" ] && return
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
# BH-004: Convert to array to avoid word splitting and injection.
local -a _pkgs=()
while IFS= read -r _pkg; do
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
done < <(echo "$cleaned" | tr ' ' '\n')
# Pre-apply summary: confirm the selection once,
# before any apt transaction starts.
_confirm_install_list "Audio & Sound" "${_pkgs[@]}" || return
for pkg in "${_pkgs[@]}"; do
case $pkg in
pipewire-audio) _install_pipewire_standard ;;
alsa-utils)
if ! is_installed "alsa-utils"; then
_run_install "alsa-utils alsa-ucm-conf"
else
echo "alsa-utils already installed."
fi
;;
pavucontrol)
if ! is_installed "pavucontrol"; then
_run_install "pavucontrol"
else
echo "pavucontrol already installed."
fi
;;
pulsemixer)
if ! is_installed "pulsemixer"; then
_run_install "pulsemixer"
else
echo "pulsemixer already installed."
fi
;;
playerctl)
if ! is_installed "playerctl"; then
_run_install "playerctl"
else
echo "playerctl already installed."
fi
;;
esac
done
_SELECTION_CONFIRMED=0
echo -e "${GREEN}Audio & Sound setup complete.${NC}"
}
_install_pipewire_standard() {
local bt_pkgs="libldacbt-abr2 libldacbt-enc2 libopenaptx0 libspa-0.2-bluetooth"
if apt-cache show libfdk-aac2t64 &>/dev/null; then
bt_pkgs+=" libfdk-aac2t64"
elif apt-cache show libfdk-aac2 &>/dev/null; then
bt_pkgs+=" libfdk-aac2"
fi
# ── Resolve versions (needed for both installed and not-installed branches) ──
local stable_ver=""
stable_ver=$(apt-cache policy pipewire-audio 2>/dev/null | awk 'NR==3 {print $2; exit}') || true
local bpo_ver=""
if [ "$DEBIAN_VERSION" = "13" ] && [ "$(is_backports_enabled)" = "true" ]; then
bpo_ver=$(apt-cache madison pipewire-audio 2>/dev/null | \
grep "${DEBIAN_CODENAME}-backports" | awk '{print $3}' | head -1) || true
fi
local pkg_check="pipewire-audio"
[ "$DEBIAN_VERSION" = "11" ] && pkg_check="pipewire"
# ── Already installed branch ──
if is_installed "$pkg_check"; then
if [ -n "$bpo_ver" ]; then
local current_ver
current_ver=$(dpkg -l "$pkg_check" 2>/dev/null | awk '/^ii/{print $3}') || true
if _confirm "PipeWire" \
"PipeWire ${current_ver:+v${current_ver} }already installed.\n\nUpgrade to backports version v${bpo_ver}?"; then
echo "Upgrading PipeWire Audio Stack..."
echo "-> Target version: v${bpo_ver} (from ${DEBIAN_CODENAME}-backports)"
echo "-> Including Bluetooth Hi-Res codecs (LDAC, aptX, AAC)"
_run_cmd "PipeWire" \
"sudo apt install -y --reinstall -t ${DEBIAN_CODENAME}-backports pipewire-audio $bt_pkgs" \
"Upgrading PipeWire from backports..."
if [ "$DEBIAN_VERSION" != "11" ]; then
echo "Restarting PipeWire services..."
systemctl --user restart wireplumber pipewire pipewire-pulse 2>/dev/null || true
echo -e "${GREEN}PipeWire services restarted.${NC}"
fi
return
fi
fi
echo "$pkg_check already installed."
return
fi
# ── Confirm dialog ──
local apt_target="" pw_ver="" pw_src=""
if [ -n "$bpo_ver" ]; then
local msg="Backports repository is enabled.\n\n"
msg+="Available versions:\n"
msg+=" - Stable: v${stable_ver:-unknown}\n"
msg+=" - Backports: v${bpo_ver}\n\n"
msg+="Install from Backports (Recommended for newer hardware)\nor from Stable (more conservative)?"
if _confirm_custom "PipeWire Install" "$msg" "Backports" "Stable" 14 70; then
apt_target="-t ${DEBIAN_CODENAME}-backports"
pw_ver="$bpo_ver"
pw_src="from ${DEBIAN_CODENAME}-backports"
else
pw_ver="$stable_ver"
fi
else
local msg="PipeWire Audio Stack (v${stable_ver:-unknown})\n"
msg+="will be installed along with Bluetooth Hi-Res\n"
msg+="codecs (LDAC, aptX, AAC).\n\n"
msg+="Continue with installation?"
if ! _confirm "PipeWire Install" "$msg" 12 65; then
echo "PipeWire installation cancelled."
return
fi
pw_ver="$stable_ver"
fi
echo "Installing PipeWire Audio Stack..."
echo "-> Detected version: ${pw_ver:-unknown} ${pw_src:+($pw_src)}"
echo "-> Including Bluetooth Hi-Res codecs (LDAC, aptX, AAC)"
case "$DEBIAN_VERSION" in
11)
_run_cmd "PipeWire" \
"sudo apt install -y pipewire libspa-0.2-bluetooth pipewire-alsa libspa-0.2-jack $bt_pkgs" \
"Installing PipeWire (Bullseye basic mode)..."
echo -e "${GREEN}PipeWire installed (Bullseye basic mode).${NC}"
;;
12)
_run_cmd "PipeWire" \
"sudo apt install -y pipewire-audio $bt_pkgs" \
"Installing pipewire-audio meta-package (Bookworm)..."
echo -e "${GREEN}PipeWire audio stack installed.${NC}"
;;
13)
_run_cmd "PipeWire" \
"sudo apt install -y $apt_target pipewire-audio $bt_pkgs" \
"Installing pipewire-audio meta-package (Trixie)..."
echo -e "${GREEN}PipeWire audio stack installed.${NC}"
;;
esac
if [ "$DEBIAN_VERSION" != "11" ]; then
echo "Restarting PipeWire services..."
systemctl --user restart wireplumber pipewire pipewire-pulse 2>/dev/null || true
echo -e "${GREEN}PipeWire services restarted.${NC}"
fi
}
-41
View File
@@ -1,41 +0,0 @@
#!/usr/bin/env bash
# system_prefs.sh — System Preferences: time, locale, keyboard, audio
_prefs_date_time() {
echo -e "${YELLOW}Current system date/time:${NC} $(date '+%Y-%m-%d %H:%M')"
echo -e "${YELLOW}Timezone:${NC} $(timedatectl show -p Timezone --value 2>/dev/null || echo unknown)"
if _confirm "Timezone" "Change the system timezone (dpkg-reconfigure tzdata)?"; then
sudo env LC_ALL=C LANGUAGE=C dpkg-reconfigure tzdata || true
echo -e "${GREEN}Timezone: $(timedatectl show -p Timezone --value 2>/dev/null)${NC}"
fi
_ensure_time_synced
}
_prefs_locale_keyboard() {
sudo env LC_ALL=C LANGUAGE=C dpkg-reconfigure locales || true
sudo env LC_ALL=C LANGUAGE=C dpkg-reconfigure keyboard-configuration || true
echo -e "${GREEN}Language, locales and keyboard configured.${NC}"
}
_prefs_audio() { _prefs_audio_menu; }
_system_preferences_menu() {
while true; do
local choice
choice=$(_menu "System Preferences" "Select a preference group:" \
$TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
"1" "Date, Time & Timezone" \
"2" "Language, Locales & Keyboard" \
"3" "Audio & Sound Stack" \
"4" "Back to main menu")
[ -z "$choice" ] && return
clear
case "$choice" in
1) _prefs_date_time ;;
2) _prefs_locale_keyboard ;;
3) _prefs_audio ;;
4) return ;;
esac
done
}
+214 -457
View File
@@ -4,117 +4,23 @@
# ------------------
# Global variables
# ------------------
# Session state shared with every lazily-loaded module:
# exported so shellcheck sees the cross-file usage (SC2034).
export CPU_SUMMARY=""
export RAM_SUMMARY=""
export GPU_TYPE=""
export GPU_DESC=""
export GPU_VERSION=""
export INTEL_GPU_DEVICE_ID=""
export NVIDIA_GPU_DEVICE_ID=""
export HAS_NVIDIA=false
export HAS_AMD=false
export HAS_INTEL=false
export HAS_AMD_LEGACY_GCN=false
export KERNEL_VERSION=""
export DISPLAY_SERVER="unknown"
export STORAGE_SUMMARY=""
export WIFI_CHIPSET=""
export DESKTOP_ENV=""
export AUDIO_SERVER=""
# OS identity populated by detect_debian_version(); read by
# every module (SC2034).
export DEBIAN_VERSION=""
# APT update deduplication flag (set to 1 after the first successful apt-get update)
APT_UPDATED=0
# Cached output of `lspci -nn` for the whole session (populated once via _init_lspci_cache)
LSPCI_OUTPUT=""
# Deferred hardware state: the detection battery runs on
# first need (_ensure_state_detected) instead of at boot.
STATE_DETECTED=0
# Network check result cache: 0 = online, 1 = offline,
# empty = not checked yet (checked on first use).
_NET_STATUS=""
# Pre-apply summary support: countdown of installs whose
# per-package prompt was already confirmed by
# _confirm_install_list. 0 = every _install_pkg asks
# individually (the default, safe behavior).
_SELECTION_CONFIRMED=0
# Lazy module registry (presence of a key = loaded).
# Populated by _load_module() so every module is
# sourced at most once per session.
declare -A _LOADED_MODULES=()
# ── Lazy module loading ──────────────────────────
# Sources a module on first use and remembers it in
# _LOADED_MODULES. Call at the top of a main_menu()
# case branch, before calling into the module:
# _load_module <name> || continue
# Cross-module dependencies (functions called at
# runtime that live in another module) are declared
# in the deps case below, so a module never calls
# an unloaded function.
_load_module() {
# Defensive: an empty name would produce a bad
# array subscript under `set -u`.
local name="${1:-}"
[ -n "$name" ] || return 1
# Already loaded in this session?
[ -n "${_LOADED_MODULES[$name]+x}" ] && return 0
# Module file path. Most modules live directly in
# modules/; a few have a dedicated subdirectory.
local path
case "$name" in
system_prefs) path="${MODULES_DIR}/system/system_prefs.sh" ;;
audio) path="${MODULES_DIR}/system/audio.sh" ;;
java) path="${MODULES_DIR}/extras/java.sh" ;;
repo_detect) path="${MODULES_DIR}/repos/repo_detect.sh" ;;
gnome) path="${MODULES_DIR}/desktops/desktop_gnome.sh" ;;
kde) path="${MODULES_DIR}/desktops/desktop_kde.sh" ;;
xfce) path="${MODULES_DIR}/desktops/desktop_xfce.sh" ;;
lxde) path="${MODULES_DIR}/desktops/desktop_lxde.sh" ;;
*) path="${MODULES_DIR}/${name}.sh" ;;
esac
# Cross-module dependencies, loaded first.
local -a deps=()
case "$name" in
system_prefs) deps=(audio) ;;
repos) deps=(repo_detect) ;;
firmware) deps=(repo_detect bluetooth repos) ;;
gaming) deps=(repo_detect java repos) ;;
extras) deps=(java) ;;
desktop_display) deps=(repos audio) ;;
gnome) deps=(desktop_display audio) ;;
kde) deps=(desktop_display audio) ;;
xfce) deps=(desktop_display audio) ;;
lxde) deps=(desktop_display audio) ;;
esac
local dep
for dep in "${deps[@]}"; do
_load_module "$dep" || return 1
done
if [ ! -f "$path" ]; then
echo -e "${RED}[-] Module not found: ${path}${NC}" >&2
return 1
fi
# shellcheck disable=SC1090
source "$path"
_LOADED_MODULES["$name"]=1
return 0
}
CPU_SUMMARY=""
RAM_SUMMARY=""
GPU_TYPE=""
GPU_DESC=""
GPU_VERSION=""
INTEL_GPU_DEVICE_ID=""
NVIDIA_GPU_DEVICE_ID=""
HAS_NVIDIA=false
HAS_AMD=false
HAS_INTEL=false
HAS_AMD_LEGACY_GCN=false
KERNEL_VERSION=""
DISPLAY_SERVER="unknown"
STORAGE_SUMMARY=""
WIFI_CHIPSET=""
DESKTOP_ENV=""
AUDIO_SERVER=""
# --------------------------
# Pre-flight checks
@@ -133,42 +39,57 @@ check_sudo() {
fi
}
# -------------------------------------------------------------------
# Robust time sync: NTP + timezone validation + service restart
# -------------------------------------------------------------------
_ensure_time_synced() {
# --------------------------------
# Time sync detection + NTP
# --------------------------------
check_system_time() {
command -v timedatectl &>/dev/null || return
# ── Step 1: Force NTP active ──
sudo timedatectl set-ntp true --no-ask-password 2>/dev/null || true
local year
year=$(date +%Y)
# ── Step 2: Validate timezone ──
local tz
tz=$(timedatectl show -p Timezone --value 2>/dev/null || echo "")
if [ -z "$tz" ] || [ "$tz" = "n/a" ] || [ "$tz" = "Etc/UTC" ]; then
if [ -n "${DISPLAY:-}" ] || [ -n "${SSH_TTY:-}" ]; then
_msg "Timezone" \
"Your system timezone is not set or is set to UTC.\n\nThe script will now open the timezone\nconfiguration tool to set your local timezone." 12 60
sudo env LC_ALL=C LANGUAGE=C dpkg-reconfigure tzdata || true
echo -e "${GREEN}Timezone configured: $(timedatectl show -p Timezone --value 2>/dev/null)${NC}"
if [ "$year" -lt 2025 ]; then
local msg="System date/time appears to be incorrect\n"
msg+="($(date '+%Y-%m-%d %H:%M')). This will prevent Debian\n"
msg+="repositories from working properly.\n\n"
msg+="Attempt automatic NTP synchronization?\n"
msg+="(requires network access and timedatectl)"
if _confirm "System Date" "$msg"; then
sync_system_time
else
echo -e "${YELLOW}Timezone not set. Run 'sudo dpkg-reconfigure tzdata' later.${NC}"
echo -e "${YELLOW}Warning: System time is incorrect. Package installations may fail.${NC}"
fi
return
fi
# ── Step 3: Install/ensure systemd-timesyncd ──
local ntp_active
ntp_active=$(timedatectl show --property=NTP --value 2>/dev/null || echo "no")
if [ "$ntp_active" != "yes" ]; then
sync_system_time
fi
}
sync_system_time() {
command -v timedatectl &>/dev/null || return
if ! is_installed systemd-timesyncd; then
sudo DEBIAN_FRONTEND=noninteractive apt install -y systemd-timesyncd || true
fi
sudo systemctl enable systemd-timesyncd 2>/dev/null || true
sudo systemctl restart systemd-timesyncd 2>/dev/null || true
# ── Step 4: Verify result ──
sleep 2
if ! systemctl is-enabled systemd-timesyncd &>/dev/null; then
sudo systemctl enable systemd-timesyncd || true
fi
if ! systemctl is-active systemd-timesyncd &>/dev/null; then
sudo systemctl start systemd-timesyncd || true
fi
sudo timedatectl set-ntp true || true
sleep 4
if timedatectl show --property=NTPSynchronized --value 2>/dev/null | grep -q yes; then
echo -e "${GREEN}Time synchronized: $(date '+%Y-%m-%d %H:%M')${NC}"
else
echo -e "${YELLOW}NTP sync did not complete yet (may need a moment).${NC}"
echo -e "${YELLOW}NTP sync did not complete.${NC}"
fi
}
@@ -176,12 +97,12 @@ _ensure_time_synced() {
# Debian version detection
# --------------------------------
detect_debian_version() {
if ! command -v lsb_release &>/dev/null; then
if ! command -v lsb_release &> /dev/null; then
if [ -f /etc/os-release ]; then
DEBIAN_CODENAME=$(grep -oP 'VERSION_CODENAME=\K\w+' /etc/os-release 2>/dev/null || echo "")
fi
if [ -z "$DEBIAN_CODENAME" ]; then
_ensure_time_synced || true
sync_system_time || true
sudo apt update -qq 2>/dev/null && sudo apt install -y -qq lsb-release || true
fi
fi
@@ -189,13 +110,13 @@ detect_debian_version() {
DEBIAN_CODENAME=$(lsb_release -cs 2>/dev/null || echo "")
fi
case "$DEBIAN_CODENAME" in
bullseye) DEBIAN_VERSION="11" ;;
bookworm) DEBIAN_VERSION="12" ;;
trixie) DEBIAN_VERSION="13" ;;
*)
echo -e "${RED}Unsupported Debian version: '$DEBIAN_CODENAME'. Only 11 (bullseye), 12 (bookworm) and 13 (trixie) are supported.${NC}"
exit 1
;;
bullseye) DEBIAN_VERSION="11" ;;
bookworm) DEBIAN_VERSION="12" ;;
trixie) DEBIAN_VERSION="13" ;;
*)
echo -e "${RED}Unsupported Debian version: '$DEBIAN_CODENAME'. Only 11 (bullseye), 12 (bookworm) and 13 (trixie) are supported.${NC}"
exit 1
;;
esac
}
@@ -203,15 +124,20 @@ detect_debian_version() {
# CPU and RAM info (cosmetic)
# ----------------------------------
detect_cpu_ram() {
CPU_SUMMARY=$(grep -m1 'model name' /proc/cpuinfo | sed 's/.*: //' || true)
RAM_KB=$(grep MemTotal /proc/meminfo 2>/dev/null | awk '{print $2}') || RAM_KB=0
# BH-001: If /proc/meminfo is unavailable or grep finds no MemTotal,
# RAM_KB would be empty under set -u. Assign 0 as safe default.
[ -z "$RAM_KB" ] && RAM_KB=0
CPU_SUMMARY=$(grep -m1 'model name' /proc/cpuinfo | sed 's/.*: //')
RAM_KB=$(grep MemTotal /proc/meminfo | awk '{print $2}')
RAM_GB=$(awk -v kb="$RAM_KB" 'BEGIN { printf "%.2f", kb / 1048576 }')
RAM_SUMMARY="${RAM_GB} GB"
}
get_cpu_summary() {
echo "$CPU_SUMMARY"
}
get_ram_summary() {
echo "$RAM_SUMMARY"
}
# ----------------------------------
# Check if running backports kernel
# ----------------------------------
@@ -232,37 +158,27 @@ is_installed() {
dpkg -l "$1" 2>/dev/null | grep -q '^ii'
}
# ----------------------------------
# Package version helpers
# ----------------------------------
# Get the stable version of a package from apt-cache policy
# Returns: version string or "" if not found
_get_pkg_version() {
local pkg="$1"
apt-cache policy "$pkg" 2>/dev/null | awk 'NR==3 {print $2; exit}'
}
# Get the installed version of a package from dpkg
# Returns: version string or "" if not installed
_get_installed_version() {
local pkg="$1"
dpkg -l "$pkg" 2>/dev/null | awk '/^ii/{print $3; exit}'
}
# Get the backports version of a package
# Returns: version string or "" if not found
_get_backports_version() {
local pkg="$1"
local codename="${2:-$DEBIAN_CODENAME}"
apt-cache madison "$pkg" 2>/dev/null |
grep "${codename}-backports" | awk '{print $3}' | head -1
}
_state() {
is_installed "$1" && echo "ON" || echo "OFF"
}
# ----------------------------------
# Package version lookup
# ----------------------------------
pkg_versions() {
local result=""
for pkg in "$@"; do
local ver
ver=$(apt-cache policy "$pkg" 2>/dev/null | awk 'NR==3 {print $2; exit}')
if [ -n "$ver" ] && [ "$ver" != "(none)" ]; then
result+=" - ${pkg} ${ver}\n"
else
result+=" - ${pkg}\n"
fi
done
echo -e "$result"
}
# ----------------------------------
# Kernel version
# ----------------------------------
@@ -271,26 +187,11 @@ detect_kernel() {
}
# ----------------------------------
# lspci output cache
# ----------------------------------
# Populates LSPCI_OUTPUT once per session. `lspci -nn` includes the textual PCI
# class (VGA/3D/Ethernet/Network/Bluetooth controller) and the device IDs
# (e.g. 14e4:), so a single capture covers every grep used across modules.
_init_lspci_cache() {
[ -n "${LSPCI_OUTPUT:-}" ] && return
LSPCI_OUTPUT=$(timeout 2 lspci -nn 2>/dev/null || true)
}
_lspci_desc() {
# Strip the "bus domain] device ID" and "(rev XX)" fields,
# keeping the human-readable device name.
echo "$1" | sed -E 's/^.*\]: //; s/ \[[0-9a-fA-F]{4}:[0-9a-fA-F]{4}\]//; s/ \(rev [0-9a-fA-F]+\)//'
}
# GPU detection
# ----------------------------------
detect_gpu() {
local gpu_lines
gpu_lines=$(echo "$LSPCI_OUTPUT" | grep -E "VGA|3D") || true
gpu_lines=$(timeout 2 lspci -nn | grep -E "VGA|3D") || true
if [ -z "$gpu_lines" ]; then
GPU_TYPE="unknown"
GPU_DESC="No GPU detected"
@@ -317,7 +218,7 @@ detect_gpu() {
has_intel=true
[ -z "$intel_dev_id" ] && intel_dev_id=$(echo "$line" | grep -oP '8086:\K[0-9a-fA-F]+' | head -n1)
fi
done <<<"$gpu_lines"
done <<< "$gpu_lines"
GPU_DESC="$desc_lines"
HAS_NVIDIA=$has_nvidia
@@ -342,14 +243,14 @@ detect_gpu() {
local nv_ver
nv_ver=$(timeout 3 nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -1) || true
if [ -z "$nv_ver" ]; then
nv_ver=$(_get_installed_version "nvidia-driver" | sed 's/-.*//') || true
nv_ver=$(dpkg -l nvidia-driver 2>/dev/null | awk '/^ii/ {print $3}' | sed 's/-.*//') || true
fi
[ -n "$nv_ver" ] && GPU_VERSION="NVIDIA $nv_ver"
fi
if [ -z "$GPU_VERSION" ]; then
local mesa_ver
mesa_ver=$(_get_installed_version "libgl1-mesa-dri" | sed 's/-.*//')
mesa_ver=$(dpkg -l libgl1-mesa-dri 2>/dev/null | awk '/^ii/ {print $3; exit}' | sed 's/-.*//')
[ -n "$mesa_ver" ] && GPU_VERSION="Mesa $mesa_ver"
fi
}
@@ -381,40 +282,26 @@ declare -a WIFI_IPS=()
declare -a WIFI_SSIDS=()
detect_network() {
# BH-013: Reset all network arrays at the start.
# Without this, if detect_network() is called more than once (e.g. from
# refresh_system_state()), the arrays ETH_NAMES, WIFI_NAMES, etc. would
# accumulate duplicates instead of being reset, causing incorrect data.
ETH_NAMES=()
ETH_STATES=()
ETH_IPS=()
ETH_DESCS=()
WIFI_NAMES=()
WIFI_STATES=()
WIFI_IPS=()
WIFI_SSIDS=()
WIFI_DESCS=()
local eth_line
eth_line=$(echo "$LSPCI_OUTPUT" | grep -i 'Ethernet controller' | head -n1) || true
eth_line=$(timeout 2 lspci -nn | grep -i 'Ethernet controller' | head -n1) || true
if [ -n "$eth_line" ]; then
ETH_DESC=$(_lspci_desc "$eth_line")
ETH_DESC=$(echo "$eth_line" | sed -E 's/^.*\]: //; s/ \[[0-9a-fA-F]{4}:[0-9a-fA-F]{4}\]//; s/ \(rev [0-9a-fA-F]+\)//')
fi
local wifi_line
# Layer 1: grep by PCI class description text
wifi_line=$(echo "$LSPCI_OUTPUT" | grep -iE 'network controller|wireless|wi-fi|wlan|802\.11' | head -n1) || true
wifi_line=$(timeout 2 lspci -nn 2>/dev/null | grep -iE 'network controller|wireless|wi-fi|wlan|802\.11' | head -n1) || true
# Layer 2: grep by exact PCI class code 0x0280 (Network controller)
if [ -z "$wifi_line" ]; then
wifi_line=$(echo "$LSPCI_OUTPUT" | grep -i 'network controller' | head -n1) || true
wifi_line=$(timeout 2 lspci -d ::0280 2>/dev/null | head -n1) || true
fi
# Layer 3: Broadcom vendor ID fallback (14e4)
if [ -z "$wifi_line" ]; then
wifi_line=$(echo "$LSPCI_OUTPUT" | grep -i '14e4:' | head -n1) || true
wifi_line=$(timeout 2 lspci -nn 2>/dev/null | grep -i '14e4:' | head -n1) || true
fi
if [ -n "$wifi_line" ]; then
WIFI_CHIPSET="$wifi_line"
WIFI_DESC=$(_lspci_desc "$wifi_line")
WIFI_DESC=$(echo "$wifi_line" | sed -E 's/^.*\]: //; s/ \[[0-9a-fA-F]{4}:[0-9a-fA-F]{4}\]//; s/ \(rev [0-9a-fA-F]+\)//')
fi
# Layer 4: USB WiFi adapter (no PCI device)
if [ -z "$wifi_line" ] && command -v lsusb &>/dev/null; then
@@ -437,23 +324,23 @@ detect_network() {
iface=$(echo "$line" | awk -F': ' '{print $2}' | sed 's/@.*//')
state=$(echo "$line" | awk '{print $9}')
case "$iface" in
eth* | enp* | ens* | enx* | eno*)
ip4=$(timeout 1 ip -4 -o addr show "$iface" 2>/dev/null | awk '{print $4}')
ETH_NAMES+=("$iface")
ETH_STATES+=("$state")
ETH_IPS+=("${ip4:-}")
ETH_DESCS+=("${ETH_DESC:-}")
;;
wl*)
ip4=$(timeout 1 ip -4 -o addr show "$iface" 2>/dev/null | awk '{print $4}')
ssid=""
[ "$state" = "UP" ] && ssid=$(timeout 1 iwgetid -r "$iface" 2>/dev/null || true)
WIFI_NAMES+=("$iface")
WIFI_STATES+=("$state")
WIFI_IPS+=("${ip4:-}")
WIFI_SSIDS+=("${ssid:-}")
WIFI_DESCS+=("${WIFI_DESC:-}")
;;
eth*|enp*|ens*|enx*|eno*)
ip4=$(timeout 2 ip -4 -o addr show "$iface" 2>/dev/null | awk '{print $4}')
ETH_NAMES+=("$iface")
ETH_STATES+=("$state")
ETH_IPS+=("${ip4:-}")
ETH_DESCS+=("${ETH_DESC:-}")
;;
wl*|wlp*|wlo*|wlan*)
ip4=$(timeout 2 ip -4 -o addr show "$iface" 2>/dev/null | awk '{print $4}')
ssid=""
[ "$state" = "UP" ] && ssid=$(timeout 2 iwgetid -r "$iface" 2>/dev/null || true)
WIFI_NAMES+=("$iface")
WIFI_STATES+=("$state")
WIFI_IPS+=("${ip4:-}")
WIFI_SSIDS+=("${ssid:-}")
WIFI_DESCS+=("${WIFI_DESC:-}")
;;
esac
done < <(timeout 2 ip -o link show 2>/dev/null)
}
@@ -464,18 +351,18 @@ detect_network() {
detect_displayserver() {
local st="${XDG_SESSION_TYPE:-}"
case "$st" in
wayland) DISPLAY_SERVER="Wayland" ;;
x11) DISPLAY_SERVER="X11" ;;
tty) DISPLAY_SERVER="none (tty)" ;;
*)
if [ -n "${WAYLAND_DISPLAY:-}" ]; then
DISPLAY_SERVER="Wayland"
elif [ -n "${DISPLAY:-}" ]; then
DISPLAY_SERVER="X11"
else
DISPLAY_SERVER="unknown"
fi
;;
wayland) DISPLAY_SERVER="Wayland" ;;
x11) DISPLAY_SERVER="X11" ;;
tty) DISPLAY_SERVER="none (tty)" ;;
*)
if [ -n "${WAYLAND_DISPLAY:-}" ]; then
DISPLAY_SERVER="Wayland"
elif [ -n "${DISPLAY:-}" ]; then
DISPLAY_SERVER="X11"
else
DISPLAY_SERVER="unknown"
fi
;;
esac
}
@@ -525,10 +412,10 @@ detect_storage() {
# ---------------------------------------
detect_desktop_environment() {
case "${XDG_CURRENT_DESKTOP:-}" in
*GNOME*) DESKTOP_ENV="gnome" ;;
*KDE*) DESKTOP_ENV="kde" ;;
*XFCE*) DESKTOP_ENV="xfce" ;;
*) DESKTOP_ENV="other" ;;
*GNOME*) DESKTOP_ENV="gnome" ;;
*KDE*) DESKTOP_ENV="kde" ;;
*XFCE*) DESKTOP_ENV="xfce" ;;
*) DESKTOP_ENV="other" ;;
esac
}
@@ -557,7 +444,7 @@ get_intel_generation() {
fi
local dev_int
dev_int=$(printf "%d" "$INTEL_GPU_DEVICE_ID")
if [ "$dev_int" -lt 5632 ]; then # 0x1600 = 5632
if [ "$dev_int" -lt 5632 ]; then # 0x1600 = 5632
echo "gen7-"
else
echo "gen8+"
@@ -566,55 +453,48 @@ get_intel_generation() {
# ----------------------------------------------------------------------
# Check if backports repository is enabled (active line without #)
# Note: the Debian 12 (Bookworm) NVIDIA flow no longer uses this function
# (backports EOL 2026-08-09). Still active for kernels, desktop/greetd,
# gaming, audio, communication and Mesa (AMD/Intel).
# ----------------------------------------------------------------------
is_backports_enabled() {
local codename="${DEBIAN_CODENAME:-}"
[ -z "$codename" ] && {
echo false
return
}
[ -z "$codename" ] && { echo false; return; }
local c_pattern="^[^#]*${codename}-backports[[:space:]]+"
local d_pattern="Suites:.*${codename}-backports"
local apt_dir="${APT_DIR:-/etc/apt}"
# Classic embedded (sources.list)
if [ -f "$apt_dir/sources.list" ] && grep -Eq "$c_pattern" "$apt_dir/sources.list" 2>/dev/null; then
echo true
return
if [ -f /etc/apt/sources.list ] && grep -Eq "$c_pattern" /etc/apt/sources.list 2>/dev/null; then
echo true; return
fi
# Classic standalone (any .list file in sources.list.d)
if [ -d "$apt_dir/sources.list.d" ] && grep -qrE "$c_pattern" "$apt_dir/sources.list.d/"*.list 2>/dev/null; then
echo true
return
# Classic standalone (new — debian-backports.list)
if [ -f /etc/apt/sources.list.d/debian-backports.list ] && \
grep -Eq "$c_pattern" /etc/apt/sources.list.d/debian-backports.list 2>/dev/null; then
echo true; return
fi
# Deb822 any .sources file
if [ -d "$apt_dir/sources.list.d" ] && grep -qr "$d_pattern" "$apt_dir/sources.list.d/"*.sources 2>/dev/null; then
echo true
return
if grep -qr "$d_pattern" /etc/apt/sources.list.d/*.sources 2>/dev/null; then
echo true; return
fi
echo false
}
install_backports_or_stable() {
local pkg="$1"
local pkg_desc="${2:-$pkg}"
local bpo_ver=""
if [ "$(is_backports_enabled)" == true ]; then
bpo_ver=$(_get_backports_version "$pkg")
bpo_ver=$(apt-cache madison "$pkg" 2>/dev/null | \
grep "${DEBIAN_CODENAME}-backports" | awk '{print $3}' | head -1)
fi
if is_installed "$pkg"; then
if [ -n "$bpo_ver" ]; then
local current_ver
current_ver=$(_get_installed_version "$pkg")
current_ver=$(dpkg -l "$pkg" 2>/dev/null | awk '/^ii/{print $3}')
if _confirm "Backports: ${pkg}" \
"${pkg} ${current_ver} installed.\nUpgrade to backports ${bpo_ver}?"; then
_run_cmd "Backports" \
@@ -629,7 +509,7 @@ install_backports_or_stable() {
if [ -n "$bpo_ver" ]; then
local stable_ver
stable_ver=$(_get_pkg_version "$pkg")
stable_ver=$(apt-cache policy "$pkg" 2>/dev/null | awk 'NR==3 {print $2; exit}')
if _confirm_custom "${pkg}" "Install ${pkg_desc}?\n\n Backports: ${bpo_ver} (newer, recommended for gaming/newer HW)\n Stable: ${stable_ver:-N/A}\n\nChoose version:" "Backports" "Stable"; then
_run_cmd "Backports" \
"sudo DEBIAN_FRONTEND=noninteractive apt install -y -t ${DEBIAN_CODENAME}-backports $pkg" \
@@ -640,7 +520,7 @@ install_backports_or_stable() {
return
fi
local stable_ver
stable_ver=$(_get_pkg_version "$pkg")
stable_ver=$(apt-cache policy "$pkg" 2>/dev/null | awk 'NR==3 {print $2; exit}')
if _confirm "Install: ${pkg}" "Install ${pkg} ${stable_ver:-}?"; then
_run_cmd "APT" "sudo DEBIAN_FRONTEND=noninteractive apt install -y $pkg" "Installing $pkg..."
fi
@@ -651,9 +531,6 @@ install_backports_or_stable() {
# ----------------------------------------------------------------------
_confirm() {
# --yes (unattended mode): auto-accept without
# invoking whiptail; the prompt gate is the flag itself.
[ "${AUTO_YES:-0}" = "1" ] && return 0
whiptail --title "$1" --yes-button "Yes" --no-button "No" \
--yesno "$2" "${3:-10}" "${4:-65}"
}
@@ -668,11 +545,7 @@ _confirm_custom() {
}
_msg() {
local _msg_title="$1"
local _msg_text="$2"
# BH-014: Escape '%' to prevent whiptail from interpreting them as printf format.
_msg_text="${_msg_text//%/%%}"
whiptail --title "$_msg_title" --msgbox "$_msg_text" "${3:-10}" "${4:-65}" || true
whiptail --title "$1" --msgbox "$2" "${3:-10}" "${4:-65}" || true
}
_msg_red() {
@@ -680,23 +553,15 @@ _msg_red() {
}
_menu() {
local title="$1" text="$2" h="$3" w="$4" lh="$5"
shift 5
local title="$1" text="$2" h="$3" w="$4" lh="$5"; shift 5
whiptail --title "$title" --menu "$text" "$h" "$w" "$lh" "$@" 3>&1 1>&2 2>&3 || true
}
_checklist() {
local title="$1" text="$2" h="$3" w="$4" lh="$5"
shift 5
local title="$1" text="$2" h="$3" w="$4" lh="$5"; shift 5
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
}
@@ -705,7 +570,7 @@ _validate_sudoers() {
local content="$1" dest="$2"
local tmpfile
tmpfile=$(mktemp) || return 1
echo "$content" >"$tmpfile"
echo "$content" > "$tmpfile"
if ! /usr/sbin/visudo -cf "$tmpfile" &>/dev/null; then
local err
err=$(/usr/sbin/visudo -cf "$tmpfile" 2>&1 || true)
@@ -718,7 +583,6 @@ _validate_sudoers() {
rm -f "$tmpfile"
}
# shellcheck disable=SC2120 # Optional prompt arg used by GPU modules.
_pause() {
local msg="${1:-Press Enter to continue...}"
echo -e "$msg"
@@ -726,14 +590,12 @@ _pause() {
}
# Blocks 2-4: run → pause
# Returns the exit code of the executed command so callers can abort
# or adjust state (e.g. NVIDIA_DRIVER_MODE) when a step fails.
_run_cmd() {
local title="$1" command="$2" success_msg="${3:-Running...}"
echo -e "${GREEN}[+]${NC} $success_msg"
echo "──────────────────────────────────────────────"
local rc=0
bash -c "$command" || rc=$?
bash -c "$command"
local rc=$?
echo "──────────────────────────────────────────────"
if [ $rc -eq 0 ]; then
echo -e "${GREEN}[+]${NC} Done."
@@ -741,7 +603,6 @@ _run_cmd() {
echo -e "${RED}[-]${NC} Failed (exit code: $rc)."
fi
_pause
return "$rc"
}
# Blocks 1-4: confirm → run → pause
@@ -755,13 +616,23 @@ _is_headless() {
[ -z "${DISPLAY:-}" ] && [ -z "${WAYLAND_DISPLAY:-}" ]
}
_run_install() {
local pkg="$1"
local ver
ver=$(apt-cache policy "$pkg" 2>/dev/null | awk 'NR==3 {print $2; exit}')
[ -z "$ver" ] && ver="(version unknown)"
if _confirm "Install: ${pkg}" "Install ${pkg}\nVersion: ${ver}?"; then
_run_cmd "Install" "sudo DEBIAN_FRONTEND=noninteractive apt install -y $pkg" "Installing $pkg..."
fi
}
_run_install_batch() {
local pkgs=("$@")
[ ${#pkgs[@]} -eq 0 ] && return 0
local ver_list=""
for pkg in "${pkgs[@]}"; do
local ver
ver=$(_get_pkg_version "$pkg")
ver=$(apt-cache policy "$pkg" 2>/dev/null | awk 'NR==3 {print $2; exit}')
ver_list+=" - ${pkg} ${ver:-unknown}\n"
done
if _confirm "Install" "Install these packages?\n${ver_list}"; then
@@ -769,99 +640,25 @@ _run_install_batch() {
fi
}
# Pre-apply summary: confirm a parsed selection before
# any apt transaction starts. On success it arms
# _SELECTION_CONFIRMED (a countdown) so _install_pkg
# skips its per-package prompt; checklist flows reset
# the counter to 0 after their install loop.
_confirm_install_list() {
local title="$1"
shift
local -a items=("$@")
[ ${#items[@]} -eq 0 ] && return 1
local list=""
local shown=0
local max_show=15
local item
for item in "${items[@]}"; do
if [ "$shown" -lt "$max_show" ]; then
list+=" - ${item}\n"
shown=$((shown + 1))
fi
done
local hidden=$(( ${#items[@]} - shown ))
if [ "$hidden" -gt 0 ]; then
list+="\n ... and ${hidden} more package(s)\n"
fi
list+="\nProceed with the installation?"
local height=$(( shown + 8 ))
[ "$height" -gt 24 ] && height=24
if _confirm "$title" "$list" "$height" 65; then
_SELECTION_CONFIRMED=${#items[@]}
return 0
fi
return 1
}
# Show a multi-select checklist of packages and return the
# selection as a space-separated string ("" on cancel/Esc).
# Args: title prompt "pkg|description" [pkg|description ...]
# Already-installed packages are pre-checked via _state.
_select_packages_checklist() {
local title="$1" prompt="$2"
shift 2
local -a items=() entry
for entry in "$@"; do
local pkg="${entry%%|*}"
items+=("$pkg" "${entry#*|}" "$(_state "$pkg")")
done
local choices
# _checklist swallows whiptail's cancel (Esc) exit code
# via `|| true`, so a cancelled dialog looks like an empty
# selection here; an empty result means "nothing to install".
choices=$(_checklist "$title" "$prompt" \
$TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA "${items[@]}")
choices=$(echo "$choices" | tr -d '"')
echo "$choices"
}
# Install a package with confirmation prompt.
# Handles set -e: failures are caught and reported, not fatal.
_install_pkg() {
_run_install_pkg() {
local pkg="$1"
local ver
ver=$(_get_pkg_version "$pkg")
[ -z "$ver" ] && ver="(version unknown)"
# Pre-apply summary support: when a checklist flow has
# confirmed its full selection (_confirm_install_list),
# the per-package prompt is already answered — the
# countdown expires after exactly N installs.
if [ "$_SELECTION_CONFIRMED" -gt 0 ]; then
_SELECTION_CONFIRMED=$((_SELECTION_CONFIRMED - 1))
elif ! _confirm "Install: ${pkg}" "Install ${pkg}\nVersion: ${ver}?"; then
return 0
ver=$(apt-cache policy "$pkg" 2>/dev/null | awk 'NR==3 {print $2; exit}')
[ -z "$ver" ] && ver="(unknown)"
if _confirm "Install: ${pkg}" "Package: ${pkg}\nVersion: ${ver}\n\nProceed with installation?"; then
_run_cmd "Install" "sudo DEBIAN_FRONTEND=noninteractive apt install -y $pkg" "Installing $pkg..."
fi
_run_cmd "Install" "sudo DEBIAN_FRONTEND=noninteractive apt install -y $pkg" "Installing $pkg..."
}
# Install a package if not already installed.
# Returns: 0 if already installed, 1 if install failed, 2 if cancelled
_install_if_missing() {
local pkg="$1"
if is_installed "$pkg"; then
echo -e "${GREEN}[+]${NC} $pkg already installed."
return 0
get_backports_kernel_version() {
local ver
ver=$(apt-cache policy linux-image-amd64 2>/dev/null | \
grep -E '^[[:space:]]+[0-9]+\.[0-9]+\.[0-9]+.*~bpo' | head -n1 | awk '{print $1}')
if [ -n "$ver" ]; then
echo "$ver"
else
echo "unknown"
fi
_run_cmd "Install" "sudo DEBIAN_FRONTEND=noninteractive apt install -y $pkg" \
"Installing $pkg..."
return $?
}
# _run_install() wrapper: redirect to _install_pkg for consistency
_run_install() {
_install_pkg "$@"
}
# ----------------------------------
@@ -880,9 +677,7 @@ _detect_lang_pkg() {
[ "$lang2" = "en" ] && echo "" && return
# Defensive: LANG may be unset in minimal environments; do not trip set -u.
local full="${LANG:-C}"
full="${full%%.*}"
local full="${LANG%%.*}"
local hyphenated_full
hyphenated_full=$(echo "$full" | tr '[:upper:]' '[:lower:]' | tr '_' '-')
local pkg
@@ -897,90 +692,52 @@ _detect_lang_pkg() {
# ----------------------------------
# Network connectivity check
# ----------------------------------
# shellcheck disable=SC2120 # Optional probe target (default: deb.debian.org).
_check_network() {
local target="${1:-deb.debian.org}"
# Single bounded probe (worst case 3 s; the old
# ping/wget/curl cascade could block up to 13 s).
[[ "$target" =~ ^[a-zA-Z0-9.-]+$ ]] || return 1
timeout 3 bash -c "</dev/tcp/${target}/80" &>/dev/null
}
# ── On-demand network check (non-blocking boot) ──
# The connectivity banner moved from boot to the first
# apt operation. _NET_STATUS caches the result so the
# probe runs at most once per session.
_require_network() {
if [ -z "$_NET_STATUS" ]; then
if _check_network; then
_NET_STATUS=0
else
_NET_STATUS=1
echo -e "${YELLOW}──────────────────────────────────────────${NC}"
echo -e "${YELLOW} No internet connectivity detected.${NC}"
echo -e "${YELLOW} Package installation will fail without network.${NC}"
echo -e "${YELLOW}──────────────────────────────────────────${NC}"
fi
fi
return "$_NET_STATUS"
}
# ----------------------------------
# APT update deduplication
# ----------------------------------
# Runs `apt-get update` at most once per session. Subsequent calls bypass the
# network refresh. Returns 0 on success, 1 if apt-get update fails.
_ensure_apt_updated() {
if [ "$APT_UPDATED" -eq 1 ]; then
echo -e "${GREEN}[+]${NC} APT package lists already refreshed this session."
return 0
if command -v ping &>/dev/null; then
ping -c 1 -W 3 "$target" &>/dev/null && return 0
fi
# Advisory: probe the network once (cached in
# _NET_STATUS) before attempting an apt operation.
_require_network || true
# A correct clock is required for HTTPS certificate
# validation; only runs when apt is actually used.
_ensure_time_synced
echo -e "${GREEN}[+]${NC} Refreshing APT package lists..."
if sudo apt-get update; then
APT_UPDATED=1
return 0
if command -v wget &>/dev/null; then
wget -q --timeout=5 --spider "http://${target}" &>/dev/null && return 0
fi
echo -e "${RED}[-]${NC} apt-get update failed."
if command -v curl &>/dev/null; then
curl -s --connect-timeout 5 -o /dev/null "http://${target}" &>/dev/null && return 0
fi
return 1
}
# ── Deferred hardware state (lazy pre-flight) ──
# The detection battery runs on first need instead of at
# boot. Call _ensure_state_detected() before reading any
# DETECT_* global (LSPCI_OUTPUT, GPU_*, ETH_NAMES,
# DESKTOP_ENV, AUDIO_SERVER, ...).
_run_detection_battery() {
_init_lspci_cache
detect_gpu
detect_network
detect_displayserver
detect_storage
detect_desktop_environment
detect_audio_server
detect_cpu_ram
detect_kernel
}
_ensure_state_detected() {
[ "$STATE_DETECTED" -eq 1 ] && return 0
_run_detection_battery
STATE_DETECTED=1
# ----------------------------------
# LightDM configuration
# ----------------------------------
_configure_lightdm() {
command -v lightdm &>/dev/null || return 0
if _confirm "LightDM" "Configure LightDM to show the user list on the login screen?\n\nThis disables greeter-hide-users."; then
if ! is_installed lightdm-gtk-greeter-settings; then
echo -e "${YELLOW}Installing lightdm-gtk-greeter-settings...${NC}"
sudo DEBIAN_FRONTEND=noninteractive apt install -y lightdm-gtk-greeter-settings
fi
local conf_dir="/etc/lightdm/lightdm.conf.d"
local conf_file="${conf_dir}/99-show-users.conf"
if [ -f "$conf_file" ] && grep -q '^greeter-hide-users=false' "$conf_file"; then
return
fi
sudo mkdir -p "$conf_dir"
printf '[Seat:*]\ngreeter-hide-users=false\n' | sudo tee "$conf_file" > /dev/null
echo -e "${GREEN}LightDM configured to show user list.${NC}"
fi
}
# ── Lazy system state refresh ──
refresh_system_state() {
detect_debian_version
# Forced re-run: a menu action may have changed the
# hardware or repo state since the first detection.
_run_detection_battery
STATE_DETECTED=1
detect_gpu
detect_cpu_ram
}
+18 -48
View File
@@ -2,10 +2,6 @@
# zram.sh — ZRAM submenu: view, create/reconfigure, remove
zram_menu() {
# Hardware state is detected on demand; RAM_KB
# and RAM_SUMMARY are populated by the battery.
_ensure_state_detected
while true; do
local choice
choice=$(_menu "ZRAM Configuration" \
@@ -19,10 +15,10 @@ zram_menu() {
clear
case "$choice" in
1) _zram_view ;;
2) _zram_create ;;
3) _zram_remove ;;
4) break ;;
1) _zram_view ;;
2) _zram_create ;;
3) _zram_remove ;;
4) break ;;
esac
done
}
@@ -37,11 +33,11 @@ _zram_view() {
if [ -f /etc/default/zramswap ]; then
while IFS='=' read -r key val; do
case "$key" in
ALGO) algo=$val ;;
SIZE) size=$val ;;
PRIORITY) priority=$val ;;
ALGO) algo=$val ;;
SIZE) size=$val ;;
PRIORITY) priority=$val ;;
esac
done </etc/default/zramswap
done < /etc/default/zramswap
fi
local info="ZRAM Configuration:\n"
@@ -72,11 +68,11 @@ _zram_create() {
if [ -f /etc/default/zramswap ]; then
while IFS='=' read -r key val; do
case "$key" in
ALGO) cur_algo=$val ;;
SIZE) cur_size=$val ;;
PRIORITY) cur_prio=$val ;;
ALGO) cur_algo=$val ;;
SIZE) cur_size=$val ;;
PRIORITY) cur_prio=$val ;;
esac
done </etc/default/zramswap
done < /etc/default/zramswap
fi
local cur="ZRAM is already configured:\n"
cur+=" Algorithm: ${cur_algo:-not set}\n"
@@ -89,18 +85,13 @@ _zram_create() {
fi
fi
local ram_gb=$((RAM_KB / 1024 / 1024))
if [ "$ram_gb" -gt 8 ]; then
recommended_mb=4096
else
recommended_mb=$((((RAM_KB / 1024 / 1024 + 1) / 2) * 1024))
fi
local half_ram_mb=$(( ((RAM_KB / 1024 / 1024 + 1) / 2) * 1024 ))
local algo
algo=$(_menu "ZRAM Configuration" \
"ZRAM creates a compressed swap device in RAM to reduce disk I/O and boost speed. Data is stored compressed in memory. Choose an algorithm below to balance CPU usage and compression ratio:" \
$TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
"lz4" "Fastest compression. Lowest CPU overhead. (Default)" \
"lz4" "Fastest compression. Lowest CPU overhead. (Default)" \
"zstd" "Higher compression ratio. Saves more RAM, uses more CPU.")
if [ -z "$algo" ]; then
@@ -109,10 +100,10 @@ _zram_create() {
fi
local zram_size
if _confirm "ZRAM Size" "Use recommended size for ZRAM? (${recommended_mb} MB out of ${RAM_SUMMARY})"; then
zram_size=$recommended_mb
if _confirm "ZRAM Size" "Use recommended size for ZRAM? (${half_ram_mb} MB out of ${RAM_SUMMARY})"; then
zram_size=$half_ram_mb
else
zram_size=$(_inputbox "ZRAM Size" "Enter ZRAM size in MB:" 8 60 "$recommended_mb")
zram_size=$(_inputbox "ZRAM Size" "Enter ZRAM size in MB:" 8 60 "$half_ram_mb")
if [ -z "$zram_size" ] || ! [[ "$zram_size" =~ ^[0-9]+$ ]] || [ "$zram_size" -eq 0 ]; then
echo "ZRAM configuration cancelled."
return
@@ -126,21 +117,8 @@ _zram_create() {
_run_cmd "ZRAM" "sudo apt install -y zram-tools" "Installing zram-tools..."
echo "Resetting existing ZRAM device..."
sudo swapoff /dev/zram0 2>/dev/null || true
sudo modprobe -r zram 2>/dev/null || true
echo "Writing configuration..."
# SECURITY: Validate algo and size before writing to system file.
[[ "$algo" =~ ^(lz4|zstd)$ ]] || {
echo "Invalid ZRAM algorithm" >&2
return 1
}
[[ "$zram_size" =~ ^[0-9]+$ ]] || {
echo "Invalid ZRAM size" >&2
return 1
}
sudo tee /etc/default/zramswap >/dev/null <<EOF
sudo tee /etc/default/zramswap > /dev/null <<EOF
ALGO=$algo
SIZE=$zram_size
PRIORITY=100
@@ -149,10 +127,6 @@ EOF
echo "Restarting zramswap service..."
sudo systemctl restart zramswap
if ! grep -q "\[${algo}\]" /sys/block/zram0/comp_algorithm 2>/dev/null; then
echo -e "${YELLOW}Warning: ZRAM algorithm not applied yet. Reboot to finalize.${NC}"
fi
echo ""
echo -e "${GREEN}ZRAM configured successfully.${NC}"
echo ""
@@ -177,10 +151,6 @@ _zram_remove() {
echo "Stopping zramswap service..."
sudo systemctl stop zramswap || true
echo "Releasing ZRAM device..."
sudo swapoff /dev/zram0 2>/dev/null || true
sudo modprobe -r zram 2>/dev/null || true
_run_cmd "ZRAM" "sudo apt purge -y zram-tools" "Purging zram-tools..."
echo "Removing configuration..."
-429
View File
@@ -1,429 +0,0 @@
#!/usr/bin/env bash
# desktops.bats — Offline tests for the desktop helpers in
# modules/desktop_display.sh (SP2) and the desktop module
# wiring in _load_module (SP2.5).
#
# No `set -euo pipefail` here: BATS controls error handling
# per test (see utils.bats).
#
# State-mutating functions are called DIRECTLY (a `run`
# subshell would lose their globals); pure-output helpers
# use `run`.
#
# 100% offline: dpkg and whiptail come from the PATH mocks
# in test_helper.bash, systemctl uses the helper mock,
# /etc/X11/default-display-manager is redirected through the
# DM_FILE fixture, and the real audio installer is replaced
# by a call-counter stub.
load test_helper.bash
# Stub for modules/system/audio.sh (never loaded here):
# count calls instead of running apt. Counter reset in setup().
_install_pipewire_standard() {
PIPEWIRE_INSTALL_CALLS=$((PIPEWIRE_INSTALL_CALLS + 1))
}
setup() {
mock_reset
load_utils
# load_utils() sources utils.sh inside a function, so the
# top-level `declare -A _LOADED_MODULES` (utils.sh:48) was
# function-local and vanished when it returned — the same
# caveat the helper documents for the ETH_* arrays.
# Re-declare it globally for the _load_module tests.
declare -gA _LOADED_MODULES=()
# Only function definitions at the top level: safe to source.
# Only function definitions at the top level: safe to source.
source "${TEST_PROJECT_ROOT}/modules/desktop_display.sh"
# The GNOME installers are pure function definitions too;
# desktop_display.sh (their DM/pipewire collaborators) is
# already sourced above.
source "${TEST_PROJECT_ROOT}/modules/desktops/desktop_gnome.sh"
# Fixture redirect for /etc/X11/default-display-manager:
# the file exists but is empty, so `-s` fails and the
# systemctl fallback is exercised deterministically.
DM_FILE="$(mktemp)"
export DM_FILE
PIPEWIRE_INSTALL_CALLS=0
}
teardown() {
if [ -n "${DM_FILE:-}" ]; then
rm -f "$DM_FILE"
fi
if [ -n "${MOCK_SUDO_LOG:-}" ]; then
rm -f "$MOCK_SUDO_LOG"
fi
if [ -n "${MOCK_APT_ROOT:-}" ]; then
rm -rf "$MOCK_APT_ROOT"
fi
if [ -n "${SESSION_FIXTURE:-}" ]; then
rm -rf "$SESSION_FIXTURE"
fi
unset SESSION_DIR_WAYLAND SESSION_DIR_X11
}
# ── _existing_display_manager ────────────────────────
@test "_existing_display_manager reads the default DM file" {
printf '/usr/sbin/gdm3\n' >"$DM_FILE"
run _existing_display_manager
[ "$status" -eq 0 ]
[ "$output" = "gdm3" ]
}
@test "_existing_display_manager prefers the DM file over an enabled unit" {
printf '/usr/sbin/sddm\n' >"$DM_FILE"
export MOCK_SYSTEMCTL_ENABLED="gdm3"
run _existing_display_manager
[ "$status" -eq 0 ]
[ "$output" = "sddm" ]
}
@test "_existing_display_manager falls back to an enabled systemd unit" {
# DM_FILE exists but is empty: `-s` must fail, not `-e`.
export MOCK_SYSTEMCTL_ENABLED="lightdm"
run _existing_display_manager
[ "$status" -eq 0 ]
[ "$output" = "lightdm" ]
}
@test "_existing_display_manager evaluates units in candidate order" {
# gdm3 → lightdm → sddm → greetd → gdm, whatever order
# systemctl reports them in.
export MOCK_SYSTEMCTL_ENABLED="sddm lightdm"
run _existing_display_manager
[ "$status" -eq 0 ]
[ "$output" = "lightdm" ]
}
@test "_existing_display_manager falls back when the DM file is missing" {
rm -f "$DM_FILE"
export MOCK_SYSTEMCTL_ENABLED="greetd"
run _existing_display_manager
[ "$status" -eq 0 ]
[ "$output" = "greetd" ]
}
@test "_existing_display_manager prints nothing when nothing is configured" {
run _existing_display_manager
[ "$status" -eq 0 ]
[ -z "$output" ]
}
# ── _offer_pipewire_after_desktop ────────────────────
@test "_offer_pipewire_after_desktop skips without prompting when pipewire-audio is installed" {
MOCK_DPKG_INSTALLED="pipewire-audio"
WHIPTAIL_CALLS=0
whiptail() { WHIPTAIL_CALLS=$((WHIPTAIL_CALLS + 1)); }
_offer_pipewire_after_desktop
[ "$WHIPTAIL_CALLS" -eq 0 ]
[ "$PIPEWIRE_INSTALL_CALLS" -eq 0 ]
}
@test "_offer_pipewire_after_desktop installs the stack when accepted" {
export MOCK_WHIPTAIL_RC=0 # user answers "Yes"
_offer_pipewire_after_desktop
[ "$PIPEWIRE_INSTALL_CALLS" -eq 1 ]
}
@test "_offer_pipewire_after_desktop returns 0 without installing when declined" {
export MOCK_WHIPTAIL_RC=1 # user answers "No"
offer_rc=0
_offer_pipewire_after_desktop || offer_rc=$?
[ "$offer_rc" -eq 0 ]
[ "$PIPEWIRE_INSTALL_CALLS" -eq 0 ]
}
@test "_offer_pipewire_after_desktop checks the pipewire package on Bullseye" {
export DEBIAN_VERSION="11"
MOCK_DPKG_INSTALLED="pipewire" # Bullseye package name
_offer_pipewire_after_desktop
[ "$PIPEWIRE_INSTALL_CALLS" -eq 0 ]
}
@test "_offer_pipewire_after_desktop ignores pipewire-audio on Bullseye" {
export DEBIAN_VERSION="11"
MOCK_DPKG_INSTALLED="pipewire-audio" # wrong name on 11
export MOCK_WHIPTAIL_RC=0 # user accepts
_offer_pipewire_after_desktop
[ "$PIPEWIRE_INSTALL_CALLS" -eq 1 ]
}
# ── _load_module: desktop wiring (SP2.5) ─────────────
# The dependency cascade is not under test here: mark the
# transitive deps of the desktop modules as loaded so only
# the module file itself is sourced.
_load_module_with_deps_marked() {
local module="$1"
declare -gA _LOADED_MODULES=(
[desktop_display]=1
[audio]=1
[repos]=1
[repo_detect]=1
)
_load_module "$module"
}
@test "_load_module xfce sources modules/desktops/desktop_xfce.sh" {
_load_module_with_deps_marked xfce
type xfce_menu >/dev/null
type _install_xfce_custom >/dev/null
type _xfce_polkit_rules >/dev/null
}
@test "_load_module lxde sources modules/desktops/desktop_lxde.sh" {
_load_module_with_deps_marked lxde
type lxde_menu >/dev/null
type _install_lxde_full >/dev/null
}
@test "_load_module gnome sources modules/desktops/desktop_gnome.sh" {
_load_module_with_deps_marked gnome
type gnome_menu >/dev/null
type _install_gnome_full >/dev/null
}
@test "_load_module kde sources modules/desktops/desktop_kde.sh" {
_load_module_with_deps_marked kde
type kde_menu >/dev/null
type _install_kde_minimal >/dev/null
}
@test "_load_module returns 1 for an unknown module" {
run _load_module no_such_module
[ "$status" -eq 1 ]
}
# ── terminal selection: ptyxis on trixie ──────────────
# gnome-core declares the OR-dep "gnome-terminal or
# gnome-console or ptyxis"; apt resolves an unset OR-dep
# with the first alternative, so listing the terminal
# explicitly in the same apt transaction is what steers
# the resolver. These tests assert the exact package
# that lands in the apt command.
#
# _gnome_capture_apt_cmd runs one installer with every
# external effect stubbed: _run_cmd is replaced so the
# apt command is captured instead of executed, and the
# PATH sudo mock stays in dry-run mode so debconf and
# systemctl invocations are logged, not executed.
_gnome_capture_apt_cmd() {
local installer="$1"
GNOME_APT_CMD=""
_confirm_install_list() { return 0; }
_ensure_apt_updated() { :; }
_run_cmd() { GNOME_APT_CMD="$2"; }
"$installer"
printf '%s\n' "$GNOME_APT_CMD"
}
@test "GNOME Full installs ptyxis on trixie" {
export DEBIAN_CODENAME="trixie"
export MOCK_SUDO_DRY_RUN=1
run _gnome_capture_apt_cmd _install_gnome_full
[ "$status" -eq 0 ]
[[ "$output" == *"apt install -y gnome gdm3 ptyxis"* ]]
[[ "$output" != *gnome-terminal* ]]
}
@test "GNOME Core installs ptyxis on trixie" {
export DEBIAN_CODENAME="trixie"
export MOCK_SUDO_DRY_RUN=1
run _gnome_capture_apt_cmd _install_gnome_core
[ "$status" -eq 0 ]
[[ "$output" == *"apt install -y gnome-core gdm3 ptyxis"* ]]
[[ "$output" != *gnome-terminal* ]]
}
@test "GNOME Full falls back to gnome-terminal off trixie" {
export DEBIAN_CODENAME="bookworm"
export MOCK_SUDO_DRY_RUN=1
run _gnome_capture_apt_cmd _install_gnome_full
[ "$status" -eq 0 ]
[[ "$output" == *"apt install -y gnome gdm3 gnome-terminal"* ]]
[[ "$output" != *ptyxis* ]]
}
@test "GNOME falls back to gnome-terminal when the codename is empty" {
# /etc/os-release missing or without VERSION_CODENAME
# leaves DEBIAN_CODENAME unset: the safe default is the
# historical behaviour.
export DEBIAN_CODENAME=""
export MOCK_SUDO_DRY_RUN=1
run _gnome_capture_apt_cmd _install_gnome_full
[ "$status" -eq 0 ]
[[ "$output" == *"apt install -y gnome gdm3 gnome-terminal"* ]]
[[ "$output" != *ptyxis* ]]
}
# ── KDE Plasma: bookworm Wayland gate ─────────────
# plasma-workspace-wayland exists only on bookworm:
# bookworm's plasma-workspace ships the X11 session
# only, so the explicit listing keeps the Wayland
# session in the transaction deterministically; off
# bookworm the package does not exist and nothing is
# named in the apt command.
# _kde_capture_apt_cmd runs one KDE installer with
# every external effect stubbed (same shape as the
# GNOME capture helper): the apt command is captured
# instead of executed, and the PATH sudo mock stays
# in dry-run mode so debconf and systemctl invocations
# are logged, not executed.
_kde_capture_apt_cmd() {
local installer="$1"
_load_module_with_deps_marked kde
KDE_APT_CMD=""
_confirm_install_list() { return 0; }
_ensure_apt_updated() { :; }
_run_cmd() { KDE_APT_CMD="$2"; }
"$installer"
printf '%s\n' "$KDE_APT_CMD"
}
# _kde_capture_confirm runs one KDE installer with the
# pre-apply summary captured instead of shown: _confirm
# records the list _confirm_install_list builds, so the
# summary content (never an empty package line) can be
# asserted directly. The pipewire follow-up offer is
# stubbed so the recorded list is the package summary.
_kde_capture_confirm() {
local installer="$1"
_load_module_with_deps_marked kde
KDE_CONFIRM_LIST=""
_confirm() { KDE_CONFIRM_LIST="$2"; return 0; }
_ensure_apt_updated() { :; }
_run_cmd() { :; }
_offer_pipewire_after_desktop() { :; }
"$installer"
printf '%s\n' "$KDE_CONFIRM_LIST"
}
@test "_kde_wayland_pkg prints plasma-workspace-wayland on bookworm" {
export DEBIAN_CODENAME="bookworm"
_load_module_with_deps_marked kde
run _kde_wayland_pkg
[ "$status" -eq 0 ]
[ "$output" = "plasma-workspace-wayland" ]
}
@test "_kde_wayland_pkg prints nothing off bookworm" {
export DEBIAN_CODENAME="trixie"
_load_module_with_deps_marked kde
run _kde_wayland_pkg
[ "$status" -eq 0 ]
[ -z "$output" ]
}
@test "KDE Full installs the Wayland session package on bookworm" {
export DEBIAN_CODENAME="bookworm"
export MOCK_SUDO_DRY_RUN=1
run _kde_capture_apt_cmd _install_kde_full
[ "$status" -eq 0 ]
[[ "$output" == *"apt install -y kde-standard sddm plasma-workspace-wayland"* ]]
}
@test "KDE Full omits the Wayland session package off bookworm" {
export DEBIAN_CODENAME="trixie"
export MOCK_SUDO_DRY_RUN=1
run _kde_capture_apt_cmd _install_kde_full
[ "$status" -eq 0 ]
[[ "$output" == *"apt install -y kde-standard sddm"* ]]
[[ "$output" != *"plasma-workspace-wayland"* ]]
}
@test "KDE Minimal installs the Wayland session package on bookworm" {
export DEBIAN_CODENAME="bookworm"
export MOCK_SUDO_DRY_RUN=1
run _kde_capture_apt_cmd _install_kde_minimal
[ "$status" -eq 0 ]
[[ "$output" == *"apt install -y kde-plasma-desktop sddm plasma-workspace-wayland"* ]]
}
@test "KDE Minimal omits the Wayland session package off bookworm" {
export DEBIAN_CODENAME="trixie"
export MOCK_SUDO_DRY_RUN=1
run _kde_capture_apt_cmd _install_kde_minimal
[ "$status" -eq 0 ]
[[ "$output" == *"apt install -y kde-plasma-desktop sddm"* ]]
[[ "$output" != *"plasma-workspace-wayland"* ]]
}
@test "KDE Minimal summary lists the Wayland session package on bookworm" {
export DEBIAN_CODENAME="bookworm"
export MOCK_SUDO_DRY_RUN=1
run _kde_capture_confirm _install_kde_minimal
[ "$status" -eq 0 ]
[[ "$output" == *"plasma-workspace-wayland"* ]]
}
@test "KDE Minimal summary shows no empty package line off bookworm" {
export DEBIAN_CODENAME="trixie"
export MOCK_SUDO_DRY_RUN=1
run _kde_capture_confirm _install_kde_minimal
[ "$status" -eq 0 ]
[[ "$output" == *"kde-plasma-desktop"* ]]
[[ "$output" == *"sddm"* ]]
[[ "$output" != *"plasma-workspace-wayland"* ]]
[[ "$output" != *"- "$'\n'* ]]
}
# ── _sddm_session_candidates: ordered session probe ──
# Existence decides (no codename gates) and Wayland is
# preferred. The roots are redirected to a per-test
# fixture tree through SESSION_DIR_WAYLAND /
# SESSION_DIR_X11 (same spirit as the DM_FILE fixture),
# so the probe never touches the real /usr/share tree.
_sddm_session_fixture() {
SESSION_FIXTURE="$(mktemp -d)"
mkdir -p "$SESSION_FIXTURE/wayland" "$SESSION_FIXTURE/x11"
SESSION_DIR_WAYLAND="$SESSION_FIXTURE/wayland"
SESSION_DIR_X11="$SESSION_FIXTURE/x11"
export SESSION_DIR_WAYLAND SESSION_DIR_X11
}
@test "SDDM session probe prefers bookworm Wayland (plasmawayland)" {
_sddm_session_fixture
: >"$SESSION_DIR_WAYLAND/plasmawayland.desktop"
: >"$SESSION_DIR_X11/plasma.desktop"
run _sddm_session_candidates
[ "$status" -eq 0 ]
[ "$output" = "plasmawayland" ]
}
@test "SDDM session probe picks trixie Wayland (plasma.desktop)" {
_sddm_session_fixture
: >"$SESSION_DIR_WAYLAND/plasma.desktop"
: >"$SESSION_DIR_X11/plasmax11.desktop"
run _sddm_session_candidates
[ "$status" -eq 0 ]
[ "$output" = "plasma" ]
}
@test "SDDM session probe falls back to trixie X11 (plasmax11)" {
_sddm_session_fixture
: >"$SESSION_DIR_X11/plasmax11.desktop"
run _sddm_session_candidates
[ "$status" -eq 0 ]
[ "$output" = "plasmax11" ]
}
@test "SDDM session probe keeps LXQt last when no KDE session exists" {
_sddm_session_fixture
: >"$SESSION_DIR_WAYLAND/lxqt-wayland.desktop"
: >"$SESSION_DIR_X11/lxqt.desktop"
run _sddm_session_candidates
[ "$status" -eq 0 ]
[ "$output" = "lxqt-wayland" ]
}
@test "SDDM session probe returns empty when no session file exists" {
_sddm_session_fixture
run _sddm_session_candidates
[ "$status" -eq 0 ]
[ -z "$output" ]
}
-114
View File
@@ -1,114 +0,0 @@
#!/usr/bin/env bash
# repo_detect.bats — Offline tests for modules/repos/repo_detect.sh
# and is_backports_enabled() from modules/utils.sh.
#
# No `set -euo pipefail` here: BATS controls error handling
# per test.
#
# The modules under test read ${APT_DIR:-/etc/apt} (the APT_DIR
# indirection added for testability). mock_reset() points APT_DIR
# at a fresh empty $MOCK_APT_ROOT per test, and every test builds
# its own fixture tree inside it — the real /etc/apt is never
# read, so the suite is 100% offline and hermetic.
#
# Every function under test answers via stdout (echo
# true/false/deb822/...), so `run` + `$output` is the natural
# assertion style.
load test_helper.bash
setup() {
mock_reset
load_utils
}
teardown() {
if [ -n "${MOCK_SUDO_LOG:-}" ]; then
rm -f "$MOCK_SUDO_LOG"
fi
if [ -n "${MOCK_APT_ROOT:-}" ]; then
rm -rf "$MOCK_APT_ROOT"
fi
}
# ── Fixture helper ───────────────────────────────────
# Write one fixture file inside the mock apt root, with one
# content line per argument:
# _fixt_file sources.list "deb ... bookworm main" ...
# _fixt_file sources.list.d/debian.sources "Types: deb" ...
_fixt_file() {
local rel="$1"
shift
mkdir -p "${MOCK_APT_ROOT}/$(dirname "$rel")"
printf '%s\n' "$@" >"${MOCK_APT_ROOT}/${rel}"
}
# ── is_backports_enabled (modules/utils.sh) ──────────
@test "is_backports_enabled returns true with backports in sources.list" {
_fixt_file sources.list \
"deb http://deb.debian.org/debian bookworm main" \
"deb http://deb.debian.org/debian bookworm-updates main" \
"deb http://deb.debian.org/debian bookworm-backports main"
run is_backports_enabled
[ "$status" -eq 0 ]
[ "$output" = "true" ]
}
@test "is_backports_enabled returns false without backports" {
_fixt_file sources.list \
"deb http://deb.debian.org/debian bookworm main" \
"deb http://deb.debian.org/debian bookworm-updates main"
run is_backports_enabled
[ "$status" -eq 0 ]
[ "$output" = "false" ]
}
@test "is_backports_enabled returns true with a standalone backports .list file" {
_fixt_file sources.list.d/debian-backports.list \
"deb http://deb.debian.org/debian bookworm-backports main"
run is_backports_enabled
[ "$status" -eq 0 ]
[ "$output" = "true" ]
}
@test "is_backports_enabled returns true with a deb822 backports suite" {
_fixt_file sources.list.d/debian-backports.sources \
"Types: deb" \
"URIs: http://deb.debian.org/debian" \
"Suites: bookworm-backports" \
"Components: main" \
"Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg"
run is_backports_enabled
[ "$status" -eq 0 ]
[ "$output" = "true" ]
}
# ── detect_repo_format ───────────────────────────────
@test "detect_repo_format returns deb822 for a debian.sources file" {
# DEB822 is only valid on Debian 13 (Trixie).
DEBIAN_VERSION="13"
_fixt_file sources.list.d/debian.sources \
"Types: deb" \
"URIs: http://deb.debian.org/debian" \
"Suites: bookworm bookworm-updates" \
"Components: main contrib non-free non-free-firmware" \
"Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg"
run detect_repo_format
[ "$status" -eq 0 ]
[ "$output" = "deb822" ]
}
@test "detect_repo_format returns classic for a classic sources.list" {
_fixt_file sources.list \
"deb http://deb.debian.org/debian bookworm main" \
"deb http://deb.debian.org/debian bookworm-updates main"
run detect_repo_format
[ "$status" -eq 0 ]
[ "$output" = "classic" ]
}
@test "detect_repo_format returns none for an empty apt root" {
run detect_repo_format
[ "$status" -eq 0 ]
[ "$output" = "none" ]
}
-377
View File
@@ -1,377 +0,0 @@
#!/usr/bin/env bash
# test_helper.bash — Setup and offline system mocks for the
# debianito BATS test suite.
#
# Layout
# ------
# load_utils() Safe loading of the modules under test.
# _mock_*() Mock implementations (one per command).
# _install_mocks() Serializes the _mock_* functions into
# standalone executables under $MOCK_BIN_DIR.
# mock_reset() Per-test fixture reset + PATH setup.
#
# Why PATH-based mock scripts instead of plain shell functions?
# ------------------------------------------------------------
# _init_lspci_cache() runs `timeout 2 lspci -nn` and
# detect_network() runs `timeout 2 ip -o link show`,
# `timeout 2 ip -4 -o addr show <iface>` and
# `timeout 2 iwgetid -r <iface>`. timeout(1) execs its
# target as a CHILD PROCESS, and a child process cannot see
# the shell functions of its parent. The _mock_* functions
# are therefore written to disk as real executables
# (self-contained: shebang + `declare -f` output + dispatch
# line) and $MOCK_BIN_DIR is prepended to PATH, so both
# direct calls and timeout-wrapped calls hit the mocks.
#
# Everything here is 100% offline: no network, no apt
# update, and no real package is installed or removed.
#
# shellcheck disable=SC2148
# ── Locations ──────────────────────────────────────────────────
[ -n "${BATS_TEST_DIRNAME:-}" ] || {
echo "test_helper.bash must be loaded from a BATS test file" >&2
return 1 2>/dev/null || exit 1
}
TEST_PROJECT_ROOT="$(cd "${BATS_TEST_DIRNAME}/.." && pwd)"
export TEST_PROJECT_ROOT
# Holds the generated mock executables (created once per
# load; the OS cleans /tmp, so no per-test cleanup needed).
MOCK_BIN_DIR="$(mktemp -d "${TMPDIR:-/tmp}/debianito-mocks.XXXXXX")"
export MOCK_BIN_DIR
# ── Safe module loading ────────────────────────────────────────
# Contract (verified against the current sources):
# * modules/utils.sh and modules/repos/repo_detect.sh
# contain ONLY function definitions and variable
# assignments — no top-level command execution — so
# sourcing them is side-effect free.
# * debianito.sh itself must NEVER be sourced from tests:
# it executes the module chain and the menu at top
# level.
load_utils() {
local f
for f in modules/utils.sh modules/repos/repo_detect.sh; do
if [ ! -f "${TEST_PROJECT_ROOT}/${f}" ]; then
echo "load_utils: missing ${TEST_PROJECT_ROOT}/${f}" >&2
return 1
fi
done
# shellcheck source=modules/utils.sh
source "${TEST_PROJECT_ROOT}/modules/utils.sh"
# shellcheck source=modules/repos/repo_detect.sh
source "${TEST_PROJECT_ROOT}/modules/repos/repo_detect.sh"
# Note: sourcing from inside a function makes the
# top-level `declare -a ETH_NAMES=()` & co. of utils.sh
# function-local; they vanish when load_utils() returns.
# That is harmless — the functions under test assign
# those arrays as globals at call time (e.g. the BH-013
# reset at the top of detect_network()), so their
# behaviour is unchanged.
:
}
# ── Globals that debianito.sh normally defines ───────────────
# The modules consume them at call time only. Colour codes
# are kept EMPTY on purpose so output assertions stay clean.
export RED="" GREEN="" YELLOW="" CYAN="" NC=""
export DEBIAN_CODENAME="bookworm"
export DEBIAN_VERSION="12"
export MODULES_DIR="${TEST_PROJECT_ROOT}/modules"
# ════════════════════════════════════════════════════════════
# Mock implementations
#
# Each mock reads its canned data from MOCK_* environment
# variables, so a test injects predictable output with a
# plain assignment (the vars are exported by mock_reset, and
# assigning to an exported var keeps the export attribute):
#
# MOCK_DPKG_INSTALLED space-separated "installed" packages
# MOCK_DPKG_VERSION version reported for installed pkgs
# MOCK_APT_CACHE_POLICY_OUTPUT `apt-cache policy <pkg>` output
# MOCK_APT_CACHE_MADISON_OUTPUT `apt-cache madison <pkg>` output
# MOCK_APT_CACHE_SEARCH_OUTPUT `apt-cache search <re>` output
# MOCK_LSPCI_OUTPUT `lspci -nn` output
# MOCK_LSUSB_OUTPUT `lsusb` output
# MOCK_IP_LINK_OUTPUT `ip -o link show` output
# MOCK_IP_ADDR_OUTPUT "iface cidr" lines for
# `ip -4 -o addr show <iface>`
# MOCK_SSID_OUTPUT `iwgetid -r <iface>` output
# MOCK_TIMEZONE_OUTPUT `timedatectl show -p Timezone --value`
# MOCK_NTP_SYNC_OUTPUT `timedatectl show --property=NTPSynchronized --value`
# MOCK_SYSTEMCTL_ENABLED space-separated units that report "enabled"
# MOCK_WHIPTAIL_OUTPUT / _RC whiptail stdout / exit code
# MOCK_SUDO_DRY_RUN 1 = log-only sudo (see _mock_sudo)
# MOCK_SUDO_LOG log file for dry-run sudo
# MOCK_APT_ROOT per-test fixture mirror of /etc/apt,
# exported to the code under test as
# APT_DIR (see the APT_DIR
# indirection in utils.sh and
# repos/repo_detect.sh)
# ════════════════════════════════════════════════════════════
# ── dpkg(1) ───────────────────────────────────────────────────
# `dpkg -l <pkg>` prints an "ii" status line and exits 0
# when <pkg> is listed in $MOCK_DPKG_INSTALLED; prints
# nothing and exits 1 otherwise (real dpkg -l reports
# "no packages found" with status 1). is_installed()
# greps for a leading "ii", so the line prefix is what
# matters — both are implemented faithfully anyway.
_mock_dpkg() {
local pkg="${@: -1}" # last argument
local installed
for installed in ${MOCK_DPKG_INSTALLED:-}; do
if [ "$installed" = "$pkg" ]; then
printf 'ii %s %s all mock package\n' \
"$pkg" "${MOCK_DPKG_VERSION:-1.0.0-mock}"
return 0
fi
done
return 1
}
# ── apt-cache(8) ──────────────────────────────────────────────
# Canned output per subcommand. The policy fixture needs at
# least 3 lines: _get_pkg_version() parses field 2 of line
# 3 (the "Candidate:" line in real output).
_mock_apt_cache() {
local sub="${1:-}"
[ $# -gt 0 ] && shift
case "$sub" in
policy)
[ -n "${MOCK_APT_CACHE_POLICY_OUTPUT:-}" ] &&
printf '%s\n' "${MOCK_APT_CACHE_POLICY_OUTPUT}"
;;
madison)
[ -n "${MOCK_APT_CACHE_MADISON_OUTPUT:-}" ] &&
printf '%s\n' "${MOCK_APT_CACHE_MADISON_OUTPUT}"
;;
search)
[ -n "${MOCK_APT_CACHE_SEARCH_OUTPUT:-}" ] &&
printf '%s\n' "${MOCK_APT_CACHE_SEARCH_OUTPUT}"
;;
esac
return 0
}
# ── lspci(8) ──────────────────────────────────────────────────
# Prints $MOCK_LSPCI_OUTPUT verbatim; arguments (-nn) are
# ignored. Served through the PATH wrapper because
# _init_lspci_cache() captures `timeout 2 lspci -nn`.
_mock_lspci() {
printf '%s' "${MOCK_LSPCI_OUTPUT:-}"
return 0
}
# ── lsusb(8) ──────────────────────────────────────────────────
# Prints $MOCK_LSUSB_OUTPUT verbatim. detect_network()
# uses it as the USB-WiFi fallback (layer 4) when no PCI
# wireless device exists.
_mock_lsusb() {
printf '%s' "${MOCK_LSUSB_OUTPUT:-}"
return 0
}
# ── ip(8) ─────────────────────────────────────────────────────
# Just enough for detect_network():
# ip -o link show → one line per interface
# ($MOCK_IP_LINK_OUTPUT)
# ip -4 -o addr show <iface> → a full "ip -o" line whose
# 4th whitespace field is
# the IPv4 CIDR of <iface>,
# looked up from the
# "iface cidr" lines of
# $MOCK_IP_ADDR_OUTPUT
_mock_ip() {
case "$1 $2" in
"-o link")
# Real `ip` terminates its output with a newline;
# `while read` loops (detect_network) drop the last
# line of an unterminated stream, so the mock must
# terminate it too.
if [ -n "${MOCK_IP_LINK_OUTPUT:-}" ]; then
printf '%s\n' "${MOCK_IP_LINK_OUTPUT:-}"
fi
;;
"-4 -o")
local iface="${5:-}" name cidr
[ -z "$iface" ] && return 0
while IFS=' ' read -r name cidr; do
if [ "$name" = "$iface" ]; then
printf '2: %s inet %s brd 192.168.1.255 scope global dynamic %s\n' \
"$iface" "$cidr" "$iface"
return 0
fi
done <<EOF
${MOCK_IP_ADDR_OUTPUT:-}
EOF
;;
esac
return 0
}
# ── iwgetid(1) ────────────────────────────────────────────────
# Prints $MOCK_SSID_OUTPUT. detect_network() calls it only
# for interfaces whose link state is UP.
_mock_iwgetid() {
printf '%s' "${MOCK_SSID_OUTPUT:-}"
return 0
}
# ── timedatectl(1) ────────────────────────────────────────────
# Just enough for _ensure_time_synced():
# show -p Timezone --value → $MOCK_TIMEZONE_OUTPUT
# show --property=NTPSynchronized --value → $MOCK_NTP_SYNC_OUTPUT
_mock_timedatectl() {
case "$1 $2" in
"show -p")
printf '%s\n' "${MOCK_TIMEZONE_OUTPUT:-UTC}"
;;
"show --property="*)
printf '%s\n' "${MOCK_NTP_SYNC_OUTPUT:-no}"
;;
esac
return 0
}
# ── sudo(8) ───────────────────────────────────────────────────
# Spec behaviour: execute the command it receives, without
# asking for a password. sudo's own flags (-v, -n, -H, …)
# are stripped; the rest runs through env(1) so that
# `sudo VAR=val cmd` prefixes work too.
#
# ⚠ WARNING: the command executes FOR REAL. Only exercise
# sudo code paths with benign commands (file copies, echo,
# true, …). Set MOCK_SUDO_DRY_RUN=1 to switch to
# logging-only mode: the invocation is appended to
# $MOCK_SUDO_LOG and the mock exits 0 without executing
# anything — the safe choice for tests that touch
# privileged code paths.
_mock_sudo() {
if [ "${MOCK_SUDO_DRY_RUN:-0}" = "1" ]; then
printf '%s\n' "$*" >>"${MOCK_SUDO_LOG:-/tmp/debianito-sudo-mock.log}"
return 0
fi
while [ $# -gt 0 ]; do
case "$1" in
-*) shift ;;
*) break ;;
esac
done
[ $# -eq 0 ] && return 0 # e.g. `sudo -v`
env "$@"
}
# ── systemctl(8) ──────────────────────────────────────────────
# Just enough for _existing_display_manager(): only the
# `is-enabled <unit>` verb is exercised. Units listed in
# $MOCK_SYSTEMCTL_ENABLED report "enabled" (exit 0); every
# other unit reports "disabled" (exit 1), matching systemd.
_mock_systemctl() {
case "${1:-}" in
is-enabled)
local unit="${2:-}" cand
for cand in ${MOCK_SYSTEMCTL_ENABLED:-}; do
if [ "$cand" = "$unit" ]; then
printf 'enabled\n'
return 0
fi
done
printf 'disabled\n'
return 1
;;
esac
return 0
}
# ── whiptail(1) ───────────────────────────────────────────────
# Prints $MOCK_WHIPTAIL_OUTPUT on stdout and exits with
# $MOCK_WHIPTAIL_RC (default 0 = "Yes" for confirm
# dialogs). Not used by the current tests; future-proofs
# the _confirm / _menu / _checklist helpers.
_mock_whiptail() {
printf '%s' "${MOCK_WHIPTAIL_OUTPUT:-}"
return "${MOCK_WHIPTAIL_RC:-0}"
}
# ── Mock installation ─────────────────────────────────────────
# Serializes every _mock_* function into a standalone
# executable under $MOCK_BIN_DIR (shebang + `declare -f`
# output + dispatch line). See the header comment for why
# PATH scripts are required.
_install_mocks() {
local name func
for name in dpkg apt-cache lspci lsusb ip iwgetid timedatectl sudo whiptail systemctl; do
func="_mock_${name//-/_}" # apt-cache → _mock_apt_cache
if ! declare -f "$func" >/dev/null; then
echo "test_helper.bash: mock function ${func} is not defined" >&2
return 1
fi
{
printf '#!/usr/bin/env bash\n'
declare -f "$func"
printf '%s "$@"\n' "$func"
} >"${MOCK_BIN_DIR}/${name}"
chmod +x "${MOCK_BIN_DIR}/${name}"
done
}
# ── Per-test reset ────────────────────────────────────────────
# Restores every fixture to a predictable empty state and
# puts the mock bin dir first on PATH. Call from setup() in
# each .bats file: BATS runs every test in a fresh subshell,
# so PATH changes never leak between tests.
mock_reset() {
# dpkg(1)
export MOCK_DPKG_INSTALLED=""
export MOCK_DPKG_VERSION="1.0.0-mock"
# apt-cache(8)
export MOCK_APT_CACHE_POLICY_OUTPUT=""
export MOCK_APT_CACHE_MADISON_OUTPUT=""
export MOCK_APT_CACHE_SEARCH_OUTPUT=""
# hardware
export MOCK_LSPCI_OUTPUT=""
export MOCK_LSUSB_OUTPUT=""
export MOCK_IP_LINK_OUTPUT=""
export MOCK_IP_ADDR_OUTPUT=""
export MOCK_SSID_OUTPUT=""
# systemd
export MOCK_TIMEZONE_OUTPUT="UTC"
export MOCK_NTP_SYNC_OUTPUT="no"
export MOCK_SYSTEMCTL_ENABLED=""
# dialogs
export MOCK_WHIPTAIL_OUTPUT=""
export MOCK_WHIPTAIL_RC="0"
# CLI flags (debianito.sh parser) — reset so unattended
# mode never leaks between tests.
export AUTO_YES=0
export ALLOW_DESTRUCTIVE=0
# sudo(8)
export MOCK_SUDO_DRY_RUN="0"
export MOCK_SUDO_LOG="$(mktemp)"
# /etc/apt(5) fixture mirror. The modules under test read
# ${APT_DIR:-/etc/apt} (APT_DIR indirection added for
# testability), so tests build fixture trees inside
# $MOCK_APT_ROOT instead of touching the real /etc/apt.
# Fresh per test: fixtures never leak between tests.
export MOCK_APT_ROOT="$(mktemp -d)"
export APT_DIR="${MOCK_APT_ROOT}"
export PATH="${MOCK_BIN_DIR}:${PATH}"
}
# Generate the mock executables once, when this helper is
# loaded.
_install_mocks
-177
View File
@@ -1,177 +0,0 @@
#!/usr/bin/env bash
# utils.bats — Offline tests for modules/utils.sh.
#
# No `set -euo pipefail` here: BATS controls error handling
# per test — any failing command in a test body fails the
# test, and `run` already isolates the commands under test.
#
# `run` executes its command in a SUBSHELL, so global side
# effects would be lost. The state-mutating functions
# (_init_lspci_cache, detect_network) are therefore called
# DIRECTLY and the globals they set (LSPCI_OUTPUT, the
# ETH_* / WIFI_* arrays) are asserted afterwards.
load test_helper.bash
setup() {
mock_reset
load_utils
}
teardown() {
if [ -n "${MOCK_SUDO_LOG:-}" ]; then
rm -f "$MOCK_SUDO_LOG"
fi
if [ -n "${MOCK_APT_ROOT:-}" ]; then
rm -rf "$MOCK_APT_ROOT"
fi
}
# ── Shared fixtures ─────────────────────────────────────
# Hardware view for the detect_network tests: one Intel
# Ethernet controller, one Intel Wi-Fi 6 controller, and
# `ip -o link show` output with lo (UNKNOWN), eth0 (UP)
# and wlan0 (UP, with IPv4 address and SSID).
_setup_detect_network_fixtures() {
MOCK_LSPCI_OUTPUT="00:19.0 Ethernet controller [0200]: Intel Corporation Ethernet Connection (7) I219-LM [8086:1533] (rev 10)
01:00.0 Network controller [0280]: Intel Corporation Wi-Fi 6 AX200 [8086:2725] (rev 1a)"
MOCK_IP_LINK_OUTPUT="1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000"
MOCK_IP_ADDR_OUTPUT="eth0 192.168.1.10/24
wlan0 10.0.0.5/24"
MOCK_SSID_OUTPUT="CasaWiFi"
# Populate the global cache from the lspci mock
# (exercises the timeout-wrapped PATH mock chain).
LSPCI_OUTPUT=""
_init_lspci_cache
}
# ── is_installed ────────────────────────────────────────
@test "is_installed returns 0 for an installed package" {
MOCK_DPKG_INSTALLED="curl wget"
run is_installed curl
[ "$status" -eq 0 ]
}
@test "is_installed returns 1 for a missing package" {
MOCK_DPKG_INSTALLED="curl wget"
run is_installed vim
[ "$status" -eq 1 ]
}
# ── _get_pkg_version ────────────────────────────────────
@test "_get_pkg_version parses the candidate from policy line 3" {
MOCK_APT_CACHE_POLICY_OUTPUT="curl:
Installed: 1.2.3-1
Candidate: 1.2.3"
run _get_pkg_version curl
[ "$status" -eq 0 ]
[ "$output" = "1.2.3" ]
}
# ── _get_backports_version ──────────────────────────────
@test "_get_backports_version returns the backports version" {
MOCK_APT_CACHE_MADISON_OUTPUT=" curl | 1.2.3-1 | https://deb.debian.org/debian bookworm/main amd64 Packages
curl | 1.2.3-1~bpo12+1 | https://deb.debian.org/debian bookworm-backports/main amd64 Packages"
run _get_backports_version curl bookworm
[ "$status" -eq 0 ]
[ "$output" = "1.2.3-1~bpo12+1" ]
}
@test "_get_backports_version defaults the codename to DEBIAN_CODENAME" {
MOCK_APT_CACHE_MADISON_OUTPUT=" curl | 1.2.3-1~bpo12+1 | https://deb.debian.org/debian bookworm-backports/main amd64 Packages"
run _get_backports_version curl
[ "$output" = "1.2.3-1~bpo12+1" ]
}
@test "_get_backports_version prints nothing without a backports entry" {
MOCK_APT_CACHE_MADISON_OUTPUT=" curl | 1.2.3-1 | https://deb.debian.org/debian bookworm/main amd64 Packages"
run _get_backports_version curl bookworm
[ "$status" -eq 0 ]
[ -z "$output" ]
}
# ── _state ──────────────────────────────────────────────
@test "_state prints ON for an installed package" {
MOCK_DPKG_INSTALLED="curl"
run _state curl
[ "$output" = "ON" ]
}
@test "_state prints OFF for a missing package" {
MOCK_DPKG_INSTALLED=""
run _state curl
[ "$output" = "OFF" ]
}
# ── detect_network ──────────────────────────────────────
@test "detect_network fills ETH_NAMES and WIFI_NAMES from mocked hardware" {
_setup_detect_network_fixtures
detect_network
# Ethernet (PCI) interface
[ "${#ETH_NAMES[@]}" -eq 1 ]
[ "${ETH_NAMES[0]}" = "eth0" ]
[ "${ETH_STATES[0]}" = "UP" ]
[ "${ETH_IPS[0]}" = "192.168.1.10/24" ]
[ "$ETH_DESC" = "Intel Corporation Ethernet Connection (7) I219-LM" ]
# WiFi (PCI) interface
[ "${#WIFI_NAMES[@]}" -eq 1 ]
[ "${WIFI_NAMES[0]}" = "wlan0" ]
[ "${WIFI_STATES[0]}" = "UP" ]
[ "${WIFI_IPS[0]}" = "10.0.0.5/24" ]
[ "${WIFI_SSIDS[0]}" = "CasaWiFi" ]
[ "$WIFI_DESC" = "Intel Corporation Wi-Fi 6 AX200" ]
}
@test "detect_network does not duplicate arrays when called twice (BH-013)" {
_setup_detect_network_fixtures
detect_network
local eth_count=${#ETH_NAMES[@]}
local wifi_count=${#WIFI_NAMES[@]}
detect_network
# BH-013: the arrays must be reset and refilled, never appended to.
[ "${#ETH_NAMES[@]}" -eq "$eth_count" ]
[ "${#WIFI_NAMES[@]}" -eq "$wifi_count" ]
[ "${#ETH_NAMES[@]}" -eq 1 ]
[ "${#WIFI_NAMES[@]}" -eq 1 ]
[ "${ETH_NAMES[0]}" = "eth0" ]
[ "${WIFI_NAMES[0]}" = "wlan0" ]
}
# ── CLI flags: --yes / --allow-destructive ──────────────
# debianito.sh (the parser) is never sourced in tests:
# mock_reset seeds the flag globals and the readers under
# test (_confirm, _branch_migration) are exercised directly.
@test "_confirm auto-accepts when AUTO_YES=1 (no whiptail call)" {
export AUTO_YES=1
# Shadow the PATH mock to count invocations: the gate
# must return before whiptail is ever executed.
WHIPTAIL_CALLS=0
whiptail() { WHIPTAIL_CALLS=$((WHIPTAIL_CALLS + 1)); return 1; }
_confirm "Title" "text"
[ "$WHIPTAIL_CALLS" -eq 0 ]
}
@test "_confirm still prompts when AUTO_YES=0" {
export AUTO_YES=0
export MOCK_WHIPTAIL_RC=1 # user answers "No"
run _confirm "Title" "text"
[ "$status" -eq 1 ]
}
@test "_branch_migration refuses unattended mode without --allow-destructive" {
source "${TEST_PROJECT_ROOT}/modules/repos/migrate.sh"
export AUTO_YES=1
export ALLOW_DESTRUCTIVE=0
run _branch_migration
[ "$status" -eq 1 ]
[[ "$output" == *"destructive"* ]]
}