Compare commits
22 Commits
main
..
72032f2916
| Author | SHA1 | Date | |
|---|---|---|---|
| 72032f2916 | |||
| af8fac4ac4 | |||
| 3d1738ddc0 | |||
| eec16d0c83 | |||
| 43ae3e55a4 | |||
| 194f021ad0 | |||
| 18d088f6c7 | |||
| 771558a287 | |||
| d6410ee6aa | |||
| bf6bcdf65e | |||
| 4195d6f444 | |||
| 29f8d56548 | |||
| e3978c5a4f | |||
| f8b77e7d9b | |||
| f5c431f063 | |||
| b2e83321e2 | |||
| 364a956488 | |||
| c2e26b7f32 | |||
| 17c7514896 | |||
| df68160728 | |||
| 3178cf98ab | |||
| 887ec4de25 |
@@ -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 +0,0 @@
|
|||||||
tests/.tmp/
|
|
||||||
@@ -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
|
If the program does terminal interaction, make it output a short
|
||||||
notice like this when it starts in an interactive mode:
|
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 program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
This is free software, and you are welcome to redistribute it
|
This is free software, and you are welcome to redistribute it
|
||||||
under certain conditions; type `show c' for details.
|
under certain conditions; type `show c' for details.
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
<h1 align="center">Debianito - Post-Installation Automation for Debian</h1>
|
# Debianito - Post-Installation Automation for Debian
|
||||||
|
|
||||||
<div align="center">
|
<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 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.
|
||||||
|
|
||||||
[](https://github.com/stornic56/debianito-post-install/blob/main/LICENSE)
|
[](https://github.com/stornic56/debianito-post-install/blob/main/LICENSE)
|
||||||

|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -15,8 +14,8 @@ Debianito is a post-installation automation script for Debian 11 (Bullseye), Deb
|
|||||||
|
|
||||||
| Requirement | Specification |
|
| Requirement | Specification |
|
||||||
|-------------|---------------|
|
|-------------|---------------|
|
||||||
| **OS** | Debian 11 (Bullseye), Debian 12 (Bookworm), Debian 13 (Trixie) |
|
| **OS** | Debian 12 (Bookworm), Debian 13 (Trixie) |
|
||||||
| **Privileges** | Normal user with `sudo` access, script validates root/sudo in utils.sh |
|
| **Privileges** | Normal user with `sudo` access, script validates root/sudo in `utils.sh`|
|
||||||
| **Terminal** | Any modern terminal emulator supporting ANSI colors and UTF-8 box-drawing characters |
|
| **Terminal** | Any modern terminal emulator supporting ANSI colors and UTF-8 box-drawing characters |
|
||||||
| **Dependencies** | Standard Debian packages (`whiptail`, `lsb-release`); auto-installed if missing |
|
| **Dependencies** | Standard Debian packages (`whiptail`, `lsb-release`); auto-installed if missing |
|
||||||
|
|
||||||
@@ -36,244 +35,99 @@ chmod +x debianito.sh && ./debianito.sh
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Command-Line Usage
|
## 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
|
|
||||||
|
|
||||||
After running the script:
|
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.
|
2. **Confirm Actions:** Installation prompts use `whiptail` for TUI confirmations.
|
||||||
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. **Review System Info:** Header displays detected Debian version `DEBIAN_CODENAME`, `DEBIAN_VERSION` and hardware summary before each action.
|
||||||
4. **Review System Info:** Header displays detected Debian version and hardware summary before each action.
|
4. **Repeat as Needed:** Return to main menu at any time or exit when done.
|
||||||
5. **Repeat as Needed:** Return to main menu at any time or exit when done.
|
|
||||||
|
|
||||||
| Option | Description | What it does |
|
| Option | Description | What it does |
|
||||||
| -------- | ------------- | -------------- |
|
|--------|-------------|--------------|
|
||||||
| **1** | [System Info](/docs/system_info.md) | Show detected OS, CPU, RAM, GPU and hardware details |
|
| **1** | System Info | Show detected Os, CPU, RAM, GPU and others |
|
||||||
| **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 |
|
| **2** | User Privileges & Feedback | Add user to sudo group, enable `pwfeedback` for password visibility |
|
||||||
| **3** | [System Preferences](/docs/system_prefs.md) | Configure date/time & timezone, language/locales & keyboard layout, and audio stack (PipeWire, ALSA, PulseAudio) |
|
| **3** | Configure Repositories | Setup official repos with/without backports (deb822 or classic format)|
|
||||||
| **4** | [Configure Repositories](/docs/repos_config.md) | Setup official repos with non-free/contrib options, optional Backports support and Deb822/classic format injection |
|
| **4** | Setup Wireless & Firmware | Install WiFi firmware for Broadcom, Intel, and other chipsets|
|
||||||
| **5** | [Firmware, Wireless & Bluetooth](/docs/firmware.md) | Install essential firmware for GPUs and wireless; configure Bluetooth stack (bluez, bluedevil/blueman) |
|
| **5** | Configure Graphics Stack | AMD/Intel/NVIDIA drivers + monitoring tools |
|
||||||
| **6** | [Graphics Drivers & Mesa Stack](/docs/gpu.md) | Configure AMD/Intel/NVIDIA drivers and Mesa graphics stack + monitoring tools |
|
| **6** | Update Kernel to Backports | Install latest stable kernel from Debian backports|
|
||||||
| **7** | [Kernel](/docs/kernel.md) | Install kernel variants: Stable, RT, Cloud, or Backports (Debian 13) |
|
| **7** | Gaming Setup and Performance | Steam, Heroic Games Launcher, GameMode, MangoHud and others|
|
||||||
| **8** | [Gaming Setup](/docs/gaming.md) | Steam, Heroic Games Launcher, [RetroArch](/docs/retroarch.md), GameMode, MangoHud, OpenRGB, Java JRE (Temurin 8/17/21/25) |
|
| **8** | Install ZRAM (compressed swap) | Configure compressed RAM for memory optimization|
|
||||||
| **9** | [ZRAM](/docs/zram.md) | Configure compressed RAM for memory optimization |
|
| **9** | Install Programs and Software | Selection across 10 categories (Dev, Themes, System, etc.) |
|
||||||
| **10** | [Swap Management](/docs/swap.md) | Manage swap file or partition size and enable/disable swap space for system stability |
|
| **10** | Exit | Return to terminal |
|
||||||
| **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 |
|
|
||||||
|
|
||||||
### Install Programs and Software (Option 11)
|
### Extra Applications Categories (Option 9)
|
||||||
|
|
||||||
The submenu offers these categories:
|
The submenu offers 11 categories:
|
||||||
|
|
||||||
| Option | Category Title | Description |
|
| Option | Category | 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 |
|
| **1** | Customization System | Desktop themes, icon themes, cursor themes, and fonts|
|
||||||
| **2** | Download & Network | Downloaders (aria2, ytdlp, FileZilla) + Torrent clients (qBittorrent, Deluge, Transmission) |
|
| **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) |
|
| **3** | Internet | Browsers (Firefox/Mozilla, LibreWolf, Floorp, Chromium), email clients, VPN tools|
|
||||||
| **4** | Communication | Signal, Telegram, HexChat: chat and messaging clients (GUI only) |
|
| **4** | Media Players | VLC and MPV|
|
||||||
| **5** | Media Players | Multimedia playback with VLC media player and MPV for advanced video/audio support |
|
| **5** | Multimedia & Design | GIMP, Kdenlive, Blender, OBS Studio, Audacity, Inkscape and HandBrake|
|
||||||
| **6** | Multimedia & Design | Image editing (GIMP), video editing (Kdenlive, HandBrake), 3D modeling (Blender), audio recording (Audacity), and graphics design (Inkscape) |
|
| **6** | Programming Applications | Development tools (Docker, Nginx/Apache, PostgreSQL/MariaDB, SSH, fail2ban, Python)|
|
||||||
| **7** | Code Editors & IDEs | vim, vim-gtk3, Neovim, Helix, nano, Emacs, Kate, Mousepad, Gedit, Geany, GNOME Text Editor, and VSCodium (VS Code open-source) |
|
| **7** | Security & Networking | Additional security and networking utilities |
|
||||||
| **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 |
|
| **8** | System Tools | htop, btop, ncdu, timeshift, tmux, flatpak, extrepo and virtualization tools|
|
||||||
| **9** | Security & Networking | Wireshark, tcpdump, Zenmap, ClamAV, UFW, fail2ban |
|
| **9** | Fetch / System Info | Fastfetch/Neofetch, hyfetch, Linux logo and Screenfetch |
|
||||||
| **10** | Software Center & Flatpak | GNOME Software / KDE Discover and Flatpak support |
|
| **10** | Back to main menu | Return to the main script menu |
|
||||||
| **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) |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Quick Start Guide
|
|
||||||
|
|
||||||
For a full setup in ~15-20 minutes, refer to the [Quick Start Visual Guide](/docs/quickstart.md). It provides:
|
|
||||||
|
|
||||||
- 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.)
|
|
||||||
|
|
||||||
**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
|
## File Structure
|
||||||
|
|
||||||
| Directory/File | Description |
|
| Directory/File | Description |
|
||||||
| ---------------- | ------------- |
|
|----------------|-------------|
|
||||||
| `debianito.sh` | Main entry point; handles menu navigation and system detection. |
|
| `./debianito.sh` | Main entry point; handles menu navigation and system detection. |
|
||||||
| `docs/` | Documentation directory containing Markdown files for each module. |
|
| `/modules/` | Modular scripts for specific tasks: sudo config, repos, firmware, gpu, kernel, gaming, zram. |
|
||||||
| `modules/` | Core modular scripts organized by category: repos, gpu, gaming, kernel, firmware, zram, etc. |
|
| `/modules/extras/` | Split-by-category sub-modules for the "Install Programs and Software" menu (Option 9). |
|
||||||
| `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
|
```bash
|
||||||
├── .github
|
├── debianito.sh # Main entry point; TUI menu + system detection
|
||||||
│ └── workflows
|
└── modules/
|
||||||
│ └── ci.yml
|
├── utils.sh # System info helpers: CPU/RAM/GPU/WiFi detection, Debian version check ([`detect_debian_version()`](utils.sh#L79))
|
||||||
├── .gitignore
|
├── sudo_config.sh # User group + pwfeedback setup
|
||||||
├── debianito.sh
|
├── repos.sh # Repository configuration (deb822/classic format) with backup/restore
|
||||||
├── docs
|
├── firmware.sh # WiFi microcode and chipset-specific support via [`firmware-linux-nonfree`](modules/firmware.sh)
|
||||||
│ ├── boot.md
|
├── gpu.sh # AMD/Intel/NVIDIA driver handling ([`detect_gpu()`](utils.sh#L168)); NVIDIA Kepler/Turing detection veto (line 203+)
|
||||||
│ ├── desktops_display.md
|
├── kernel.sh # Update Kernel to Backports with NVIDIA compatibility warnings
|
||||||
│ ├── firmware.md
|
├── gaming.sh # Steam, GameMode, MangoHud, Heroic Games Launcher
|
||||||
│ ├── gaming.md
|
├── extras.sh # Dispatcher for the "Extras" submenu (Option 9)
|
||||||
│ ├── gpu.md
|
├── zram.sh # ZRAM compressed swap configuration
|
||||||
│ ├── kernel.md
|
└── extras/ # Category modules organized by function
|
||||||
│ ├── quickstart.md
|
├── _helpers.sh # Shared helpers: [`_inst()`](utils.sh#L418), [`_state()`] for package management
|
||||||
│ ├── repos_config.md
|
├── essential/ # Essential Pack (compression, system info, VLC, MS fonts)
|
||||||
│ ├── retroarch.md
|
│ └── essential.sh # One-click essentials installation
|
||||||
│ ├── swap.md
|
├── system/ # System Tools (htop, btop, ncdu, timeshift, tmux, flatpak, extrepo, virtualization tools)
|
||||||
│ ├── system_info.md
|
│ └── system.sh # 25+ system utility packages
|
||||||
│ ├── system_prefs.md
|
├── dev/ # Development & Servers (Docker, Nginx/Apache, PostgreSQL/MariaDB, SSH, fail2ban, Python)
|
||||||
│ ├── user_priv_feed.md
|
│ └── dev.sh # 15 development/server tools
|
||||||
│ └── zram.md
|
├── players/ # Media Players (VLC, MPV)
|
||||||
├── media
|
│ └── players.sh # Video player packages
|
||||||
│ ├── gift
|
├── internet/ # Internet: Firefox (Mozilla), LibreWolf, Floorp, Chromium, Thunderbird, Riseup VPN, Tor Browser
|
||||||
│ │ └── script.gif
|
│ └── internet.sh # Browsers, email clients, VPN tools
|
||||||
│ └── screenshots
|
├── themes/ # Customization submenu with 4 options
|
||||||
│ ├── 01-system-info.png
|
│ ├── themes.sh # Submenu dispatcher for customization categories
|
||||||
│ ├── 02b-pwfeedback.png
|
│ ├── desktop-themes/ # Desktop Themes (GTK/KDE): Arc, Numix, Breeze GTK, Bluebird, Blackbird, Greybird, Orchis
|
||||||
│ ├── 02-user-privileges.png
|
│ │ └── desktop-themes.sh
|
||||||
│ ├── 03-system-prefs.png
|
│ ├── icons/ # Icon Themes: Papirus, Numix, Elementary, Deepin, Suru, Obsidian, Breeze, Moka
|
||||||
│ ├── 04b-backports.png
|
│ │ └── icons.sh # 11-12 icon theme packages
|
||||||
│ ├── 04-repos.png
|
│ ├── cursors/ # Cursor Themes: Bibata, Breeze, Chameleon, DMZ, XCursor, Oxygen
|
||||||
│ ├── 05-firmware-plan.png
|
│ │ └── cursors.sh # 6 cursor theme packages
|
||||||
│ ├── 06-gpu-choice.png
|
│ └── fonts/ # Fonts: Bebas Neue, Anonymous Pro, ADF Verana, 3270, Liberation, MS Core, Ubuntu, Recommended
|
||||||
│ ├── 07-kernel.png
|
│ └── fonts.sh # 8 font packages including MS Core fonts
|
||||||
│ ├── 08-gaming.png
|
├── fetch/ # Fetch Tools (Neofetch/Fastfetch, hyfetch, Linux logo, Screenfetch)
|
||||||
│ ├── 09b-zram-status.png
|
│ └── fetch.sh # System info display tools
|
||||||
│ ├── 09-zram-algo.png
|
├── download/ # Downloaders & Torrent Clients: aria2, ytdlp, FileZilla, qBittorrent, Deluge, Transmission, mktorrent
|
||||||
│ ├── 10b-swap-status.png
|
│ └── download.sh # Download manager + torrent client packages
|
||||||
│ ├── 10-swap.png
|
└── design/ # Multimedia & Design (GIMP, Kdenlive, Blender, OBS Studio, Audacity, Inkscape, HandBrake)
|
||||||
│ ├── 11b-essential-pack.png
|
└── design.sh # 12+ creative applications
|
||||||
│ └── 11-programs.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
|
|
||||||
│ │ ├── communication
|
|
||||||
│ │ │ └── communication.sh
|
|
||||||
│ │ ├── design
|
|
||||||
│ │ │ └── design.sh
|
|
||||||
│ │ ├── dev
|
|
||||||
│ │ │ ├── dev.sh
|
|
||||||
│ │ │ └── jellyfin.sh
|
|
||||||
│ │ ├── download
|
|
||||||
│ │ │ └── download.sh
|
|
||||||
│ │ ├── essential
|
|
||||||
│ │ │ └── essential.sh
|
|
||||||
│ │ ├── fetch
|
|
||||||
│ │ │ └── fetch.sh
|
|
||||||
│ │ ├── _helpers.sh
|
|
||||||
│ │ ├── internet
|
|
||||||
│ │ │ └── internet.sh
|
|
||||||
│ │ ├── java.sh
|
|
||||||
│ │ ├── office
|
|
||||||
│ │ │ └── office.sh
|
|
||||||
│ │ ├── players
|
|
||||||
│ │ │ └── players.sh
|
|
||||||
│ │ ├── programming
|
|
||||||
│ │ │ └── programming.sh
|
|
||||||
│ │ ├── security
|
|
||||||
│ │ │ └── security.sh
|
|
||||||
│ │ ├── system
|
|
||||||
│ │ │ ├── software_centers.sh
|
|
||||||
│ │ │ └── system.sh
|
|
||||||
│ │ ├── terminals
|
|
||||||
│ │ │ └── terminals.sh
|
|
||||||
│ │ └── themes
|
|
||||||
│ │ ├── cursors
|
|
||||||
│ │ │ └── cursors.sh
|
|
||||||
│ │ ├── desktop-themes
|
|
||||||
│ │ │ └── desktop-themes.sh
|
|
||||||
│ │ ├── fonts
|
|
||||||
│ │ │ └── fonts.sh
|
|
||||||
│ │ ├── icons
|
|
||||||
│ │ │ └── icons.sh
|
|
||||||
│ │ └── themes.sh
|
|
||||||
│ ├── extras.sh
|
|
||||||
│ ├── firmware.sh
|
|
||||||
│ ├── gaming
|
|
||||||
│ │ ├── _helpers.sh
|
|
||||||
│ │ ├── heroic.sh
|
|
||||||
│ │ ├── steam.sh
|
|
||||||
│ │ └── tools.sh
|
|
||||||
│ ├── gaming.sh
|
|
||||||
│ ├── gpu
|
|
||||||
│ │ ├── amd_intel.sh
|
|
||||||
│ │ ├── _helpers.sh
|
|
||||||
│ │ ├── nvidia_manage.sh
|
|
||||||
│ │ └── nvidia.sh
|
|
||||||
│ ├── gpu.sh
|
|
||||||
│ ├── hw_detect.sh
|
|
||||||
│ ├── kernel.sh
|
|
||||||
│ ├── repos
|
|
||||||
│ │ ├── migrate.sh
|
|
||||||
│ │ └── repo_detect.sh
|
|
||||||
│ ├── repos.sh
|
|
||||||
│ ├── rescue.sh
|
|
||||||
│ ├── 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
|
|
||||||
└── README.md
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
> 🤖 **AI-Assisted Development Note**
|
> 🤖 **AI-Assisted Development Note**
|
||||||
> This project was developed with assistance from large language models for code generation, documentation and testing suggestions. The author takes full responsibility for the accuracy of all scripts included in this repository. All modifications have been reviewed manually before inclusion to ensure compatibility with Debian systems.
|
> This project was developed with assistance from large language models for code generation, documentation and testing suggestions. The author takes full responsibility for the accuracy of all scripts included in this repository. All modifications have been reviewed manually before inclusion to ensure compatibility with Debian systems.
|
||||||
|
> 🤖 AI-Assisted Development Note
|
||||||
|
This project was developed with assistance from large language models for code generation, documentation and testing suggestions. The author takes full responsibility for the accuracy of all scripts included in this repository. All modifications have been reviewed manually before inclusion to ensure compatibility with Debian systems.
|
||||||
|
|||||||
@@ -2,14 +2,10 @@
|
|||||||
# Debianito — simple configurator script
|
# Debianito — simple configurator script
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Colour palette shared with every lazily-loaded module:
|
RED='\033[0;31m'
|
||||||
# exported so shellcheck sees them as cross-file globals
|
GREEN='\033[0;32m'
|
||||||
# (SC2034) instead of unused locals.
|
YELLOW='\033[1;33m'
|
||||||
export RED='\033[0;31m'
|
NC='\033[0m'
|
||||||
export GREEN='\033[0;32m'
|
|
||||||
export YELLOW='\033[1;33m'
|
|
||||||
export CYAN='\033[0;36m'
|
|
||||||
export NC='\033[0m'
|
|
||||||
|
|
||||||
# TUI dimensions — fixed centered size for whiptail dialogs
|
# TUI dimensions — fixed centered size for whiptail dialogs
|
||||||
TUI_ALTO=20
|
TUI_ALTO=20
|
||||||
@@ -19,269 +15,93 @@ TUI_ALTO_LISTA=10
|
|||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
MODULES_DIR="${SCRIPT_DIR}/modules"
|
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}/utils.sh"
|
||||||
source "${MODULES_DIR}/hw_detect.sh"
|
source "${MODULES_DIR}/sudo_config.sh"
|
||||||
|
source "${MODULES_DIR}/repos.sh"
|
||||||
# ── Bullseye-specific modules (loaded only on Debian 11) ──
|
[ -f "${MODULES_DIR}/firmware.sh" ] && source "${MODULES_DIR}/firmware.sh"
|
||||||
# Eager-conditional: the menu dispatch branches on
|
[ -f "${MODULES_DIR}/gpu.sh" ] && source "${MODULES_DIR}/gpu.sh"
|
||||||
# `type install_*_bullseye`, so these must be defined
|
[ -f "${MODULES_DIR}/kernel.sh" ] && source "${MODULES_DIR}/kernel.sh"
|
||||||
# before main_menu() runs.
|
[ -f "${MODULES_DIR}/gaming.sh" ] && source "${MODULES_DIR}/gaming.sh"
|
||||||
if [ -d "${MODULES_DIR}/bullseye" ]; then
|
[ -f "${MODULES_DIR}/extras.sh" ] && source "${MODULES_DIR}/extras.sh"
|
||||||
[ -f "${MODULES_DIR}/bullseye/legacy.sh" ] && source "${MODULES_DIR}/bullseye/legacy.sh"
|
[ -f "${MODULES_DIR}/zram.sh" ] && source "${MODULES_DIR}/zram.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=""
|
DEBIAN_VERSION=""
|
||||||
export DEBIAN_CODENAME=""
|
DEBIAN_CODENAME=""
|
||||||
|
|
||||||
main_menu() {
|
main_menu() {
|
||||||
# Auto-adjust TUI dimensions for small terminals
|
# Auto-adjust TUI dimensions for small terminals
|
||||||
if [ "${LINES:-24}" -lt $((TUI_ALTO + 6)) ] || [ "${COLUMNS:-80}" -lt $((TUI_ANCHO + 6)) ]; then
|
if [ "$LINES" -lt $((TUI_ALTO + 6)) ] || [ "$COLUMNS" -lt $((TUI_ANCHO + 6)) ]; then
|
||||||
TUI_ALTO=$((${LINES:-24} - 4 > 8 ? ${LINES:-24} - 4 : 8))
|
TUI_ALTO=$((LINES - 4 > 8 ? LINES - 4 : 8))
|
||||||
TUI_ANCHO=$((${COLUMNS:-80} - 4 > 50 ? ${COLUMNS:-80} - 4 : 50))
|
TUI_ANCHO=$((COLUMNS - 4 > 50 ? COLUMNS - 4 : 50))
|
||||||
TUI_ALTO_LISTA=$((TUI_ALTO - 10 > 4 ? TUI_ALTO - 10 : 4))
|
TUI_ALTO_LISTA=$((TUI_ALTO - 10 > 4 ? TUI_ALTO - 10 : 4))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
sudo -v >/dev/null 2>&1 || true
|
|
||||||
local STATE_REFRESHED=false
|
|
||||||
local choice
|
local choice
|
||||||
choice=$(_menu "DEBIANITO — Simple Configurator Script" "" \
|
choice=$(whiptail --title "DEBIANITO — simple configurator script" --menu "" \
|
||||||
$TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
$TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
||||||
"1" "System Information" \
|
"1" "System Info" \
|
||||||
"2" "User Privileges & Feedback" \
|
"2" "User Privileges & Feedback" \
|
||||||
"3" "System Preferences" \
|
"3" "Configure repositories" \
|
||||||
"4" "Configure Repositories" \
|
"4" "Wireless & Firmware" \
|
||||||
"5" "Firmware, Wireless & Bluetooth" \
|
"5" "Graphics Stack" \
|
||||||
"6" "Graphics Drivers & Mesa Stack" \
|
"6" "Backports Kernel" \
|
||||||
"7" "Kernel" \
|
"7" "Gaming Setup" \
|
||||||
"8" "Gaming Setup" \
|
"8" "ZRAM (Swap)" \
|
||||||
"9" "ZRAM" \
|
"9" "Install Programs and Software" \
|
||||||
"10" "Swap Management" \
|
"10" "Exit" \
|
||||||
"11" "Install Programs and Software" \
|
3>&1 1>&2 2>&3)
|
||||||
"12" "Boot Rescue + GRUB" \
|
|
||||||
"13" "Desktop & Display" \
|
|
||||||
"14" "Exit")
|
|
||||||
|
|
||||||
clear
|
clear
|
||||||
|
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
1)
|
1) _show_sysinfo ;;
|
||||||
_load_module sysinfo || continue
|
2) config_sudo || true ;;
|
||||||
_show_sysinfo
|
3) configure_repos || true ;;
|
||||||
;;
|
4) install_firmware || true ;;
|
||||||
2)
|
5) install_gpu_drivers || true ;;
|
||||||
_load_module sudo_config || continue
|
6) install_kernel_backports || true ;;
|
||||||
config_sudo || true
|
7) install_gaming || true ;;
|
||||||
;;
|
8) install_zram || true ;;
|
||||||
3)
|
9) install_extras || true ;;
|
||||||
_load_module system_prefs || continue
|
10) echo "Exiting."; exit 0 ;;
|
||||||
_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
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
if $STATE_REFRESHED; then
|
|
||||||
refresh_system_state
|
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
_boot_mark "boot: pre-flight checks"
|
_show_sysinfo() {
|
||||||
|
local gpu_info="${GPU_DESC}"
|
||||||
|
[ -n "$GPU_VERSION" ] && gpu_info+=" (${GPU_VERSION})"
|
||||||
|
|
||||||
|
local network_info
|
||||||
|
if [ -n "$ETH_DESC" ] && [ -n "$WIFI_DESC" ]; then
|
||||||
|
network_info="Ethernet: ${ETH_DESC}\nWiFi: ${WIFI_DESC}"
|
||||||
|
elif [ -n "$ETH_DESC" ]; then
|
||||||
|
network_info="${ETH_DESC}"
|
||||||
|
elif [ -n "$WIFI_DESC" ]; then
|
||||||
|
network_info="${WIFI_DESC}"
|
||||||
|
else
|
||||||
|
network_info="No adapters detected"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_msg "System Information" \
|
||||||
|
"Debian: ${DEBIAN_VERSION} (${DEBIAN_CODENAME})
|
||||||
|
Kernel: ${KERNEL_VERSION}
|
||||||
|
CPU: $(get_cpu_summary)
|
||||||
|
RAM: $(get_ram_summary)
|
||||||
|
GPU: ${gpu_info}
|
||||||
|
Network: ${network_info}" 13 65
|
||||||
|
}
|
||||||
|
|
||||||
check_root
|
check_root
|
||||||
check_sudo
|
check_sudo
|
||||||
if ! command -v whiptail >/dev/null 2>&1; then
|
check_system_time
|
||||||
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
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 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_debian_version
|
||||||
|
detect_cpu_ram
|
||||||
|
detect_kernel
|
||||||
|
detect_gpu
|
||||||
|
detect_network
|
||||||
|
|
||||||
# ── 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
|
main_menu
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
# Option 12: Boot Rescue + GRUB
|
|
||||||
|
|
||||||
### 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.
|
|
||||||
|
|
||||||
### 2. Logical Execution Flow
|
|
||||||
|
|
||||||
**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).
|
|
||||||
3. Show menu for 5 seconds (Default behavior).
|
|
||||||
4. Custom timeout (User inputs specific seconds; -1 allows indefinite wait).
|
|
||||||
- **Variable Application:** Each selection updates three core variables: `GRUB_TIMEOUT`, `GRUB_TIMEOUT_STYLE`, and `GRUB_RECORDFAIL_TIMEOUT`. The custom option also allows setting `GRUB_DISABLE_OS_PROBER` to prevent extra entries.
|
|
||||||
- **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**
|
|
||||||
|
|
||||||
- **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).
|
|
||||||
- **Skip Logic:** If either check fails, the process stops immediately with a message indicating no repair is needed or applicable.
|
|
||||||
- **Repair Steps:** Upon user confirmation:
|
|
||||||
1. Reinstalls signed boot packages (`shim-signed`, `grub-efi-amd64-signed`, `linux-image-amd64`).
|
|
||||||
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**
|
|
||||||
|
|
||||||
- **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.
|
|
||||||
- **Purpose:** This fixes boot issues caused by missing drivers or corrupted initrd images that prevent the system from loading properly.
|
|
||||||
|
|
||||||
### 3. Safeguards and Automation
|
|
||||||
|
|
||||||
- **EFI Directory Detection:** The script 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.
|
|
||||||
- **Override Persistence:** Configuration values are written to `/etc/default/grub.d/99_script_override.cfg`, ensuring that changes survive package updates which might otherwise reset `/etc/default/grub`.
|
|
||||||
- **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
|
|
||||||
|
|
||||||
**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) |
|
|
||||||
|
|
||||||
>**Note UEFI:** The Secure Boot repair ONLY works on UEFI systems. The script checks for `/sys/firmware/efi` and `mokutil --sb-state` before attempting anything. Legacy BIOS systems are automatically skipped.
|
|
||||||
|
|
||||||
>**Note GRUB:** After hiding the GRUB menu (Option 1), you must hold ESC immediately after powering on to access the menu. There is no other way to reach it during boot.
|
|
||||||
|
|
||||||
> **Technical data:** The override file at `/etc/default/grub.d/99_script_override.cfg` persists across kernel updates because GRUB reads drop-in configs from that directory. This is more reliable than editing `/etc/default/grub` directly for values that should survive updates.
|
|
||||||
|
|
||||||
> **Technical data:** `GRUB_DISABLE_OS_PROBER=true` is set when hiding the menu to prevent OS prober from adding extra entries that would require the menu to appear. For multi-boot systems, use Option 3 or Custom instead.
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
# Option 13: 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`.
|
|
||||||
|
|
||||||
### 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).
|
|
||||||
- **Available Managers:**
|
|
||||||
- LightDM (available on all Debian versions).
|
|
||||||
- GDM3 (available on all Debian versions).
|
|
||||||
- SDDM (available on all Debian versions).
|
|
||||||
- greetd (available only on Debian 12 and 13).
|
|
||||||
- Each selected manager opens its own dedicated configuration submenu.
|
|
||||||
|
|
||||||
**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**
|
|
||||||
|
|
||||||
- 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).
|
|
||||||
3. Configure autologin (prompts for a username via whiptail inputbox to set in daemon.conf).
|
|
||||||
4. Force Enable Wayland on NVIDIA (Bookworm/Trixie + NVIDIA only).
|
|
||||||
- **Mechanism:** Creates or removes a symlink: `/etc/udev/rules.d/61-gdm.rules` → `/dev/null`.
|
|
||||||
- **Purpose:** This disables the udev rule that blocks GDM from using Wayland on NVIDIA GPUs.
|
|
||||||
- **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**
|
|
||||||
|
|
||||||
- A submenu offers two options:
|
|
||||||
1. Install SDDM (auto-selects via debconf).
|
|
||||||
2. Enable Autologin:
|
|
||||||
- **Session Detection:** Auto-detects the installed session by checking `.desktop` files in priority order: `plasmawayland` → `lxqt-wayland` → `plasma` → `lxqt`.
|
|
||||||
- **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**
|
|
||||||
|
|
||||||
- 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).
|
|
||||||
- **Debian 13 only:** Additional options to install `gtk-greet`, `nwg-hello`, or `wlgreet`.
|
|
||||||
- **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:
|
|
||||||
|
|
||||||
- **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 |
|
|
||||||
| greetd (base) | greetd | 12, 13 |
|
|
||||||
| greetd + tuigreet | greetd, tuigreet | 12 (backports), 13 |
|
|
||||||
| greetd + gtk-greet | greetd, gtk-greet | 13 only |
|
|
||||||
| 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) |
|
|
||||||
| GDM3 | /etc/gdm3/daemon.conf | Autologin user and enable flag |
|
|
||||||
| GDM3 | /etc/udev/rules.d/61-gdm.rules | Wayland override (symlink to /dev/null) |
|
|
||||||
| 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 |
|
|
||||||
| 4 | /usr/share/xsessions/lxqt.desktop | LXQt X11 |
|
|
||||||
|
|
||||||
> ⚠️ **Warning: Wayland on NVIDIA:** The "Force Enable Wayland on NVIDIA" option creates a symlink that disables the GDM udev rule. This is a known workaround but MAY cause graphics instability or breakage. To revert, remove `/etc/udev/rules.d/61-gdm.rules`.
|
|
||||||
|
|
||||||
> ⚠️ **Warning: greetd:** Unlike other display managers, greetd is installed but NOT configured by this script. You MUST manually create `/etc/greetd/config.toml` before you can log in. See `man greetd` and `man 5 greetd-sessions` for configuration details.
|
|
||||||
|
|
||||||
> ⚠️ **Warning: Autologin:** All autologin configurations use `SUDO_USER` (the user who ran sudo) instead of `$USER` (which is root during script execution). This ensures the correct user is set for automatic login.
|
|
||||||
|
|
||||||
> 💡 **Technical Note:** `debconf-set-selections` pre-selects the display manager before apt install, preventing the interactive "Configuring shared/default-x-display-manager" dialog that would otherwise pause the script.
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
# Option 5: Firmware, Wireless & Bluetooth
|
|
||||||
|
|
||||||
## 1. What the Debian Package Misses
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## 2. Dual Scan Engine (PCI & USB)
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## 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 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`.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## 4. Installation Execution Flow (Atomic Pipeline)
|
|
||||||
|
|
||||||
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 (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 Wireless Support (DKMS Single-Path)
|
|
||||||
|
|
||||||
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.).
|
|
||||||
|
|
||||||
## 6. Bluetooth Stack Integration
|
|
||||||
|
|
||||||
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 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.
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
# Option 8: 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:
|
|
||||||
|
|
||||||
**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.
|
|
||||||
|
|
||||||
**Stability Over Features:** Unlike gaming-focused distributions that prioritize bleeding-edge kernels or unstable repositories, Debianito maintains the stability of Debian Stable while providing necessary backports for Mesa 32-bit support when available. This ensures long-term compatibility with games and launchers without risking system breakage from aggressive updates.
|
|
||||||
|
|
||||||
The module architecture separates concerns into distinct scripts (`steam.sh`, `heroic.sh`, `tools.sh`) that can be sourced independently, allowing users to enable only the components they need while maintaining a clean, auditable installation history.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 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:
|
|
||||||
|
|
||||||
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:
|
|
||||||
- `debconf` and `cdebconf` for configuration management
|
|
||||||
- `default-dbus-session-bus` for inter-process communication
|
|
||||||
- `lsof` for file listing utilities
|
|
||||||
- `zenity` or `yad` for graphical dialogs
|
|
||||||
- `steam-libs` and `steam-libs-i386` metapackages
|
|
||||||
|
|
||||||
3. **Self-Update Mechanism:** The installed Steam launcher includes a minimal version capable of downloading full updates automatically, reducing manual intervention requirements.
|
|
||||||
|
|
||||||
**Heroic Games Launcher Management:** Unlike Steam's Debian package approach, Heroic requires direct download from GitHub releases due to its rapid iteration cycle. The `install_heroic()` function in `heroic.sh` implements:
|
|
||||||
|
|
||||||
1. **Release Detection:** Uses GitHub API to identify the latest amd64 `.deb` release dynamically
|
|
||||||
2. **Dependency Pre-installation:** Ensures `curl` and `wget` are available before attempting download
|
|
||||||
3. **Temporary File Handling:** Downloads to `/tmp/heroic.deb`, validates file integrity, then installs via apt
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gh_url=$(curl -s --connect-timeout 10 https://api.github.com/repos/Heroic-Games-Launcher/\
|
|
||||||
HeroicGamesLauncher/releases/latest | \
|
|
||||||
grep -oP 'https://[^"]+amd64\.deb' | head -1)
|
|
||||||
```
|
|
||||||
|
|
||||||
This approach ensures users always get the latest stable release while maintaining compatibility with Debian's package verification mechanisms. The script also supports Flatpak alternatives for users who prefer sandboxed installations through Flathub.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 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:
|
|
||||||
|
|
||||||
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)
|
|
||||||
3. **Kernel Governor Tuning:** Forces CPU governor to Performance mode when games are detected, eliminating frequency scaling delays
|
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
This allows users to wrap game launch commands with `gamemoderun %command%` for automatic optimization without manual configuration.
|
|
||||||
|
|
||||||
**MangoHud + goverlay Integration:** MangoHud provides real-time performance telemetry through Vulkan/OpenGL overlays that render directly into the game window:
|
|
||||||
|
|
||||||
1. **FPS Counter:** Displays current and average frames per second
|
|
||||||
2. **Temperature Monitoring:** Tracks CPU/GPU temperatures in real-time
|
|
||||||
3. **Memory Usage:** Shows VRAM, RAM, and swap utilization metrics
|
|
||||||
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..."
|
|
||||||
_run_cmd "MangoHud" "sudo apt install -y mangohud:i386"
|
|
||||||
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.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 4. Peripheral Control and RGB Lighting (OpenRGB)
|
|
||||||
|
|
||||||
**Secure Hardware Access:** OpenRGB requires direct communication with hardware peripherals through the I2C bus, which traditionally required root privileges. The `install_openrgb()` function implements several security measures:
|
|
||||||
|
|
||||||
1. **udev Rule Configuration:** Reloads udev rules to properly identify and manage I2C devices without requiring elevated permissions during runtime
|
|
||||||
2. **Module Loading:** Ensures `i2c-dev` module is loaded and added to `/etc/modules` for persistence across reboots
|
|
||||||
3. **Group Membership:** Adds the user to the `i2c` group via `usermod -aG i2c "$USER"` so OpenRGB can access hardware without root
|
|
||||||
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"
|
|
||||||
elif [ "$DEBIAN_VERSION" = "13" ]; then
|
|
||||||
url="https://codeberg.org/OpenRGB/OpenRGB/releases/download/release_candidate_1.0rc2/openrgb_1.0rc2_amd64_trixie_0fca93e.deb"
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
This approach eliminates the security risk of running OpenRGB as root while maintaining full hardware control for RGB peripherals, RAM modules, and motherboard lighting systems. Users must log out/in or reboot after installation to apply group membership changes.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 5. Java Runtimes (Eclipse Temurin 8 / 17 / 21 / 25)
|
|
||||||
|
|
||||||
**Multi-Version Support:** The gaming module provides four 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:
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
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`).
|
|
||||||
|
|
||||||
### References
|
|
||||||
|
|
||||||
- [https://wiki.debian.org/Steam](https://wiki.debian.org/Steam)
|
|
||||||
- [wiki.archlinux.org/title/Steam](wiki.archlinux.org/title/Steam)
|
|
||||||
- [https://repo.steampowered.com/steam/](https://repo.steampowered.com/steam/)
|
|
||||||
- [https://www.protondb.com/](https://www.protondb.com/)
|
|
||||||
- [https://github.com/lutris/lutris](https://github.com/lutris/lutris)
|
|
||||||
- [https://github.com/lutris/docs/blob/master/InstallingDrivers.md](https://github.com/lutris/docs/blob/master/InstallingDrivers.md)
|
|
||||||
- [https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher](https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher)
|
|
||||||
- [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)
|
|
||||||
@@ -1,290 +0,0 @@
|
|||||||
# Option 6: 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:
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 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.
|
|
||||||
|
|
||||||
**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:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
if [ "$GPU_TYPE" = "unknown" ]; then
|
|
||||||
# Install generic Mesa stack (Safe fallback)
|
|
||||||
install_mesa_generic_stack
|
|
||||||
elif $HAS_INTEL; then
|
|
||||||
# Route to Intel-specific logic (i915/Xe, VAAPI selection)
|
|
||||||
install_intel_firmware && offer_intel_tools
|
|
||||||
elif $HAS_AMD; then
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. **Intel Graphics Hardware**
|
|
||||||
|
|
||||||
| Architecture / Gen | Process Node | iGPU / dGPU | Kernel Driver (KMD) | OpenGL Driver | Vulkan Driver | Notes |
|
|
||||||
| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
|
||||||
| **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** (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). |
|
|
||||||
| **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. |
|
|
||||||
| **Gen11** (Ice Lake) | 10nm | UHD Graphics G1, Iris Plus G4/G7 | `i915` | `iris` | `anv` | First architecture at 10nm. Vulkan 1.3+ support. |
|
|
||||||
| **Gen12** (Xe-LP) (Tiger Lake, Alder Lake, Raptor Lake) | Intel 7 (10nm ESF) | Iris Xe G7, UHD Graphics 770/730 | `i915/xe` | `iris` | `anv` | Renamed to "Iris Xe". Vulkan 1.3 support. The xe module has technical support but i915 remains the standard and more stable for this generation. |
|
|
||||||
| **Xe-LPG** (Meteor Lake) | TSMC N5 | Arc Graphics (8 Xe-Cores) | `i915/xe` | `iris` | `anv` | First tile-based architecture iGPU. |
|
|
||||||
| **Xe2-LPG** (Lunar Lake) | TSMC N3B | Arc Graphics (Xe2-LPG - 8 Xe-Cores) | `i915?/xe` | `iris` | `anv` | First iGPU with Xe2 architecture (Battlemage). |
|
|
||||||
| **Xe3-LPG** (Panther Lake) | Intel 18A | Arc Graphics (Xe3 iGPU) | `i915?/xe` | `iris` | `anv` | High-power iGPU. Requires Kernel 6.19 and Mesa 26 as base. |
|
|
||||||
| **Xe-HPG** (Alchemist) | TSMC N6 | **Arc A380, A580, A750, A770** (dGPU) | `i915/xe` | `iris` | `anv` | First modern dGPU (Arc). Support since Kernel 6 and Mesa 22. |
|
|
||||||
| **Xe2-HPG** (Battlemage) | TSMC N5 | **Arc B570, B580** (dGPU) | [`i915/xe`](https://www.phoronix.com/review/intel-xe-i915-linux-619) | `iris` | `anv` | Second generation dGPU. Very solid day-one Linux support since Kernel 6.12 and Mesa 24.2 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 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.
|
|
||||||
*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`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 4. AMD Radeon Architecture Reference
|
|
||||||
|
|
||||||
AMD's open-source support is divided by architecture families, each mapped to a specific Gallium3D driver within Mesa.
|
|
||||||
|
|
||||||
| Architecture | Representative GPU Families | Kernel Driver (KMD) | OpenGL Driver (Mesa) | Vulkan Driver (Mesa) | Technical Notes & Particularities |
|
|
||||||
| :--- | :--- | :--- | :--- | :--- | :--- |
|
|
||||||
| **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 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
|
|
||||||
|
|
||||||
#### AMD GCN Migration via GRUB Parameters
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 5. **NVIDIA Hardware & Driver Support**
|
|
||||||
|
|
||||||
#### 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 |
|
|
||||||
| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
|
||||||
| **Fermi**<br>*(GF100/110)* | **390.xx** (Legacy) | `nvidia` (Closed) | **Vulkan 1.0** | No | **CUDA 8.0** | Last driver to support this architecture. Has not received security fixes for years. Only viable for completely offline systems. |
|
|
||||||
| **Kepler**<br>*(GK100/110)* | **470.xx** (Legacy) | `nvidia` (Closed) | **Vulkan 1.2** | No | **CUDA 11.8** | Last generation to receive "Legacy" status. Good compatibility with OpenGL 4.6, but Vulkan support stalled at 1.2. |
|
|
||||||
| **Maxwell**<br>*(GM100/200)* | **580.xx** (Old Standard) | `nvidia` (Closed) | **Vulkan 1.3** | No | **CUDA 12.0** | Removed from official support in driver 580 (CUDA 12.1). |
|
|
||||||
| **Pascal**<br>*(GP100/102/104)* | **580.xx** (Old Standard) | `nvidia` (Closed) | **Vulkan 1.3** | No | **CUDA 12.0** | Shares same fate as Maxwell. Still very popular (GTX 1060/1080), but requires blocking packages (e.g., in Debian) to prevent updates that break graphics support. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 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 |
|
|
||||||
| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
|
||||||
| **Turing**<br>*(TU100/102/116)* | 525.xx to 610+ | `nvidia` (**Open Module**) | **Vulkan 1.3** | **Yes** (Vulkan 1.3) | **CUDA 12.8+** | First generation to use Open Kernel Module (introduced in 515, default in 525). *Note:* Security processor firmware (GSP) remains a closed blob. Excellent NVK support. |
|
|
||||||
| **Ampere**<br>*(GA100/102/104/107)* | 525.xx to 610+ | `nvidia` (**Open Module**) | **Vulkan 1.3** | **Yes** (Vulkan 1.3) | **CUDA 12.8+** | Mature support in both proprietary driver and NVK. For RTX 3060/3080/3090, NVK offers extremely competitive performance versus proprietary in many scenarios. |
|
|
||||||
| **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.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 6. **Mesa Stack Optimization (AMD & Intel)**
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
**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+. |
|
|
||||||
| **AMD GCN/RDNA** | `amdgpu` | `radeonsi` (GL) + `RADV` (VK) | Uses standard `va-driver-all`. Requires kernel param tuning for older GCN. |
|
|
||||||
|
|
||||||
**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.
|
|
||||||
|
|
||||||
#### **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)`).
|
|
||||||
|
|
||||||
#### **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.
|
|
||||||
|
|
||||||
#### **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.
|
|
||||||
---
|
|
||||||
|
|
||||||
### 9.**NVIDIA Driver Compatibility Matrix**
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
| Driver NVIDIA | Debian Version | Supported Architectures (Generations) | Notes |
|
|
||||||
| :--- | :--- | :--- | :--- |
|
|
||||||
| **[390.157](https://us.download.nvidia.com/XFree86/Linux-x86_64/390.157/README/supportedchips.html)** | **Debian 11** (Bullseye) | Fermi, Kepler, Maxwell, Pascal, Volta | Last driver with Fermi support. Stable for legacy hardware. |
|
|
||||||
| **[470.256.02](https://us.download.nvidia.com/XFree86/Linux-x86_64/470.256.02/README/supportedchips.html)** | Debian 11 / **Bookworm** | Kepler, Maxwell, Pascal, Volta, Turing *(Limited Ampere)* | Last driver supporting Kepler (GeForce). Quadro K-series often use Maxwell chips here. |
|
|
||||||
| **[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.
|
|
||||||
|
|
||||||
#### **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):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
if [ "$is_kepler" = "true" ] && [ "$DEBIAN_CODENAME" = "bookworm" ]; then
|
|
||||||
nv_pkg="nvidia-tesla-470-driver"
|
|
||||||
# Avoids black screen issues by forcing legacy 470 branch
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 10. 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.
|
|
||||||
|
|
||||||
* **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.
|
|
||||||
|
|
||||||
**Installation Command Logic:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
if _confirm "Install Telemetry Tools"; then
|
|
||||||
case "$GPU_TYPE" in
|
|
||||||
nvidia) install_pkg nvtop ;;
|
|
||||||
amd) install_pkg radeontop ;;
|
|
||||||
intel) install_pkg intel-gpu-tools ;;
|
|
||||||
*) echo "Skipping telemetry for unknown GPU." ;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
Users can run these tools right after `debianito` to confirm that the graphics stack works.
|
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
# Option 7: Kernel Variants (Stable, Backports, RT, Cloud)
|
|
||||||
|
|
||||||
## 1. What Does This Component Do?
|
|
||||||
|
|
||||||
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):
|
|
||||||
|
|
||||||
| 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. |
|
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
> **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.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Why Offer Backports at All?
|
|
||||||
|
|
||||||
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`)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
_install_kernel_package "linux-image-amd64" "Backports" "-t trixie-backports"
|
|
||||||
_install_kernel_package "linux-image-rt-amd64" "RT" ""
|
|
||||||
```
|
|
||||||
|
|
||||||
Execution steps:
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
```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)
|
|
||||||
```
|
|
||||||
|
|
||||||
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**:
|
|
||||||
|
|
||||||
```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
|
|
||||||
```
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Menu Logic: `show_kernel_menu`
|
|
||||||
|
|
||||||
```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[@]}")
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Key details:
|
|
||||||
|
|
||||||
- **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`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Safety Mechanisms
|
|
||||||
|
|
||||||
| 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)
|
|
||||||
@@ -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`).
|
|
||||||
|
|
||||||
> 
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
>  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 |
|
|
||||||
|
|
||||||
>  User Privileges & Feedback menu.
|
|
||||||
>  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).
|
|
||||||
|
|
||||||
>  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.
|
|
||||||
|
|
||||||
>  Repositories menu.
|
|
||||||
>  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.
|
|
||||||
|
|
||||||
>  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.
|
|
||||||
|
|
||||||
>  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.
|
|
||||||
|
|
||||||
>  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).
|
|
||||||
|
|
||||||
>  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`.
|
|
||||||
|
|
||||||
>  Algorithm choice (lz4 vs zstd).
|
|
||||||
>  `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`.
|
|
||||||
|
|
||||||
>  Swap Management menu.
|
|
||||||
>  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 |
|
|
||||||
|
|
||||||
>  Category menu with 0-14 options.
|
|
||||||
>  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'`). |
|
|
||||||
@@ -1,303 +0,0 @@
|
|||||||
# Option 4: 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:
|
|
||||||
|
|
||||||
- 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
|
|
||||||
- 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.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. Supported Injection Formats
|
|
||||||
|
|
||||||
The script 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
|
|
||||||
- **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
|
|
||||||
Suites: trixie trixie-updates
|
|
||||||
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
|
|
||||||
- Old files are renamed with `.disabled` extension rather than deleted
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. Logical Decision Tree (Step-by-Step Execution Flow)
|
|
||||||
|
|
||||||
The `configure_repos()` function in `repos.sh` executes the following sequence:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ INITIAL DETECTION PHASE │
|
|
||||||
├─────────────────────────────────────────────────────────────┤
|
|
||||||
│ 1. Detect Debian Codename (DEBIAN_CODENAME) │
|
|
||||||
│ └── If empty → Abort with error │
|
|
||||||
│ │
|
|
||||||
│ 2. Detect Current Format │
|
|
||||||
│ ├── detect_repo_format() → "deb822" | "classic" | "none" │
|
|
||||||
│ └── Display: "Current format: [format]" │
|
|
||||||
│ │
|
|
||||||
│ 3. Detect Backports Status │
|
|
||||||
│ ├── detect_backports_status() → enabled/disabled │
|
|
||||||
│ └── Detect Location: embedded vs standalone │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
↓
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ USER INTERACTION PHASE │
|
|
||||||
├─────────────────────────────────────────────────────────────┤
|
|
||||||
│ 4. Repository Menu Loop (while true) │
|
|
||||||
│ └── _menu: Select action from multiple options │
|
|
||||||
│ │
|
|
||||||
│ Options Available: │
|
|
||||||
│ ├── Debian 13+ (Trixie): │
|
|
||||||
│ │ ├── 1. Enable Contrib & Non-Free Components │
|
|
||||||
│ │ ├── 2. Migrate traditional sources.list to DEB822 │
|
|
||||||
│ │ ├── 3. Setup/Update Backports repositories │
|
|
||||||
│ │ ├── 4. [ADVANCED] Upgrade system branch (Testing/SID)│
|
|
||||||
│ │ └── 5. Back to main menu │
|
|
||||||
│ │
|
|
||||||
│ └── Other Versions: │
|
|
||||||
│ ├── 1-3 same as above │
|
|
||||||
│ └── No option 4 (branch upgrade not available) │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
↓
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ DECISION MATRIX PHASE │
|
|
||||||
├─────────────────────────────────────────────────────────────┤
|
|
||||||
│ 5. Determine Action Type (per menu selection) │
|
|
||||||
│ ├── If format changed → "migrate" │
|
|
||||||
│ ├── If nothing changed → "skip" (idempotent) │
|
|
||||||
│ └── Otherwise → "write" │
|
|
||||||
│ │
|
|
||||||
│ 6. Idempotency Check │
|
|
||||||
│ └── content_differs() compares generated vs existing │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
↓
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ EXECUTION PHASE │
|
|
||||||
├─────────────────────────────────────────────────────────────┤
|
|
||||||
│ 7. Backup Current Repositories │
|
|
||||||
│ └── backup_current_repos() → temp directory │
|
|
||||||
│ │
|
|
||||||
│ 8. Write Configuration │
|
|
||||||
│ ├── _write_deb822() OR _write_classic() │
|
|
||||||
│ ├── 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 │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
↓
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ POST-EXECUTION PHASE │
|
|
||||||
├─────────────────────────────────────────────────────────────┤
|
|
||||||
│ 10. Success Path │
|
|
||||||
│ ├── Cleanup disabled files │
|
|
||||||
│ └── Optional: Upgrade system if packages available │
|
|
||||||
│ │
|
|
||||||
│ 11. Failure Path (apt update failed) │
|
|
||||||
│ └── restore_previous_repos() → rollback to backup │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
**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`
|
|
||||||
- **Disabled File Extension**: Old formats renamed with `.disabled` rather than deleted
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 4. Software Components Activated
|
|
||||||
|
|
||||||
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+)
|
|
||||||
|
|
||||||
Starting with Debian Bookworm (12.0), the `non-free-firmware` component was separated into its own repository branch:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Before Debian 12 (Bookworm)
|
|
||||||
deb https://deb.debian.org/debian bullseye main contrib non-free
|
|
||||||
|
|
||||||
# After Debian 12 (Bookworm+) - SEPARATE COMPONENTS
|
|
||||||
deb https://deb.debian.org/debian bookworm main contrib non-free non-free-firmware
|
|
||||||
```
|
|
||||||
|
|
||||||
**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
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 5. Support for Debian Backports
|
|
||||||
|
|
||||||
Backports give access to newer, tested packages without compromising system stability:
|
|
||||||
|
|
||||||
#### Detection Logic (`detect_backports_status` & `detect_backports_location`)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Checks ALL possible locations for backports configuration
|
|
||||||
├── /etc/apt/sources.list.d/debian-backports.sources (DEB822 standalone)
|
|
||||||
├── /etc/apt/sources.list.d/debian-backports.list (Classic standalone)
|
|
||||||
├── /etc/apt/sources.list.d/debian.sources (Embedded in DEB822)
|
|
||||||
└── /etc/apt/sources.list (Embedded in Classic)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Return Values:**
|
|
||||||
|
|
||||||
- `"standalone-deb822"` → Separate `.sources` file (recommended)
|
|
||||||
- `"standalone-classic"` → Separate `.list` file
|
|
||||||
- `"embedded-deb822"` → Inside `debian.sources`
|
|
||||||
- `"embedded-classic"` → Inside `sources.list`
|
|
||||||
- `"none"` → Not configured
|
|
||||||
|
|
||||||
#### Backports Injection Process
|
|
||||||
|
|
||||||
```bash
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ 1. User Selects: Enable Backports? │
|
|
||||||
│ └── whiptail confirm with explanation │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
↓
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ 2. Determine Format │
|
|
||||||
│ ├── If DEB822 → _write_deb822_backports() │
|
|
||||||
│ └── If Classic → _write_classic_backports() │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
↓
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ 3. Create Backports File │
|
|
||||||
│ ├── Location: /etc/apt/sources.list.d/ │
|
|
||||||
│ └── Name: debian-backports.sources or .list │
|
|
||||||
│ │
|
|
||||||
│ Content Example (DEB822): │
|
|
||||||
│ Types: deb │
|
|
||||||
│ URIs: https://deb.debian.org/debian │
|
|
||||||
│ Suites: bookworm-backports │
|
|
||||||
│ Components: main contrib non-free non-free-firmware │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
↓
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ 4. Cleanup Embedded Backports (Safety Net) │
|
|
||||||
│ └── If backports existed in main file, remove them │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Why Enable Backports?
|
|
||||||
|
|
||||||
The script explains this in its confirmation dialog because backports change what you get for three components:
|
|
||||||
|
|
||||||
| 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 |
|
|
||||||
| **System Stability** | Maximum stability | Tested-but-newer packages |
|
|
||||||
|
|
||||||
#### 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
|
|
||||||
- Automatically detected in other modules (kernel, GPU)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Technical Implementation Notes
|
|
||||||
|
|
||||||
**Idempotency Guarantee:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# content_differs() ensures no duplicate writes
|
|
||||||
if [ "$current" = "$generated" ]; then
|
|
||||||
return 1 # No changes needed
|
|
||||||
fi
|
|
||||||
return 0 # Changes required
|
|
||||||
```
|
|
||||||
|
|
||||||
**Atomic Backup Mechanism:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
backup_current_repos() {
|
|
||||||
REPO_BACKUP_DIR=$(mktemp -d)
|
|
||||||
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*.sources; do
|
|
||||||
cp "$f" "$REPO_BACKUP_DIR/" 2>/dev/null || true
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# Rollback on failure:
|
|
||||||
restore_previous_repos() {
|
|
||||||
sudo cp "$backup_file" "$original_path" # Restore from temp backup
|
|
||||||
rm -rf "$REPO_BACKUP_DIR" # Clean up after success/failure
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**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
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
# Installing New Cores in RetroArch on Debian
|
|
||||||
|
|
||||||
RetroArch installed via the official Debian repositories (`apt`) has a few key restrictions: the internal core downloader and updater are **disabled** by default, and cores are stored in system‑wide, read‑only directories. This guide explains two reliable ways to add new emulation cores (e.g., PPSSPP, Dolphin, MAME) to your Debian system, along with the necessary supporting files and configuration tweaks.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Understanding RetroArch’s Directory Structure on Debian
|
|
||||||
|
|
||||||
When installed via `apt`, RetroArch uses these locations:
|
|
||||||
|
|
||||||
| **Component** | **Default Path** (system‑wide) |
|
|
||||||
|-----------------------------|---------------------------------------------------------|
|
|
||||||
| Cores (`.so` files) | `/usr/lib/x86_64-linux-gnu/libretro/` |
|
|
||||||
| Core info (`.info` files) | `/usr/share/libretro/info/` |
|
|
||||||
| System/BIOS/Assets | `~/.config/retroarch/system/` (user‑writable) |
|
|
||||||
| Saves, States, Config | `~/.config/retroarch/` (user‑writable) |
|
|
||||||
|
|
||||||
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`.
|
|
||||||
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)
|
|
||||||
|
|
||||||
This method works for any core, even when the online updater is blocked.
|
|
||||||
|
|
||||||
### 1. Download and Install the Core (`.so`)
|
|
||||||
|
|
||||||
The core is a shared library. For 64‑bit Linux, get it from the official Libretro buildbot:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Example: PPSSPP core
|
|
||||||
wget https://buildbot.libretro.com/nightly/linux/x86_64/latest/ppsspp_libretro.so.zip
|
|
||||||
unzip ppsspp_libretro.so.zip
|
|
||||||
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.
|
|
||||||
> Browse all available cores at:
|
|
||||||
> [https://buildbot.libretro.com/nightly/linux/x86_64/latest/](https://buildbot.libretro.com/nightly/linux/x86_64/latest/)
|
|
||||||
|
|
||||||
### 2. Download and Install the Core Info (`.info`)
|
|
||||||
|
|
||||||
RetroArch will not recognise the core without its corresponding `.info` file.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
wget https://raw.githubusercontent.com/libretro/libretro-core-info/master/ppsspp_libretro.info
|
|
||||||
sudo mkdir -p /usr/share/libretro/info/
|
|
||||||
sudo mv ppsspp_libretro.info /usr/share/libretro/info/
|
|
||||||
```
|
|
||||||
|
|
||||||
> The `.info` files for all cores are maintained at:
|
|
||||||
> [https://github.com/libretro/libretro-core-info](https://github.com/libretro/libretro-core-info)
|
|
||||||
> You can also download directly: `https://raw.githubusercontent.com/libretro/libretro-core-info/master/<core_name>.info`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 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
|
|
||||||
|
|
||||||
Create or edit `~/.config/retroarch/retroarch.cfg` and add these lines:
|
|
||||||
|
|
||||||
```
|
|
||||||
libretro_directory = "~/.config/retroarch/libretro/"
|
|
||||||
libretro_info_path = "~/.config/retroarch/libretro-info/"
|
|
||||||
menu_show_core_updater = "true"
|
|
||||||
```
|
|
||||||
|
|
||||||
Now create the directories:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p ~/.config/retroarch/libretro
|
|
||||||
mkdir -p ~/.config/retroarch/libretro-info
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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
|
|
||||||
|
|
||||||
- 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).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Essential Configuration for Graphics
|
|
||||||
|
|
||||||
The default video driver (`gl`) often causes black screens or crashes with many cores (especially PPSSPP). **Change the driver** to `vulkan` (preferred) or `glcore`.
|
|
||||||
|
|
||||||
1. In RetroArch, go to **Settings** → **Drivers** → **Video**.
|
|
||||||
2. Select **vulkan** (or **glcore** if Vulkan is not available).
|
|
||||||
3. Return to the main menu, go to **Configuration File** → **Save Current Configuration**.
|
|
||||||
4. **Restart RetroArch** for the change to take effect.
|
|
||||||
|
|
||||||
> If you’re on older hardware, you may need to install Vulkan drivers:
|
|
||||||
> `sudo apt install mesa-vulkan-drivers`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Installing Additional System Files (Assets / BIOS)
|
|
||||||
|
|
||||||
Some emulators require extra files (fonts, sound banks, BIOS images) to work correctly. These always go into your **user** system directory:
|
|
||||||
|
|
||||||
```
|
|
||||||
~/.config/retroarch/system/
|
|
||||||
```
|
|
||||||
|
|
||||||
For **PPSSPP**, you need its asset bundle:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p ~/.config/retroarch/system/PPSSPP
|
|
||||||
wget https://buildbot.libretro.com/assets/system/PPSSPP.zip
|
|
||||||
unzip PPSSPP.zip -d ~/.config/retroarch/system/
|
|
||||||
```
|
|
||||||
|
|
||||||
After extraction, you should see subfolders like `flash0/`, `lang/`, `themes/`, etc.
|
|
||||||
|
|
||||||
For other systems (e.g., **Dolphin**, **PCSX2**), check the official Libretro documentation for required BIOS files and place them in the appropriate subdirectory under `system/`.
|
|
||||||
You can find many asset packs at:
|
|
||||||
[https://buildbot.libretro.com/assets/system/](https://buildbot.libretro.com/assets/system/)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Useful References
|
|
||||||
|
|
||||||
| Resource | URL |
|
|
||||||
|----------|-----|
|
|
||||||
| Core builds (nightly, Linux x86_64) | [https://buildbot.libretro.com/nightly/linux/x86_64/latest/](https://buildbot.libretro.com/nightly/linux/x86_64/latest/) |
|
|
||||||
| 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) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Final Notes
|
|
||||||
|
|
||||||
- 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.
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
# Option 10: Swap Management
|
|
||||||
|
|
||||||
### 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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
### 2. How does it work?
|
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
### 3. The Logical Decision Tree (Step by Step)
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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`.
|
|
||||||
|
|
||||||
### 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 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 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 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.
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
# Option 1: Hardware Detection & System Information
|
|
||||||
|
|
||||||
### 1. What Does This Component Do?
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
### 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.
|
|
||||||
|
|
||||||
| 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. |
|
|
||||||
| **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. |
|
|
||||||
|
|
||||||
### 3. Why Detection Matters
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
### 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.
|
|
||||||
@@ -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)
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
# Option 2: User Privileges & Feedback
|
|
||||||
|
|
||||||
### 1. What does this component do?
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
### 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 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.
|
|
||||||
|
|
||||||
* **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.
|
|
||||||
|
|
||||||
* **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.
|
|
||||||
|
|
||||||
**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.
|
|
||||||
|
|
||||||
**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.
|
|
||||||
|
|
||||||
**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.
|
|
||||||
|
|
||||||
**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.
|
|
||||||
|
|
||||||
### 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.
|
|
||||||
@@ -1,294 +0,0 @@
|
|||||||
# Option 9: ZRAM Configuration & Memory Optimization
|
|
||||||
|
|
||||||
## 1. ZRAM vs. Traditional Swap
|
|
||||||
|
|
||||||
### Core Concept: CPU Cycles vs. Disk I/O
|
|
||||||
|
|
||||||
Traditional swap has a latency gap that matters 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 |
|
|
||||||
|
|
||||||
When a Linux system experiences memory pressure, the kernel must decide what to swap out. Traditional swap writes pages directly to disk storage:
|
|
||||||
|
|
||||||
- **Time Cost**: Each 4 KiB page write takes microseconds (NVMe) to milliseconds (HDD)
|
|
||||||
- **Wear Cost**: Every write consumes P/E (Program/Erase) cycles from NAND flash cells, reducing TBW (Terabytes Written) lifespan
|
|
||||||
- **System Impact**: High latency causes "thrashing" where the system spends more time waiting for disk I/O than executing actual work
|
|
||||||
|
|
||||||
### ZRAM's Solution: Compression in RAM
|
|
||||||
|
|
||||||
ZRAM creates a compressed block device entirely within physical memory. When pages need to be swapped, they are:
|
|
||||||
|
|
||||||
1. **Compressed on-the-fly** using CPU algorithms (LZ4 or ZSTD)
|
|
||||||
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.
|
|
||||||
|
|
||||||
### Why Only LZ4 and ZSTD?
|
|
||||||
|
|
||||||
The script offers only two algorithms because they hit the best balance of speed and ratio:
|
|
||||||
|
|
||||||
| 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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
### Extending SSD Lifespan Through Reduced Writes
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
As the Linux kernel documentation states: *"Users with SSDs as swap devices can extend device lifespan by drastically reducing writes that shorten its life."*
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Injection Flow and Configuration Logic (`zram-tools`)
|
|
||||||
|
|
||||||
### Pipeline Execution Sequence
|
|
||||||
|
|
||||||
The script follows a fixed sequence:
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ install_zram() Function │
|
|
||||||
├─────────────────────────────────────────────────────────────┤
|
|
||||||
│ 1. Validate RAM Detection │
|
|
||||||
│ └─ Check if RAM_KB is available and non-zero │
|
|
||||||
│ │
|
|
||||||
│ 2. Compression Algorithm Selection │
|
|
||||||
│ ├─ Present menu: LZ4 (fast) vs ZSTD (better ratio) │
|
|
||||||
│ └─ User choice stored in $algo variable │
|
|
||||||
│ │
|
|
||||||
│ 3. Size Calculation Logic │
|
|
||||||
│ ┌──────────────────────────────────────────────┐ │
|
|
||||||
│ │ ram_gb <= 8 ? 50% : 4096 MB fixed │ │
|
|
||||||
│ │ recommended_mb = (ram_gb <= 8) │ │
|
|
||||||
│ │ ? (RAM_KB/1024/1024 + 1) │ │
|
|
||||||
│ │ / 2 * 1024 │ │
|
|
||||||
│ │ : 4096 │ │
|
|
||||||
│ └──────────────────────────────────────────────┘ │
|
|
||||||
│ └─ Result: 50% RAM if ≤8 GB, else fixed 4096 MB │
|
|
||||||
│ │
|
|
||||||
│ 4. Configuration Confirmation │
|
|
||||||
│ ├─ Display summary with algorithm, size, priority=100 │
|
|
||||||
│ └─ User must confirm before applying │
|
|
||||||
│ │
|
|
||||||
│ 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 │
|
|
||||||
│ /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 │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### Size Formula
|
|
||||||
|
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
**Breakdown:**
|
|
||||||
|
|
||||||
- `RAM_KB`: Total RAM in kilobytes from `/proc/meminfo`
|
|
||||||
- `/ 1024 / 1024`: Convert KB to GB
|
|
||||||
- `+ 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:**
|
|
||||||
|
|
||||||
```
|
|
||||||
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)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Priority Configuration (`PRIORITY=100`)
|
|
||||||
|
|
||||||
The `swapon` priority determines which swap device the kernel prefers when multiple devices exist:
|
|
||||||
|
|
||||||
- **Higher number** = Higher preference (used first by kernel)
|
|
||||||
- **Default system swap**: Typically 0–60
|
|
||||||
- **ZRAM with PRIORITY=100**: Ensures ZRAM is used before physical disk swap
|
|
||||||
|
|
||||||
This prevents thrashing where pages bounce between slow disk swap and fast RAM-based ZRAM.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Priority & Swappiness Integration
|
|
||||||
|
|
||||||
### How ZRAM Coexists with Disk Swap
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
```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
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Service Lifecycle and Validation
|
|
||||||
|
|
||||||
### Safe Service Initialization
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo systemctl restart zramswap || true
|
|
||||||
```
|
|
||||||
|
|
||||||
**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
|
|
||||||
|
|
||||||
### User Verification Commands
|
|
||||||
|
|
||||||
#### Primary: `zramctl` (util-linux)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
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 |
|
|
||||||
| **DATA** | Currently stored uncompressed pages in ZRAM |
|
|
||||||
| **COMPR** | Actual compressed size using physical RAM |
|
|
||||||
| **TOTAL** | Total memory used including metadata overhead |
|
|
||||||
| **STREAMS** | Number of active swap streams (typically 4) |
|
|
||||||
|
|
||||||
#### Secondary: `swapon --show`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo swapon --show
|
|
||||||
```
|
|
||||||
|
|
||||||
Shows all active swap devices with priority levels. ZRAM should appear with priority matching the configured value (100 in this script).
|
|
||||||
|
|
||||||
### Real-Time Monitoring
|
|
||||||
|
|
||||||
For continuous monitoring of compression effectiveness:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Watch compression ratio changes over time
|
|
||||||
watch -n 5 'zramctl | grep /dev/zram'
|
|
||||||
|
|
||||||
# Monitor memory pressure and swap usage
|
|
||||||
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:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
echo "zram" | sudo tee /etc/modules-load.d/zram.conf
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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)
|
|
||||||
- [https://wiki.archlinux.org/title/Zram](https://wiki.archlinux.org/title/Zram)
|
|
||||||
- [https://wiki.gentoo.org/wiki/Zram](https://wiki.gentoo.org/wiki/Zram)
|
|
||||||
|
Before Width: | Height: | Size: 852 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 23 KiB |
@@ -1,93 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# modules/bluetooth.sh
|
|
||||||
# Requires: modules/utils.sh (globals + helpers), modules/firmware.sh (PCI_BT_DEVS, USB_BT_DEVS, USB_WIFI_DEVS)
|
|
||||||
|
|
||||||
_install_bluetooth_stack() {
|
|
||||||
local has_bt=false
|
|
||||||
[ ${#PCI_BT_DEVS[@]} -gt 0 ] && has_bt=true
|
|
||||||
[ ${#USB_BT_DEVS[@]} -gt 0 ] && has_bt=true
|
|
||||||
if ! $has_bt; then
|
|
||||||
for dev in "${USB_WIFI_DEVS[@]}"; do
|
|
||||||
if echo "$dev" | grep -qi 'bluetooth'; then
|
|
||||||
has_bt=true; break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
if ! $has_bt; then
|
|
||||||
echo " → No Bluetooth hardware detected, skipping."
|
|
||||||
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
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! ${service_enable_only:-false} = true ]; then
|
|
||||||
local bt_pkgs=()
|
|
||||||
! is_installed bluez && bt_pkgs+=(bluez)
|
|
||||||
! 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
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if command -v rfkill &>/dev/null; then
|
|
||||||
if rfkill list bluetooth 2>/dev/null | grep -q "Soft blocked: yes"; then
|
|
||||||
echo " → Unblocking Bluetooth (rfkill)..."
|
|
||||||
sudo rfkill unblock bluetooth
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
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
|
|
||||||
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
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
gnome)
|
|
||||||
echo " → GNOME Bluetooth support already in gnome-control-center."
|
|
||||||
;;
|
|
||||||
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
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if ! systemctl is-enabled bluetooth &>/dev/null 2>&1; then
|
|
||||||
sudo systemctl enable bluetooth 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
if ! systemctl is-active bluetooth &>/dev/null 2>&1; then
|
|
||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,812 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# extras.sh — Bullseye: software purgado / solo repos oficiales
|
|
||||||
# License GPL v3
|
|
||||||
|
|
||||||
# ── Essential Pack (Bullseye) ──
|
|
||||||
_quick_install_bullseye() {
|
|
||||||
_msg "Essential Pack" \
|
|
||||||
"Install basic programs:\n\n\
|
|
||||||
- Compression (zip, unrar, 7z)\n\
|
|
||||||
- System tools (htop, inxi, neofetch)\n\
|
|
||||||
- Networking (curl, wget, ufw)\n\
|
|
||||||
- CA certs & GPG\n\
|
|
||||||
- VLC media player" 13 60
|
|
||||||
|
|
||||||
local quick_pkgs=(
|
|
||||||
zip unzip rar unrar p7zip-full p7zip-rar
|
|
||||||
neofetch htop inxi curl wget ufw
|
|
||||||
ca-certificates gnupg lsb-release
|
|
||||||
)
|
|
||||||
_is_headless || quick_pkgs+=(vlc)
|
|
||||||
_run_install_batch "${quick_pkgs[@]}"
|
|
||||||
echo -e "${GREEN}Essential Pack installed.${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── Firmware (Bullseye) ──
|
|
||||||
install_firmware_bullseye() {
|
|
||||||
echo -e "${YELLOW}Installing firmware-linux-nonfree (Bullseye)...${NC}"
|
|
||||||
|
|
||||||
# ── Safeguard: non-free repos must be enabled ──
|
|
||||||
if ! grep -qr "non-free" /etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null; then
|
|
||||||
_msg "Error" "Error: No 'non-free' repositories were detected.\n\
|
|
||||||
Please first run the 'Configure repositories' option in the\n\
|
|
||||||
main menu to install proprietary firmwares." 10 65
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if is_installed "firmware-linux-nonfree"; then
|
|
||||||
echo "firmware-linux-nonfree already installed."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
local fw_ver
|
|
||||||
fw_ver=$(apt-cache policy firmware-linux-nonfree 2>/dev/null | awk 'NR==3 {print $2; exit}')
|
|
||||||
|
|
||||||
if _confirm "Firmware" \
|
|
||||||
"firmware-linux-nonfree provides drivers for:\n\
|
|
||||||
WiFi, Bluetooth, GPU, audio, webcams, etc.\n\n\
|
|
||||||
Version: ${fw_ver:-unknown}\n\n\
|
|
||||||
Install?"; then
|
|
||||||
_run_cmd "Firmware" "sudo apt install -y firmware-linux-nonfree" \
|
|
||||||
"Installing firmware-linux-nonfree..."
|
|
||||||
echo -e "${GREEN}Firmware installed.${NC}"
|
|
||||||
fi
|
|
||||||
type _handle_wireless &>/dev/null && _handle_wireless
|
|
||||||
}
|
|
||||||
|
|
||||||
# ======================================================================
|
|
||||||
# EXTRA SOFTWARE CATEGORIES — Bullseye versions (purged/whitelisted)
|
|
||||||
# Only packages available in Debian 11 official repos.
|
|
||||||
# ======================================================================
|
|
||||||
|
|
||||||
_cat_customization_bullseye() {
|
|
||||||
local sub
|
|
||||||
sub=$(
|
|
||||||
_menu "Customization (Bullseye)" "Select type:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
|
||||||
"1" "Desktop Themes (GTK/KDE)" \
|
|
||||||
"2" "Icon Themes" \
|
|
||||||
"3" "Cursor Themes" \
|
|
||||||
"4" "Fonts" \
|
|
||||||
"5" "Terminals"
|
|
||||||
)
|
|
||||||
[ -z "$sub" ] && return
|
|
||||||
case $sub in
|
|
||||||
1) _cat_themes_bullseye ;;
|
|
||||||
2) _cat_icons_bullseye ;;
|
|
||||||
3) _cat_cursors_bullseye ;;
|
|
||||||
4) _cat_fonts_bullseye ;;
|
|
||||||
5) _cat_terminals ;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
_cat_themes_bullseye() {
|
|
||||||
local item_count=6
|
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
|
||||||
choices=$(
|
|
||||||
_checklist "Desktop Themes (Bullseye)" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
|
||||||
"arc-theme" "Arc GTK theme" "$(_state arc-theme)" \
|
|
||||||
"blackbird-gtk-theme" "Blackbird GTK theme" "$(_state blackbird-gtk-theme)" \
|
|
||||||
"bluebird-gtk-theme" "Bluebird GTK theme" "$(_state bluebird-gtk-theme)" \
|
|
||||||
"breeze-gtk-theme" "Breeze GTK theme (KDE port)" "$(_state breeze-gtk-theme)" \
|
|
||||||
"greybird-gtk-theme" "Greybird GTK theme" "$(_state greybird-gtk-theme)" \
|
|
||||||
"numix-gtk-theme" "Numix GTK theme" "$(_state numix-gtk-theme)"
|
|
||||||
)
|
|
||||||
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 "Desktop Themes (Bullseye)" "${_pkgs[@]}" || return
|
|
||||||
for pkg in "${_pkgs[@]}"; do
|
|
||||||
! is_installed "$pkg" && _run_install "$pkg" || echo "$pkg already installed."
|
|
||||||
done
|
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
echo -e "${GREEN}Desktop themes installed.${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
_cat_icons_bullseye() {
|
|
||||||
local items=(
|
|
||||||
"breeze-icon-theme" "Breeze icon theme" "$(_state breeze-icon-theme)"
|
|
||||||
"deepin-icon-theme" "Deepin icon theme" "$(_state deepin-icon-theme)"
|
|
||||||
"moka-icon-theme" "Moka icon theme" "$(_state moka-icon-theme)"
|
|
||||||
"numix-icon-theme" "Numix icon theme" "$(_state numix-icon-theme)"
|
|
||||||
"numix-icon-theme-circle" "Numix Circle icon theme" "$(_state numix-icon-theme-circle)"
|
|
||||||
"obsidian-icon-theme" "Obsidian icon theme" "$(_state obsidian-icon-theme)"
|
|
||||||
"papirus-icon-theme" "Papirus icon theme" "$(_state papirus-icon-theme)"
|
|
||||||
"paper-icon-theme" "Paper icon theme" "$(_state paper-icon-theme)"
|
|
||||||
"suru-icon-theme" "Suru icon theme" "$(_state suru-icon-theme)"
|
|
||||||
)
|
|
||||||
local item_count=${#items[@]}
|
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
|
||||||
choices=$(_checklist "Icon Themes (Bullseye)" "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 '"')
|
|
||||||
# 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 (Bullseye)" "${_pkgs[@]}" || return
|
|
||||||
for pkg in "${_pkgs[@]}"; do
|
|
||||||
! is_installed "$pkg" && _run_install "$pkg" || echo "$pkg already installed."
|
|
||||||
done
|
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
echo -e "${GREEN}Icon themes installed.${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
_cat_cursors_bullseye() {
|
|
||||||
local item_count=5
|
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
|
||||||
choices=$(
|
|
||||||
_checklist "Cursor Themes (Bullseye)" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
|
||||||
"breeze-cursor-theme" "Breeze cursors (KDE)" "$(_state breeze-cursor-theme)" \
|
|
||||||
"chameleon-cursor-theme" "Chameleon cursors" "$(_state chameleon-cursor-theme)" \
|
|
||||||
"dmz-cursor-theme" "DMZ cursors" "$(_state dmz-cursor-theme)" \
|
|
||||||
"oxygencursors" "Oxygen cursors (KDE legacy)" "$(_state oxygencursors)" \
|
|
||||||
"xcursor-themes" "X11 base cursors" "$(_state xcursor-themes)"
|
|
||||||
)
|
|
||||||
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 "Cursor Themes (Bullseye)" "${_pkgs[@]}" || return
|
|
||||||
for pkg in "${_pkgs[@]}"; do
|
|
||||||
! is_installed "$pkg" && _run_install "$pkg" || echo "$pkg already installed."
|
|
||||||
done
|
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
echo -e "${GREEN}Cursor themes installed.${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
_cat_fonts_bullseye() {
|
|
||||||
local item_count=4
|
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
|
||||||
choices=$(
|
|
||||||
_checklist "Fonts (Bullseye)" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
|
||||||
"fonts-firacode" "Fira Code monospace font" "$(_state fonts-firacode)" \
|
|
||||||
"fonts-noto" "Noto fonts (Google)" "$(_state fonts-noto)" \
|
|
||||||
"fonts-dejavu-core" "DejaVu core fonts" "$(_state fonts-dejavu-core)" \
|
|
||||||
"ttf-mscorefonts-installer" "Microsoft Core Fonts" "$(_state ttf-mscorefonts-installer)"
|
|
||||||
)
|
|
||||||
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 "Fonts (Bullseye)" "${_pkgs[@]}" || return
|
|
||||||
for pkg in "${_pkgs[@]}"; do
|
|
||||||
! is_installed "$pkg" && _run_install "$pkg" || echo "$pkg already installed."
|
|
||||||
done
|
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
echo -e "${GREEN}Fonts installed.${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
_cat_download_bullseye() {
|
|
||||||
local choices1 choices2=""
|
|
||||||
|
|
||||||
local item_count1=2
|
|
||||||
local lista_alto1=$((item_count1 > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count1))
|
|
||||||
choices1=$(
|
|
||||||
_checklist "Downloaders" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto1 \
|
|
||||||
"aria2" "Multiprotocol downloader (CLI)" "$(_state aria2)" \
|
|
||||||
"filezilla" "FTP/SFTP client (GUI)" "$(_state filezilla)"
|
|
||||||
)
|
|
||||||
clear
|
|
||||||
|
|
||||||
local item_count2=8
|
|
||||||
local lista_alto2=$((item_count2 > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count2))
|
|
||||||
choices2=$(
|
|
||||||
_checklist "Torrent Clients" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto2 \
|
|
||||||
"deluge" "BitTorrent client (GTK)" "$(_state deluge)" \
|
|
||||||
"deluged" "BitTorrent daemon/server" "$(_state deluged)" \
|
|
||||||
"mktorrent" "Torrent metainfo creator (CLI)" "$(_state mktorrent)" \
|
|
||||||
"qbittorrent" "BitTorrent client (Qt)" "$(_state qbittorrent)" \
|
|
||||||
"qbittorrent-nox" "BitTorrent WebUI/CLI" "$(_state qbittorrent-nox)" \
|
|
||||||
"transmission-cli" "BitTorrent client (CLI)" "$(_state transmission-cli)" \
|
|
||||||
"transmission-gtk" "BitTorrent client (GTK)" "$(_state transmission-gtk)" \
|
|
||||||
"transmission-qt" "BitTorrent client (Qt)" "$(_state transmission-qt)"
|
|
||||||
)
|
|
||||||
clear
|
|
||||||
|
|
||||||
local cleaned
|
|
||||||
cleaned=$(echo "$choices1 $choices2" | tr -d '"')
|
|
||||||
[ -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 (Bullseye)" "${_pkgs[@]}" || return
|
|
||||||
for pkg in "${_pkgs[@]}"; 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}Download & network tools installed.${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
_cat_internet_bullseye() {
|
|
||||||
local item_count=11
|
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
|
||||||
choices=$(
|
|
||||||
_checklist "Internet (Bullseye)" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
|
||||||
"chromium" "Chromium web browser" "$(_state chromium)" \
|
|
||||||
"dillo" "Lightweight graphical browser" "$(_state dillo)" \
|
|
||||||
"elinks" "Text-mode web browser" "$(_state elinks)" \
|
|
||||||
"epiphany-browser" "GNOME web browser" "$(_state epiphany-browser)" \
|
|
||||||
"falkon" "KDE web browser (QtWebEngine)" "$(_state falkon)" \
|
|
||||||
"firefox-esr" "Firefox ESR (official Debian)" "$(_state firefox-esr)" \
|
|
||||||
"konqueror" "KDE file manager / web browser" "$(_state konqueror)" \
|
|
||||||
"qutebrowser" "Keyboard-driven browser (Qt)" "$(_state qutebrowser)" \
|
|
||||||
"thunderbird" "Email client" "$(_state thunderbird)" \
|
|
||||||
"torbrowser-launcher" "Tor Browser launcher" "$(_state torbrowser-launcher)" \
|
|
||||||
"w3m" "Text-mode browser + deps" "$(_state w3m)"
|
|
||||||
)
|
|
||||||
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 "Internet (Bullseye)" "${_pkgs[@]}" || return
|
|
||||||
for pkg in "${_pkgs[@]}"; do
|
|
||||||
case $pkg in
|
|
||||||
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")
|
|
||||||
[ ${#need[@]} -gt 0 ] && _run_install_batch "${need[@]}" || echo "Already installed."
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
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}"
|
|
||||||
}
|
|
||||||
|
|
||||||
_cat_players_bullseye() {
|
|
||||||
local item_count=2
|
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
|
||||||
choices=$(
|
|
||||||
_checklist "Media Players (Bullseye)" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
|
||||||
"mpv" "Lightweight media player" "$(_state mpv)" \
|
|
||||||
"vlc" "VLC media player" "$(_state vlc)"
|
|
||||||
)
|
|
||||||
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 "Media Players (Bullseye)" "${_pkgs[@]}" || return
|
|
||||||
for pkg in "${_pkgs[@]}"; 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}Media players installed.${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
_cat_design_bullseye() {
|
|
||||||
local item_count=13
|
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
|
||||||
choices=$(
|
|
||||||
_checklist "Multimedia & Design (Bullseye)" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
|
||||||
"ardour" "Digital audio workstation" "$(_state ardour)" \
|
|
||||||
"audacity" "Audio editor/recorder" "$(_state audacity)" \
|
|
||||||
"blender" "3D modeling/animation suite" "$(_state blender)" \
|
|
||||||
"ffmpeg" "Multimedia framework (CLI)" "$(_state ffmpeg)" \
|
|
||||||
"gimp" "Image editor" "$(_state gimp)" \
|
|
||||||
"handbrake" "Video transcoder (DVD ripper)" "$(_state handbrake)" \
|
|
||||||
"inkscape" "Vector graphics editor" "$(_state inkscape)" \
|
|
||||||
"kdenlive" "Video editor (KDE)" "$(_state kdenlive)" \
|
|
||||||
"krita" "Digital painting/illustration" "$(_state krita)" \
|
|
||||||
"obs-studio" "Screen recording/streaming" "$(_state obs-studio)" \
|
|
||||||
"openshot-qt" "Video editor (simple)" "$(_state openshot-qt)" \
|
|
||||||
"scribus" "Desktop publishing (DTP)" "$(_state scribus)" \
|
|
||||||
"shotcut" "Video editor (cross-platform)" "$(_state shotcut)"
|
|
||||||
)
|
|
||||||
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 "Multimedia & Design (Bullseye)" "${_pkgs[@]}" || return
|
|
||||||
for pkg in "${_pkgs[@]}"; 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}Multimedia & design tools installed.${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
_cat_programming_bullseye() {
|
|
||||||
local item_count=9
|
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
|
||||||
choices=$(
|
|
||||||
_checklist "Code Editors & IDEs (Bullseye)" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
|
||||||
"vim" "Classic terminal editor" "$(_state vim)" \
|
|
||||||
"vim-gtk3" "Vim with GTK3 GUI" "$(_state vim-gtk3)" \
|
|
||||||
"neovim" "Modern vim fork" "$(_state neovim)" \
|
|
||||||
"nano" "Simple terminal editor" "$(_state nano)" \
|
|
||||||
"emacs" "Extensible editor / IDE" "$(_state emacs)" \
|
|
||||||
"kate" "KDE advanced text editor" "$(_state kate)" \
|
|
||||||
"mousepad" "Xfce text editor" "$(_state mousepad)" \
|
|
||||||
"gedit" "GNOME text editor" "$(_state gedit)" \
|
|
||||||
"geany" "Lightweight IDE" "$(_state geany)"
|
|
||||||
)
|
|
||||||
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 "Code Editors & IDEs (Bullseye)" "${_pkgs[@]}" || return
|
|
||||||
for pkg in "${_pkgs[@]}"; 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}Code editors & IDEs installed.${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
_cat_dev_bullseye() {
|
|
||||||
local item_count=14
|
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
|
||||||
choices=$(
|
|
||||||
_checklist "Servers & Dev Tools (Bullseye)" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
|
||||||
"apache2" "Apache web server" "$(_state apache2)" \
|
|
||||||
"build-essential" "C/C++ build tools (gcc, make)" "$(_state build-essential)" \
|
|
||||||
"docker" "Docker container runtime" "$(_state docker.io)" \
|
|
||||||
"mariadb-server" "MariaDB database server" "$(_state mariadb-server)" \
|
|
||||||
"netcat-openbsd" "TCP/IP networking utility" "$(_state netcat-openbsd)" \
|
|
||||||
"nginx" "Nginx web server" "$(_state nginx)" \
|
|
||||||
"openssh-server" "SSH server" "$(_state openssh-server)" \
|
|
||||||
"openssl" "OpenSSL cryptography toolkit" "$(_state openssl)" \
|
|
||||||
"postgresql" "PostgreSQL database server" "$(_state postgresql)" \
|
|
||||||
"python3-pip" "Python 3 pip + venv + dev" "$(_state python3-pip)" \
|
|
||||||
"redis-server" "Redis key-value store" "$(_state redis-server)" \
|
|
||||||
"sqlite3" "SQLite database engine" "$(_state sqlite3)" \
|
|
||||||
"jellyfin" "Jellyfin Media Server (Web GUI on port 8096)" OFF \
|
|
||||||
"openjdk-dev-env" "Adoptium Temurin JDK (17, 21, 25 LTS)$(_any_jdk_installed_desc)" "$(_any_jdk_state)"
|
|
||||||
)
|
|
||||||
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 "Servers & Dev Tools (Bullseye)" "${_pkgs[@]}" || return
|
|
||||||
for pkg in "${_pkgs[@]}"; do
|
|
||||||
case $pkg in
|
|
||||||
docker)
|
|
||||||
local need=()
|
|
||||||
! is_installed "docker.io" && need+=("docker.io")
|
|
||||||
! is_installed "docker-compose" && need+=("docker-compose")
|
|
||||||
if [ ${#need[@]} -gt 0 ]; then
|
|
||||||
_run_install_batch "${need[@]}"
|
|
||||||
else
|
|
||||||
echo "Docker already installed."
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
python3-pip)
|
|
||||||
local need=()
|
|
||||||
! is_installed "python3-pip" && need+=("python3-pip")
|
|
||||||
! is_installed "python3-venv" && need+=("python3-venv")
|
|
||||||
! is_installed "python3-dev" && need+=("python3-dev")
|
|
||||||
if [ ${#need[@]} -gt 0 ]; then
|
|
||||||
_run_install_batch "${need[@]}"
|
|
||||||
else
|
|
||||||
echo "Python 3 tools already installed."
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
jellyfin)
|
|
||||||
install_jellyfin
|
|
||||||
;;
|
|
||||||
openjdk-dev-env)
|
|
||||||
_install_dev_java
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
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}Servers & dev tools installed.${NC}"
|
|
||||||
}
|
|
||||||
_cat_security_bullseye() {
|
|
||||||
local item_count=5
|
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
|
||||||
choices=$(
|
|
||||||
_checklist "Security & Networking (Bullseye)" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
|
||||||
"wireshark" "Network protocol analyzer (GUI)" "$(_state wireshark)" \
|
|
||||||
"tcpdump" "Command-line packet analyzer" "$(_state tcpdump)" \
|
|
||||||
"fail2ban" "Brute-force protection daemon" "$(_state fail2ban)" \
|
|
||||||
"ufw" "Uncomplicated firewall" "$(_state ufw)" \
|
|
||||||
"clamav" "Antivirus engine (ClamAV)" "$(_state clamav)"
|
|
||||||
)
|
|
||||||
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 "Security & Networking (Bullseye)" "${_pkgs[@]}" || return
|
|
||||||
for pkg in "${_pkgs[@]}"; do
|
|
||||||
case $pkg in
|
|
||||||
clamav)
|
|
||||||
_install_clamav
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
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}Security & networking tools installed.${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
_cat_general_bullseye() {
|
|
||||||
local item_count=22
|
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
|
||||||
choices=$(
|
|
||||||
_checklist "System Tools (Bullseye)" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
|
||||||
"compress" "Compression tools (zip, unrar, p7zip)" "$(_state zip)" \
|
|
||||||
"conky-all" "System monitor for desktop" "$(_state conky-all)" \
|
|
||||||
"cpu-x" "CPU-X (alternative to CPU-Z)" "$(_state cpu-x)" \
|
|
||||||
"curl-wget" "HTTP transfer tools (curl, wget)" "$(_state curl)" \
|
|
||||||
"flatpak" "Flatpak sandbox (Bullseye native)" "$(_state flatpak)" \
|
|
||||||
"fwupd" "Firmware update daemon" "$(_state fwupd)" \
|
|
||||||
"gnome-disk-utility" "Disk management GUI" "$(_state gnome-disk-utility)" \
|
|
||||||
"gparted" "Partition editor" "$(_state gparted)" \
|
|
||||||
"htop" "Interactive process viewer" "$(_state htop)" \
|
|
||||||
"inxi" "System information tool" "$(_state inxi)" \
|
|
||||||
"jq" "JSON command-line processor" "$(_state jq)" \
|
|
||||||
"kvm" "QEMU/KVM virtualization" "$(_state virt-manager)" \
|
|
||||||
"lshw" "List hardware details" "$(_state lshw)" \
|
|
||||||
"mc" "Midnight Commander" "$(_state mc)" \
|
|
||||||
"nvme-cli" "NVMe SSD health monitoring" "$(_state nvme-cli)" \
|
|
||||||
"ncdu" "Disk usage analyzer" "$(_state ncdu)" \
|
|
||||||
"psensor" "Temperature monitor" "$(_state psensor)" \
|
|
||||||
"timeshift" "System restore snapshots" "$(_state timeshift)" \
|
|
||||||
"tmux" "Terminal multiplexer" "$(_state tmux)" \
|
|
||||||
"wine" "Windows compatibility layer" "$(_state wine)" \
|
|
||||||
"bleachbit" "System cleaner (GUI)" "$(_state bleachbit)" \
|
|
||||||
"gdebi" "Install .deb packages with deps (GUI)" "$(_state gdebi)"
|
|
||||||
)
|
|
||||||
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 "System Tools (Bullseye)" "${_pkgs[@]}" || return
|
|
||||||
for pkg in "${_pkgs[@]}"; 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")
|
|
||||||
[ ${#need[@]} -gt 0 ] && _run_install_batch "${need[@]}"
|
|
||||||
;;
|
|
||||||
curl-wget)
|
|
||||||
local need=()
|
|
||||||
! is_installed "curl" && need+=("curl")
|
|
||||||
! is_installed "wget" && need+=("wget")
|
|
||||||
[ ${#need[@]} -gt 0 ] && _run_install_batch "${need[@]}" || echo "Already installed."
|
|
||||||
;;
|
|
||||||
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
|
|
||||||
else
|
|
||||||
echo "QEMU/KVM already installed."
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
flatpak)
|
|
||||||
if ! is_installed "flatpak"; then
|
|
||||||
_run_cmd "Flatpak" "sudo apt install -y flatpak" "Installing Flatpak..."
|
|
||||||
flatpak remote-add --if-not-exists flathub \
|
|
||||||
https://dl.flathub.org/repo/flathub.flatpakrepo
|
|
||||||
echo -e "${GREEN}Flatpak + Flathub installed.${NC}"
|
|
||||||
else
|
|
||||||
echo "Flatpak 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
|
|
||||||
;;
|
|
||||||
fwupd)
|
|
||||||
if ! is_installed "fwupd"; then
|
|
||||||
_run_cmd "fwupd" "sudo apt install -y fwupd" "Installing fwupd..."
|
|
||||||
fi
|
|
||||||
if _confirm "Firmware Scan" "Scan for firmware updates now?"; then
|
|
||||||
sudo fwupdmgr refresh --force 2>/dev/null || true
|
|
||||||
sudo fwupdmgr get-updates 2>&1 || true
|
|
||||||
_pause
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
nvme-cli)
|
|
||||||
_run_install "nvme-cli"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
_cat_fetch_bullseye() {
|
|
||||||
local items=(
|
|
||||||
"neofetch" "System info fetcher" "$(_state neofetch)"
|
|
||||||
"screenfetch" "System info (BSD/Linux)" "$(_state screenfetch)"
|
|
||||||
"linuxlogo" "Linux logo + system info" "$(_state linuxlogo)"
|
|
||||||
)
|
|
||||||
local item_count=${#items[@]}
|
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
|
||||||
choices=$(_checklist "Fetch Tools (Bullseye)" "Select system info tools:" $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 "Fetch Tools (Bullseye)" "${_pkgs[@]}" || return
|
|
||||||
for pkg in "${_pkgs[@]}"; 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}Fetch tools installed.${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ======================================================================
|
|
||||||
# master installer — replaces install_extras() on Bullseye
|
|
||||||
# ======================================================================
|
|
||||||
install_extras_bullseye() {
|
|
||||||
echo -e "${YELLOW}Extra software — Bullseye mode (official repos only).${NC}"
|
|
||||||
_load_extras
|
|
||||||
|
|
||||||
while true; do
|
|
||||||
local cat_choice
|
|
||||||
cat_choice=$(
|
|
||||||
_menu "Extra Software — Bullseye" "Select a category:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
|
||||||
"0" "Essential Pack" \
|
|
||||||
"1" "Customization System" \
|
|
||||||
"2" "Download & Network" \
|
|
||||||
"3" "Internet (Browsers, Email)" \
|
|
||||||
"4" "Communication" \
|
|
||||||
"5" "Media Players" \
|
|
||||||
"6" "Multimedia & Design" \
|
|
||||||
"7" "Code Editors & IDEs" \
|
|
||||||
"8" "Servers & Dev Tools" \
|
|
||||||
"9" "Security & Networking" \
|
|
||||||
"10" "Software Centers" \
|
|
||||||
"11" "System Tools" \
|
|
||||||
"12" "Fetch / System Info" \
|
|
||||||
"13" "Back to main menu"
|
|
||||||
)
|
|
||||||
|
|
||||||
[ -z "$cat_choice" ] && return
|
|
||||||
clear
|
|
||||||
|
|
||||||
case "$cat_choice" in
|
|
||||||
0) _quick_install_bullseye ;;
|
|
||||||
1) _cat_customization_bullseye ;;
|
|
||||||
2) _cat_download_bullseye ;;
|
|
||||||
3) _cat_internet_bullseye ;;
|
|
||||||
4) _cat_communication ;;
|
|
||||||
5) _cat_players_bullseye ;;
|
|
||||||
6) _cat_design_bullseye ;;
|
|
||||||
7) _cat_programming_bullseye ;;
|
|
||||||
8) _cat_dev_bullseye ;;
|
|
||||||
9) _cat_security_bullseye ;;
|
|
||||||
10) _cat_software_centers_bullseye ;;
|
|
||||||
11) _cat_general_bullseye ;;
|
|
||||||
12) _cat_fetch_bullseye ;;
|
|
||||||
13) return ;;
|
|
||||||
esac
|
|
||||||
clear
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
_cat_software_centers_bullseye() {
|
|
||||||
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
|
|
||||||
|
|
||||||
if [ "$sc_choice" = "synaptic" ]; then
|
|
||||||
_run_cmd "Install" "sudo apt install -y synaptic" "Installing synaptic..."
|
|
||||||
echo -e "${GREEN}synaptic installed.${NC}"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
_run_cmd "Install" "sudo apt install -y $sc_choice" "Installing ${sc_choice}..."
|
|
||||||
|
|
||||||
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 store installed.${NC}"
|
|
||||||
}
|
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# legacy.sh — Bullseye: hardware detection, NVIDIA legacy drivers, lightweight gaming
|
|
||||||
# License GPL v3
|
|
||||||
|
|
||||||
BULLSEYE_USE_ARCHIVE=false
|
|
||||||
BULLSEYE_USE_ARCHIVE_CHECKED=false
|
|
||||||
|
|
||||||
check_bullseye_archive_phase() {
|
|
||||||
$BULLSEYE_USE_ARCHIVE_CHECKED && return
|
|
||||||
BULLSEYE_USE_ARCHIVE_CHECKED=true
|
|
||||||
|
|
||||||
local current_year
|
|
||||||
local current_month
|
|
||||||
current_year=$(date +%Y)
|
|
||||||
current_month=$(date +%-m)
|
|
||||||
|
|
||||||
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\
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# NVIDIA driver installer for Bullseye (Kepler → 470, Fermi → 390)
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
install_nvidia_bullseye() {
|
|
||||||
echo -e "${YELLOW}NVIDIA GPU detected (Bullseye mode).${NC}"
|
|
||||||
|
|
||||||
local is_fermi
|
|
||||||
is_fermi=$(is_nvidia_fermi)
|
|
||||||
|
|
||||||
local nv_pkg=""
|
|
||||||
local gpu_gen=""
|
|
||||||
|
|
||||||
if [ "$is_fermi" = "true" ]; then
|
|
||||||
nv_pkg="nvidia-legacy-390xx-driver"
|
|
||||||
gpu_gen="Fermi"
|
|
||||||
else
|
|
||||||
# Bullseye: cualquier otra NVIDIA (Kepler incl.) → nvidia-driver (470)
|
|
||||||
nv_pkg="nvidia-driver"
|
|
||||||
gpu_gen="NVIDIA"
|
|
||||||
fi
|
|
||||||
|
|
||||||
local nv_ver
|
|
||||||
nv_ver=$(apt-cache policy "$nv_pkg" 2>/dev/null | awk 'NR==3 {print $2; exit}')
|
|
||||||
|
|
||||||
local msg="Source: Debian Bullseye Stable\n"
|
|
||||||
msg+="Driver: ${gpu_gen} — ${nv_pkg} ${nv_ver:-unknown}\n"
|
|
||||||
msg+="[+] firmware-misc-nonfree\n"
|
|
||||||
msg+="[+] nvidia-settings\n\n"
|
|
||||||
msg+="Install NVIDIA driver?"
|
|
||||||
|
|
||||||
if ! _confirm "NVIDIA — Bullseye" "$msg" 14 70; then
|
|
||||||
echo "Skipping NVIDIA driver."
|
|
||||||
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}..."
|
|
||||||
|
|
||||||
local i386_active=false
|
|
||||||
dpkg --print-foreign-architectures | grep -q i386 && i386_active=true
|
|
||||||
if $i386_active; then
|
|
||||||
local nv32_pkg="${nv_pkg}:i386"
|
|
||||||
local nv32_ver
|
|
||||||
nv32_ver=$(apt-cache policy "$nv32_pkg" 2>/dev/null | awk 'NR==3 {print $2; exit}')
|
|
||||||
if [ -n "$nv32_ver" ] && [ "$nv32_ver" != "(none)" ]; then
|
|
||||||
echo "Installing 32-bit compatibility for ${nv_pkg}..."
|
|
||||||
_run_cmd "NVIDIA 32-bit" "sudo apt install -y $nv32_pkg" \
|
|
||||||
"Installing NVIDIA 32-bit libraries..."
|
|
||||||
else
|
|
||||||
echo "No 32-bit compatibility package available for ${nv_pkg} on Bullseye."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
export NVIDIA_DRIVER_MODE="stable"
|
|
||||||
echo -e "${GREEN}NVIDIA driver ${nv_pkg} installed. 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 "──────────────────────────────────────────────"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Lightweight Gaming for Bullseye — sin Steam, sin Heroic
|
|
||||||
# 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 \
|
|
||||||
"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 \
|
|
||||||
"retroarch" "RetroArch Emulator Frontend" OFF)
|
|
||||||
|
|
||||||
if [ -z "$choices" ]; then
|
|
||||||
echo "No gaming packages selected."
|
|
||||||
_pause
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
local cleaned
|
|
||||||
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
|
|
||||||
done
|
|
||||||
echo "$cleaned" | grep -qw i386 && need_32bit=true
|
|
||||||
|
|
||||||
local install_list
|
|
||||||
install_list=$(echo "$cleaned" | tr ' ' '\n' | grep -v '^i386$' | tr '\n' ' ')
|
|
||||||
install_list=${install_list% }
|
|
||||||
|
|
||||||
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
|
|
||||||
fi
|
|
||||||
|
|
||||||
if $need_32bit; then
|
|
||||||
echo "Installing 32-bit graphics drivers..."
|
|
||||||
if [ "$GPU_TYPE" = "nvidia" ]; then
|
|
||||||
_install_nvidia_32bit
|
|
||||||
else
|
|
||||||
_install_mesa_32bit
|
|
||||||
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
|
|
||||||
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" ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
|
|
||||||
echo -e "${GREEN}Gaming setup complete.${NC}"
|
|
||||||
_pause
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# repos.sh — Bullseye: classic repos with archive phase
|
|
||||||
# License GPL v3
|
|
||||||
|
|
||||||
configure_repos_bullseye() {
|
|
||||||
while true; do
|
|
||||||
local repo_choice
|
|
||||||
repo_choice=$(_menu "Configure Repositories — Bullseye" \
|
|
||||||
"Select an option:" $TUI_ALTO $TUI_ANCHO 6 \
|
|
||||||
"1" "Enable Contrib & Non-Free Components" \
|
|
||||||
"2" "Migrate to SID/Testing branch" \
|
|
||||||
"3" "Back to main menu")
|
|
||||||
[ -z "$repo_choice" ] && break
|
|
||||||
clear
|
|
||||||
case "$repo_choice" in
|
|
||||||
1) _repos_enable_components ;;
|
|
||||||
2) _branch_migration || true ;;
|
|
||||||
3) break ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
}
|
|
||||||
@@ -1,412 +0,0 @@
|
|||||||
#!/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" \
|
|
||||||
"Select an option:" $TUI_ALTO $TUI_ANCHO 6 \
|
|
||||||
"1" "Desktop Environment" \
|
|
||||||
"2" "Display Manager" \
|
|
||||||
"3" "Back to main menu")
|
|
||||||
[ -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 ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
display_manager_menu() {
|
|
||||||
while true; do
|
|
||||||
local -a dm_items=()
|
|
||||||
dm_items+=("1" "LightDM")
|
|
||||||
dm_items+=("2" "GDM3")
|
|
||||||
dm_items+=("3" "SDDM")
|
|
||||||
if [ "$DEBIAN_VERSION" = "12" ] || [ "$DEBIAN_VERSION" = "13" ]; then
|
|
||||||
dm_items+=("4" "greetd")
|
|
||||||
dm_items+=("5" "Back")
|
|
||||||
else
|
|
||||||
dm_items+=("4" "Back")
|
|
||||||
fi
|
|
||||||
local choice
|
|
||||||
choice=$(_menu "Display Manager" \
|
|
||||||
"Select an option:" $TUI_ALTO $TUI_ANCHO 6 \
|
|
||||||
"${dm_items[@]}")
|
|
||||||
[ -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 ;;
|
|
||||||
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")
|
|
||||||
local choices
|
|
||||||
choices=$(_checklist "LightDM Configuration" \
|
|
||||||
"Select options to apply:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
|
||||||
"${items[@]}")
|
|
||||||
if [ -z "$choices" ]; then
|
|
||||||
echo "No changes."
|
|
||||||
_pause
|
|
||||||
return
|
|
||||||
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
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
configure_greetd() {
|
|
||||||
while true; do
|
|
||||||
local -a gd_items=()
|
|
||||||
gd_items+=("1" "Install base greetd")
|
|
||||||
gd_items+=("2" "Install greetd + tuigreet (Recommended TUI greeter)")
|
|
||||||
if [ "$DEBIAN_VERSION" = "13" ]; then
|
|
||||||
gd_items+=("3" "Install gtkgreet (Requires manual setup)")
|
|
||||||
gd_items+=("4" "Install nwg-hello (Requires manual setup)")
|
|
||||||
gd_items+=("5" "Install wlgreet (Requires manual setup)")
|
|
||||||
gd_items+=("6" "Back")
|
|
||||||
else
|
|
||||||
gd_items+=("3" "Back")
|
|
||||||
fi
|
|
||||||
local choice
|
|
||||||
choice=$(_menu "greetd Configuration" \
|
|
||||||
"Select an option:" \
|
|
||||||
$TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
|
||||||
"${gd_items[@]}")
|
|
||||||
[ -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
|
|
||||||
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 ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
|
||||||
if [ "$DEBIAN_VERSION" = "12" ] || [ "$DEBIAN_VERSION" = "13" ]; then
|
|
||||||
if [ "$HAS_NVIDIA" = true ]; then
|
|
||||||
items_enabled+=("wayland" "Force Enable Wayland on NVIDIA (Risk!)" "OFF")
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
local choice
|
|
||||||
choice=$(_checklist "GDM3 Configuration" \
|
|
||||||
"Select options to apply:" \
|
|
||||||
$TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
|
||||||
"${items_enabled[@]}")
|
|
||||||
[ -z "$choice" ] && return
|
|
||||||
clear
|
|
||||||
|
|
||||||
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
|
|
||||||
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
|
|
||||||
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}"
|
|
||||||
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
|
|
||||||
choice=$(_menu "SDDM Configuration" \
|
|
||||||
"Select an option:" \
|
|
||||||
$TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
|
||||||
"1" "Install SDDM (Set as default)" \
|
|
||||||
"2" "Enable Autologin" \
|
|
||||||
"3" "Back")
|
|
||||||
[ -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
|
|
||||||
[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 ;;
|
|
||||||
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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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}"
|
|
||||||
}
|
|
||||||
@@ -9,9 +9,6 @@ _load_extras() {
|
|||||||
shopt -s nullglob
|
shopt -s nullglob
|
||||||
for _mod in "${_EXTRAS_DIR}"/*/*.sh "${_EXTRAS_DIR}"/*/*/*.sh; do
|
for _mod in "${_EXTRAS_DIR}"/*/*.sh "${_EXTRAS_DIR}"/*/*/*.sh; do
|
||||||
[ -f "$_mod" ] || continue
|
[ -f "$_mod" ] || continue
|
||||||
# Dynamic module discovery (glob): the concrete paths
|
|
||||||
# cannot be resolved statically.
|
|
||||||
# shellcheck disable=SC1090
|
|
||||||
source "$_mod"
|
source "$_mod"
|
||||||
done
|
done
|
||||||
shopt -u nullglob
|
shopt -u nullglob
|
||||||
@@ -30,23 +27,20 @@ install_extras() {
|
|||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
local cat_choice
|
local cat_choice
|
||||||
cat_choice=$(_menu "Install Programs and Software" \
|
cat_choice=$(whiptail --title "Extra Software" --menu \
|
||||||
"Select a category:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
"Select a category:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
||||||
"0" "Essential Pack" \
|
"0" "Essential Pack" \
|
||||||
"1" "Customization System" \
|
"1" "Customization System" \
|
||||||
"2" "Download & Network" \
|
"2" "Download & Network" \
|
||||||
"3" "Internet (Browsers, Email, VPN)" \
|
"3" "Internet (Browsers, Email, VPN)" \
|
||||||
"4" "Communication" \
|
"4" "Media Players" \
|
||||||
"5" "Media Players" \
|
"5" "Multimedia & Design" \
|
||||||
"6" "Multimedia & Design" \
|
"6" "Programming Applications" \
|
||||||
"7" "Code Editors & IDEs" \
|
"7" "Security & Networking" \
|
||||||
"8" "Servers & Dev Tools" \
|
"8" "System Tools" \
|
||||||
"9" "Security & Networking" \
|
"9" "Fetch / System Info" \
|
||||||
"10" "Software Center & Flatpak" \
|
"10" "Back to main menu" \
|
||||||
"11" "Office & Productivity" \
|
3>&1 1>&2 2>&3)
|
||||||
"12" "System Tools" \
|
|
||||||
"13" "Fetch / System Info" \
|
|
||||||
"14" "Back to main menu")
|
|
||||||
|
|
||||||
[ -z "$cat_choice" ] && return
|
[ -z "$cat_choice" ] && return
|
||||||
clear
|
clear
|
||||||
@@ -56,17 +50,13 @@ install_extras() {
|
|||||||
1) _cat_customization ;;
|
1) _cat_customization ;;
|
||||||
2) _cat_download ;;
|
2) _cat_download ;;
|
||||||
3) _cat_internet ;;
|
3) _cat_internet ;;
|
||||||
4) _cat_communication ;;
|
4) _cat_players ;;
|
||||||
5) _cat_players ;;
|
5) _cat_design ;;
|
||||||
6) _cat_design ;;
|
6) _cat_programming ;;
|
||||||
7) _cat_programming ;;
|
7) _cat_security ;;
|
||||||
8) _cat_dev ;;
|
8) _cat_general ;;
|
||||||
9) _cat_security ;;
|
9) _cat_fetch ;;
|
||||||
10) _cat_software_centers ;;
|
10) return ;;
|
||||||
11) _cat_office ;;
|
|
||||||
12) _cat_general ;;
|
|
||||||
13) _cat_fetch ;;
|
|
||||||
14) return ;;
|
|
||||||
esac
|
esac
|
||||||
clear
|
clear
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -1,24 +1,10 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Shared helpers for extras categories
|
# Shared helpers for extras categories
|
||||||
|
|
||||||
_install_clamav() {
|
_inst() {
|
||||||
if is_installed "clamav"; then
|
if is_installed "$1"; then echo " (installed)"; else echo ""; fi
|
||||||
echo "ClamAV already installed."
|
}
|
||||||
return
|
|
||||||
fi
|
_state() {
|
||||||
_run_install "clamav"
|
is_installed "$1" && echo "ON" || echo "OFF"
|
||||||
echo -e "${GREEN}ClamAV installed.${NC}"
|
|
||||||
|
|
||||||
if _confirm "ClamAV" "Update virus signatures now (freshclam)?"; then
|
|
||||||
sudo systemctl stop clamav-freshclam 2>/dev/null || true
|
|
||||||
sudo freshclam || true
|
|
||||||
sudo systemctl start clamav-freshclam 2>/dev/null || true
|
|
||||||
_pause
|
|
||||||
fi
|
|
||||||
|
|
||||||
if _confirm "ClamAV" "Run quick scan on /bin to verify engine works?"; then
|
|
||||||
echo "Running background scan on /bin..."
|
|
||||||
sudo clamscan --recursive --infected /bin &
|
|
||||||
echo "Scan running in background (PID $!). Check results later."
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# communication.sh — Signal, Telegram, HexChat
|
|
||||||
|
|
||||||
_install_signal() {
|
|
||||||
_ensure_extrepo
|
|
||||||
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
|
|
||||||
if ! is_installed "signal-desktop"; then
|
|
||||||
_run_cmd "Signal" "sudo apt install -y signal-desktop" "Installing Signal..."
|
|
||||||
echo -e "${GREEN}Signal installed.${NC}"
|
|
||||||
else
|
|
||||||
echo "Signal already installed."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_install_telegram() {
|
|
||||||
if [ "$DEBIAN_VERSION" = "13" ]; then
|
|
||||||
if ! is_backports_enabled; then
|
|
||||||
_msg "telegram-desktop" \
|
|
||||||
"telegram-desktop is only available in Trixie-backports.\n\n\
|
|
||||||
Please enable backports first via:\n Main Menu → 3 Configure Repositories" 12 65
|
|
||||||
echo -e "${YELLOW}Skipping telegram-desktop.${NC}"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
if ! is_installed "telegram-desktop"; then
|
|
||||||
_run_cmd "Telegram" "sudo apt install -y -t ${DEBIAN_CODENAME}-backports telegram-desktop" \
|
|
||||||
"Installing telegram-desktop from backports..."
|
|
||||||
echo -e "${GREEN}telegram-desktop installed.${NC}"
|
|
||||||
else
|
|
||||||
echo "telegram-desktop already installed."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
if ! is_installed "telegram-desktop"; then
|
|
||||||
_run_install "telegram-desktop"
|
|
||||||
echo -e "${GREEN}telegram-desktop installed.${NC}"
|
|
||||||
else
|
|
||||||
echo "telegram-desktop already installed."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_cat_communication() {
|
|
||||||
local headless=false
|
|
||||||
_is_headless && headless=true
|
|
||||||
if $headless; then
|
|
||||||
echo "Communication apps require a GUI — skipping."
|
|
||||||
return
|
|
||||||
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")
|
|
||||||
items+=(
|
|
||||||
"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[@]}
|
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
|
||||||
choices=$(_checklist "Communication" "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 '"')
|
|
||||||
# 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
|
|
||||||
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
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
echo -e "${GREEN}Communication tools installed.${NC}"
|
|
||||||
}
|
|
||||||
@@ -4,57 +4,48 @@
|
|||||||
_cat_design() {
|
_cat_design() {
|
||||||
local headless=false
|
local headless=false
|
||||||
_is_headless && headless=true
|
_is_headless && headless=true
|
||||||
local -a items=()
|
local audacity_state; audacity_state=$(_state "audacity")
|
||||||
if ! $headless; then
|
local ardour_state; ardour_state=$(_state "ardour")
|
||||||
local audacity_state; audacity_state=$(_state "audacity")
|
local blender_state; blender_state=$(_state "blender")
|
||||||
local ardour_state; ardour_state=$(_state "ardour")
|
local ffmpeg_state; ffmpeg_state=$(_state "ffmpeg")
|
||||||
local blender_state; blender_state=$(_state "blender")
|
local gimp_state; gimp_state=$(_state "gimp")
|
||||||
local gimp_state; gimp_state=$(_state "gimp")
|
local handbrake_state; handbrake_state=$(_state "handbrake")
|
||||||
local handbrake_state; handbrake_state=$(_state "handbrake")
|
local inkscape_state; inkscape_state=$(_state "inkscape")
|
||||||
local inkscape_state; inkscape_state=$(_state "inkscape")
|
local kdenlive_state; kdenlive_state=$(_state "kdenlive")
|
||||||
local kdenlive_state; kdenlive_state=$(_state "kdenlive")
|
local krita_state; krita_state=$(_state "krita")
|
||||||
local krita_state; krita_state=$(_state "krita")
|
local obs_state; obs_state=$(_state "obs-studio")
|
||||||
local obs_state; obs_state=$(_state "obs-studio")
|
local openshot_state; openshot_state=$(_state "openshot-qt")
|
||||||
local openshot_state; openshot_state=$(_state "openshot-qt")
|
local scribus_state; scribus_state=$(_state "scribus")
|
||||||
local scribus_state; scribus_state=$(_state "scribus")
|
local shotcut_state; shotcut_state=$(_state "shotcut")
|
||||||
local shotcut_state; shotcut_state=$(_state "shotcut")
|
|
||||||
items+=(
|
|
||||||
"audacity" "Audio editor/recorder" "$audacity_state"
|
|
||||||
"ardour" "Digital audio workstation" "$ardour_state"
|
|
||||||
"blender" "3D modeling/animation" "$blender_state"
|
|
||||||
"gimp" "Image editor" "$gimp_state"
|
|
||||||
"handbrake" "Video transcoder" "$handbrake_state"
|
|
||||||
"inkscape" "Vector graphics editor" "$inkscape_state"
|
|
||||||
"kdenlive" "Video editor (KDE)" "$kdenlive_state"
|
|
||||||
"krita" "Digital painting" "$krita_state"
|
|
||||||
"obs-studio" "Screen recording" "$obs_state"
|
|
||||||
"openshot-qt" "Simple video editor" "$openshot_state"
|
|
||||||
"scribus" "Desktop publishing" "$scribus_state"
|
|
||||||
"shotcut" "Cross-platform video editor" "$shotcut_state"
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
local ffmpeg_state; ffmpeg_state=$(_state "ffmpeg")
|
|
||||||
items+=("ffmpeg" "Multimedia framework (CLI)" "$ffmpeg_state")
|
|
||||||
|
|
||||||
local item_count=${#items[@]}
|
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
local choices
|
||||||
choices=$(_checklist "Multimedia & Design" "Check [*] the packages you want installed/updated on your system.\n" \
|
choices=$(whiptail --title "Multimedia & Design" --checklist \
|
||||||
$TUI_ALTO $TUI_ANCHO $lista_alto "${items[@]}")
|
"Select multimedia and design tools:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
||||||
|
"audacity" "Audio editor/recorder$(_inst audacity)" "$audacity_state" \
|
||||||
|
"ardour" "Digital audio workstation$(_inst ardour)" "$ardour_state" \
|
||||||
|
"blender" "3D modeling/animation suite$(_inst blender)" "$blender_state" \
|
||||||
|
"ffmpeg" "Multimedia framework (CLI)$(_inst ffmpeg)" "$ffmpeg_state" \
|
||||||
|
"gimp" "Image editor (Photoshop alternative)$(_inst gimp)" "$gimp_state" \
|
||||||
|
"handbrake" "Video transcoder (DVD ripper)$(_inst handbrake)" "$handbrake_state" \
|
||||||
|
"inkscape" "Vector graphics editor$(_inst inkscape)" "$inkscape_state" \
|
||||||
|
"kdenlive" "Video editor (KDE)$(_inst kdenlive)" "$kdenlive_state" \
|
||||||
|
"krita" "Digital painting/illustration$(_inst krita)" "$krita_state" \
|
||||||
|
"obs-studio" "Screen recording/streaming$(_inst obs-studio)" "$obs_state" \
|
||||||
|
"openshot-qt" "Video editor (simple)$(_inst openshot-qt)" "$openshot_state" \
|
||||||
|
"scribus" "Desktop publishing (DTP)$(_inst scribus)" "$scribus_state" \
|
||||||
|
"shotcut" "Video editor (cross-platform)$(_inst shotcut)" "$shotcut_state" \
|
||||||
|
3>&1 1>&2 2>&3)
|
||||||
clear
|
clear
|
||||||
|
|
||||||
[ -z "$choices" ] && return
|
[ -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,
|
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
|
||||||
# before any apt transaction starts.
|
|
||||||
_confirm_install_list "Multimedia & Design" "${_pkgs[@]}" || return
|
for pkg in $cleaned; do
|
||||||
for pkg in "${_pkgs[@]}"; do
|
if $headless; then
|
||||||
|
echo "Skipping $pkg (headless mode)"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
if ! is_installed "$pkg"; then
|
if ! is_installed "$pkg"; then
|
||||||
_run_install "$pkg"
|
_run_install "$pkg"
|
||||||
else
|
else
|
||||||
@@ -62,8 +53,5 @@ _cat_design() {
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
|
|
||||||
echo -e "${GREEN}Multimedia & design tools installed.${NC}"
|
echo -e "${GREEN}Multimedia & design tools installed.${NC}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
# dev.sh — Development & Servers (extrepo, zenmap, fail2ban, ufw moved out)
|
# dev.sh — Development & Servers (extrepo, zenmap, fail2ban, ufw moved out)
|
||||||
|
|
||||||
_cat_dev() {
|
_cat_dev() {
|
||||||
local -a items=()
|
local headless=false
|
||||||
|
_is_headless && headless=true
|
||||||
local apache_state; apache_state=$(_state "apache2")
|
local apache_state; apache_state=$(_state "apache2")
|
||||||
local build_state; build_state=$(_state "build-essential")
|
local build_state; build_state=$(_state "build-essential")
|
||||||
local certbot_state; certbot_state=$(_state "certbot")
|
local certbot_state; certbot_state=$(_state "certbot")
|
||||||
@@ -16,48 +17,32 @@ _cat_dev() {
|
|||||||
local pip_state; pip_state=$(_state "python3-pip")
|
local pip_state; pip_state=$(_state "python3-pip")
|
||||||
local redis_state; redis_state=$(_state "redis-server")
|
local redis_state; redis_state=$(_state "redis-server")
|
||||||
local sqlite_state; sqlite_state=$(_state "sqlite3")
|
local sqlite_state; sqlite_state=$(_state "sqlite3")
|
||||||
local jdk_desc; jdk_desc=$(_any_jdk_installed_desc)
|
|
||||||
local jdk_state; jdk_state=$(_any_jdk_state)
|
|
||||||
items+=(
|
|
||||||
"apache2" "Apache web server" "$apache_state"
|
|
||||||
"build-essential" "C/C++ build tools (gcc, make)" "$build_state"
|
|
||||||
"certbot" "Let's Encrypt TLS certificates" "$certbot_state"
|
|
||||||
"docker" "Docker + docker-compose" "$docker_state"
|
|
||||||
"mariadb-server" "MariaDB database server" "$mariadb_state"
|
|
||||||
"netcat-openbsd" "TCP/IP networking utility" "$netcat_state"
|
|
||||||
"nginx" "Nginx web server" "$nginx_state"
|
|
||||||
"openssh-server" "SSH server" "$ssh_state"
|
|
||||||
"openssl" "OpenSSL cryptography toolkit" "$openssl_state"
|
|
||||||
"postgresql" "PostgreSQL database server" "$pg_state"
|
|
||||||
"python3-pip" "Python 3 pip + venv + dev" "$pip_state"
|
|
||||||
"redis-server" "Redis key-value store" "$redis_state"
|
|
||||||
"sqlite3" "SQLite database engine" "$sqlite_state"
|
|
||||||
"jellyfin" "Jellyfin Media Server (Web GUI on port 8096)" OFF
|
|
||||||
"openjdk-dev-env" "Adoptium Temurin JDK (17, 21, 25 LTS)${jdk_desc}" "${jdk_state}"
|
|
||||||
)
|
|
||||||
|
|
||||||
local item_count=${#items[@]}
|
local TUI_ANCHO_REFORZADO=$((TUI_ANCHO + 6))
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
local choices
|
||||||
choices=$(_checklist "Development & Servers" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
choices=$(whiptail --title "Development & Servers" --checklist \
|
||||||
"${items[@]}" \
|
"Select development tools and servers (12 items, ↑↓ scroll):" $TUI_ALTO $TUI_ANCHO_REFORZADO $TUI_ALTO_LISTA \
|
||||||
)
|
"apache2" "Apache web server$(_inst apache2)" "$apache_state" \
|
||||||
|
"build-essential" "C/C++ build tools (gcc, make)$(_inst build-essential)" "$build_state" \
|
||||||
|
"certbot" "Let's Encrypt TLS certificates$(_inst certbot)" "$certbot_state" \
|
||||||
|
"docker" "Docker + docker-compose$(_inst docker.io)" "$docker_state" \
|
||||||
|
"mariadb-server" "MariaDB database server$(_inst mariadb-server)" "$mariadb_state" \
|
||||||
|
"netcat-openbsd" "TCP/IP networking utility$(_inst netcat-openbsd)" "$netcat_state" \
|
||||||
|
"nginx" "Nginx web server$(_inst nginx)" "$nginx_state" \
|
||||||
|
"openssh-server" "SSH server$(_inst openssh-server)" "$ssh_state" \
|
||||||
|
"openssl" "OpenSSL cryptography toolkit$(_inst openssl)" "$openssl_state" \
|
||||||
|
"postgresql" "PostgreSQL database server$(_inst postgresql)" "$pg_state" \
|
||||||
|
"python3-pip" "Python 3 pip + venv + dev$(_inst python3-pip)" "$pip_state" \
|
||||||
|
"redis-server" "Redis key-value store$(_inst redis-server)" "$redis_state" \
|
||||||
|
"sqlite3" "SQLite database engine$(_inst sqlite3)" "$sqlite_state" \
|
||||||
|
3>&1 1>&2 2>&3)
|
||||||
clear
|
clear
|
||||||
|
|
||||||
[ -z "$choices" ] && return
|
[ -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.
|
for pkg in $cleaned; do
|
||||||
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
|
|
||||||
case $pkg in
|
case $pkg in
|
||||||
python3-pip)
|
python3-pip)
|
||||||
local need=()
|
local need=()
|
||||||
@@ -80,12 +65,6 @@ _cat_dev() {
|
|||||||
echo "Docker already installed."
|
echo "Docker already installed."
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
jellyfin)
|
|
||||||
install_jellyfin
|
|
||||||
;;
|
|
||||||
openjdk-dev-env)
|
|
||||||
_install_dev_java
|
|
||||||
;;
|
|
||||||
*)
|
*)
|
||||||
if ! is_installed "$pkg"; then
|
if ! is_installed "$pkg"; then
|
||||||
_run_install "$pkg"
|
_run_install "$pkg"
|
||||||
@@ -96,8 +75,5 @@ _cat_dev() {
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
|
|
||||||
echo -e "${GREEN}Development tools and servers installed.${NC}"
|
echo -e "${GREEN}Development tools and servers installed.${NC}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# jellyfin.sh — Jellyfin Media Server installation (extrepo)
|
|
||||||
|
|
||||||
_enable_jellyfin_repo() {
|
|
||||||
if [ ! -f /etc/apt/sources.list.d/extrepo_jellyfin.sources ]; then
|
|
||||||
if ! command -v extrepo &>/dev/null; then
|
|
||||||
_run_cmd "extrepo" "sudo apt install -y extrepo" "Installing extrepo..."
|
|
||||||
fi
|
|
||||||
_run_cmd "Jellyfin" "sudo extrepo enable jellyfin" "Enabling Jellyfin repository..."
|
|
||||||
fi
|
|
||||||
_ensure_apt_updated
|
|
||||||
}
|
|
||||||
|
|
||||||
install_jellyfin() {
|
|
||||||
local ver
|
|
||||||
ver=$(apt-cache policy jellyfin 2>/dev/null | awk 'NR==3 {print $2; exit}')
|
|
||||||
|
|
||||||
if _confirm "Install: Jellyfin" "Install Jellyfin Media Server
|
|
||||||
Repository: repo.jellyfin.org/debian
|
|
||||||
Version: ${ver:-unknown}"; then
|
|
||||||
|
|
||||||
_enable_jellyfin_repo
|
|
||||||
_run_cmd "Jellyfin" "sudo DEBIAN_FRONTEND=noninteractive apt install -y jellyfin" "Installing Jellyfin..."
|
|
||||||
|
|
||||||
echo -e "${GREEN}Jellyfin Server installed. Web interface available at http://localhost:8096${NC}"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
@@ -4,57 +4,43 @@
|
|||||||
_cat_download() {
|
_cat_download() {
|
||||||
local headless=false
|
local headless=false
|
||||||
_is_headless && headless=true
|
_is_headless && headless=true
|
||||||
local -a items1=()
|
|
||||||
local -a items2=()
|
|
||||||
local aria2_state; aria2_state=$(_state "aria2")
|
local aria2_state; aria2_state=$(_state "aria2")
|
||||||
|
local filezilla_state; filezilla_state=$(_state "filezilla")
|
||||||
local ytdlp_state; ytdlp_state=$(_state "yt-dlp")
|
local ytdlp_state; ytdlp_state=$(_state "yt-dlp")
|
||||||
|
local ytdlp_gui_state; ytdlp_gui_state=$(_state "youtubedl-gui")
|
||||||
|
|
||||||
|
local deluge_state; deluge_state=$(_state "deluge")
|
||||||
local deluged_state; deluged_state=$(_state "deluged")
|
local deluged_state; deluged_state=$(_state "deluged")
|
||||||
local mktorrent_state; mktorrent_state=$(_state "mktorrent")
|
local mktorrent_state; mktorrent_state=$(_state "mktorrent")
|
||||||
|
local qbit_state; qbit_state=$(_state "qbittorrent")
|
||||||
local qbitnox_state; qbitnox_state=$(_state "qbittorrent-nox")
|
local qbitnox_state; qbitnox_state=$(_state "qbittorrent-nox")
|
||||||
local tr_cli_state; tr_cli_state=$(_state "transmission-cli")
|
local tr_cli_state; tr_cli_state=$(_state "transmission-cli")
|
||||||
items1+=(
|
local tr_gtk_state; tr_gtk_state=$(_state "transmission-gtk")
|
||||||
"aria2" "Multiprotocol downloader (CLI)" "$aria2_state"
|
local tr_qt_state; tr_qt_state=$(_state "transmission-qt")
|
||||||
"yt-dlp" "Video downloader CLI" "$ytdlp_state"
|
|
||||||
)
|
|
||||||
items2+=(
|
|
||||||
"deluged" "BitTorrent daemon/server" "$deluged_state"
|
|
||||||
"mktorrent" "Torrent metainfo creator (CLI)" "$mktorrent_state"
|
|
||||||
"qbittorrent-nox" "BitTorrent WebUI/CLI" "$qbitnox_state"
|
|
||||||
"transmission-cli" "BitTorrent client (CLI)" "$tr_cli_state"
|
|
||||||
)
|
|
||||||
if ! $headless; then
|
|
||||||
local filezilla_state; filezilla_state=$(_state "filezilla")
|
|
||||||
local ytdlp_gui_state; ytdlp_gui_state=$(_state "youtubedl-gui")
|
|
||||||
local deluge_state; deluge_state=$(_state "deluge")
|
|
||||||
local qbit_state; qbit_state=$(_state "qbittorrent")
|
|
||||||
local tr_gtk_state; tr_gtk_state=$(_state "transmission-gtk")
|
|
||||||
local tr_qt_state; tr_qt_state=$(_state "transmission-qt")
|
|
||||||
items1+=(
|
|
||||||
"filezilla" "FTP/SFTP client (GUI)" "$filezilla_state"
|
|
||||||
"youtubedl-gui" "GUI for yt-dlp" "$ytdlp_gui_state"
|
|
||||||
)
|
|
||||||
items2+=(
|
|
||||||
"deluge" "BitTorrent client (GTK)" "$deluge_state"
|
|
||||||
"qbittorrent" "BitTorrent client (Qt)" "$qbit_state"
|
|
||||||
"transmission-gtk" "BitTorrent client (GTK)" "$tr_gtk_state"
|
|
||||||
"transmission-qt" "BitTorrent client (Qt)" "$tr_qt_state"
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
|
|
||||||
local item_count1=${#items1[@]}
|
local TUI_ANCHO_REFORZADO=$((TUI_ANCHO + 6))
|
||||||
local lista_alto1=$((item_count1 > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count1))
|
|
||||||
local choices1 choices2=""
|
local choices1 choices2=""
|
||||||
|
|
||||||
choices1=$(_checklist "Downloaders" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto1 \
|
choices1=$(whiptail --title "Downloaders" --checklist \
|
||||||
"${items1[@]}" \
|
"Select download tools:" $TUI_ALTO $TUI_ANCHO_REFORZADO $TUI_ALTO_LISTA \
|
||||||
)
|
"aria2" "Multiprotocol downloader (CLI)$(_inst aria2)" "$aria2_state" \
|
||||||
|
"filezilla" "FTP/SFTP client (GUI)$(_inst filezilla)" "$filezilla_state" \
|
||||||
|
"yt-dlp" "Video downloader CLI$(_inst yt-dlp)" "$ytdlp_state" \
|
||||||
|
"youtubedl-gui" "GUI for yt-dlp$(_inst youtubedl-gui)" "$ytdlp_gui_state" \
|
||||||
|
3>&1 1>&2 2>&3)
|
||||||
clear
|
clear
|
||||||
|
|
||||||
local item_count2=${#items2[@]}
|
choices2=$(whiptail --title "Torrent Clients" --checklist \
|
||||||
local lista_alto2=$((item_count2 > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count2))
|
"Select torrent clients:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
||||||
choices2=$(_checklist "Torrent Clients" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto2 \
|
"deluge" "BitTorrent client (GTK)$(_inst deluge)" "$deluge_state" \
|
||||||
"${items2[@]}" \
|
"deluged" "BitTorrent daemon/server$(_inst deluged)" "$deluged_state" \
|
||||||
)
|
"mktorrent" "Torrent metainfo creator (CLI)$(_inst mktorrent)" "$mktorrent_state" \
|
||||||
|
"qbittorrent" "BitTorrent client (Qt)$(_inst qbittorrent)" "$qbit_state" \
|
||||||
|
"qbittorrent-nox" "BitTorrent WebUI/CLI$(_inst qbittorrent-nox)" "$qbitnox_state" \
|
||||||
|
"transmission-cli" "BitTorrent client (CLI)$(_inst transmission-cli)" "$tr_cli_state" \
|
||||||
|
"transmission-gtk" "BitTorrent client (GTK)$(_inst transmission-gtk)" "$tr_gtk_state" \
|
||||||
|
"transmission-qt" "BitTorrent client (Qt)$(_inst transmission-qt)" "$tr_qt_state" \
|
||||||
|
3>&1 1>&2 2>&3)
|
||||||
clear
|
clear
|
||||||
|
|
||||||
local cleaned
|
local cleaned
|
||||||
@@ -62,16 +48,7 @@ _cat_download() {
|
|||||||
|
|
||||||
[ -z "$cleaned" ] && { echo "No download tools selected."; return; }
|
[ -z "$cleaned" ] && { echo "No download tools selected."; return; }
|
||||||
|
|
||||||
# BH-004: Convert to array to avoid word splitting and injection.
|
for pkg in $cleaned; do
|
||||||
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
|
|
||||||
case $pkg in
|
case $pkg in
|
||||||
yt-dlp)
|
yt-dlp)
|
||||||
install_backports_or_stable yt-dlp
|
install_backports_or_stable yt-dlp
|
||||||
@@ -83,6 +60,10 @@ _cat_download() {
|
|||||||
install_backports_or_stable qbittorrent-nox
|
install_backports_or_stable qbittorrent-nox
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
|
if $headless; then
|
||||||
|
echo "Skipping $pkg (headless mode)"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
if ! is_installed "$pkg"; then
|
if ! is_installed "$pkg"; then
|
||||||
_run_install "$pkg"
|
_run_install "$pkg"
|
||||||
else
|
else
|
||||||
@@ -92,8 +73,5 @@ _cat_download() {
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
|
|
||||||
echo -e "${GREEN}Download & network tools installed.${NC}"
|
echo -e "${GREEN}Download & network tools installed.${NC}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,29 +3,19 @@
|
|||||||
|
|
||||||
_quick_install() {
|
_quick_install() {
|
||||||
local fetch_pkg
|
local fetch_pkg
|
||||||
if [ "$DEBIAN_CODENAME" = "trixie" ]; then
|
if [ "$DEBIAN_CODENAME" = "bookworm" ]; then
|
||||||
fetch_pkg="fastfetch"
|
|
||||||
else
|
|
||||||
fetch_pkg="neofetch"
|
fetch_pkg="neofetch"
|
||||||
fi
|
|
||||||
|
|
||||||
local -a comp_pkgs
|
|
||||||
if [ "$DEBIAN_CODENAME" = "trixie" ]; then
|
|
||||||
comp_pkgs=(7zip 7zip-rar)
|
|
||||||
else
|
else
|
||||||
comp_pkgs=(p7zip-full p7zip-rar)
|
fetch_pkg="fastfetch"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_msg "Essential Pack" \
|
_msg "Essential Pack" \
|
||||||
"Install basic programs:\n\n - Compression (zip, unrar, 7z)\n - System tools (htop, inxi, ${fetch_pkg})\n - Networking (curl, wget, ufw)\n - CA certs & GPG\n - VLC media player" 13 60
|
"Install basic programs:\n\n - Compression (zip, unrar, 7z)\n - System tools (htop, inxi, ${fetch_pkg})\n - VLC media player\n - Microsoft fonts" 13 60
|
||||||
|
|
||||||
local quick_pkgs=(
|
local quick_pkgs=(
|
||||||
zip unzip rar unrar
|
zip unzip rar unrar p7zip-full p7zip-rar
|
||||||
"${comp_pkgs[@]}"
|
"$fetch_pkg" htop vlc inxi ttf-mscorefonts-installer
|
||||||
"$fetch_pkg" htop inxi curl wget ufw
|
|
||||||
ca-certificates gnupg lsb-release
|
|
||||||
)
|
)
|
||||||
_is_headless || quick_pkgs+=(vlc)
|
|
||||||
_run_install_batch "${quick_pkgs[@]}"
|
_run_install_batch "${quick_pkgs[@]}"
|
||||||
echo -e "${GREEN}Essential Pack installed.${NC}"
|
echo -e "${GREEN}Essential Pack installed.${NC}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,45 +18,36 @@ _cat_fetch() {
|
|||||||
hyfetch_state=$(_state "hyfetch")
|
hyfetch_state=$(_state "hyfetch")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local -a items=()
|
local items=()
|
||||||
|
|
||||||
if [ "$fetch_pkg" = "fastfetch" ]; then
|
if [ "$fetch_pkg" = "fastfetch" ]; then
|
||||||
items+=("fastfetch" "System info fetcher" "$fetch_state")
|
items+=("fastfetch" "System info fetcher$(_inst fastfetch)" "$fetch_state")
|
||||||
if [ "$DEBIAN_CODENAME" = "trixie" ]; then
|
if [ "$DEBIAN_CODENAME" = "trixie" ]; then
|
||||||
items+=("hyfetch" "Neofetch with pride flags" "$hyfetch_state")
|
items+=("hyfetch" "Neofetch with pride flags$(_inst hyfetch)" "$hyfetch_state")
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
items+=("linuxlogo" "Linux logo + system info" "$linuxlogo_state")
|
items+=("linuxlogo" "Linux logo + system info$(_inst linuxlogo)" "$linuxlogo_state")
|
||||||
if [ "$fetch_pkg" = "neofetch" ]; then
|
if [ "$fetch_pkg" = "neofetch" ]; then
|
||||||
items+=("neofetch" "System info fetcher" "$fetch_state")
|
items+=("neofetch" "System info fetcher$(_inst neofetch)" "$fetch_state")
|
||||||
if [ "$DEBIAN_CODENAME" = "trixie" ]; then
|
if [ "$DEBIAN_CODENAME" = "trixie" ]; then
|
||||||
items+=("hyfetch" "Neofetch with pride flags" "$hyfetch_state")
|
items+=("hyfetch" "Neofetch with pride flags$(_inst hyfetch)" "$hyfetch_state")
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
items+=("screenfetch" "System info (BSD/Linux)" "$screenfetch_state")
|
items+=("screenfetch" "System info (BSD/Linux)$(_inst screenfetch)" "$screenfetch_state")
|
||||||
|
|
||||||
local item_count=${#items[@]}
|
local TUI_ANCHO_REFORZADO=$((TUI_ANCHO + 6))
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
local choices
|
||||||
choices=$(_checklist "Fetch Tools" "Select system info tools:" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
choices=$(whiptail --title "Fetch Tools" --checklist \
|
||||||
|
"Select system info tools:" $TUI_ALTO $TUI_ANCHO_REFORZADO $TUI_ALTO_LISTA \
|
||||||
"${items[@]}" \
|
"${items[@]}" \
|
||||||
)
|
3>&1 1>&2 2>&3)
|
||||||
clear
|
clear
|
||||||
|
|
||||||
[ -z "$choices" ] && return
|
[ -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.
|
for pkg in $cleaned; do
|
||||||
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
|
|
||||||
case $pkg in
|
case $pkg in
|
||||||
neofetch|fastfetch)
|
neofetch|fastfetch)
|
||||||
if ! is_installed "$pkg"; then
|
if ! is_installed "$pkg"; then
|
||||||
@@ -75,8 +66,5 @@ _cat_fetch() {
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
|
|
||||||
echo -e "${GREEN}Fetch tools installed.${NC}"
|
echo -e "${GREEN}Fetch tools installed.${NC}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,389 +1,181 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# internet.sh — Browsers, email, VPN
|
# internet.sh — Browsers, email, VPN (was _cat_browsers, now includes riseup-vpn)
|
||||||
# extrepo-powered: mozilla, floorp, palemoon, librewolf, tailscale, mullvad, protonvpn
|
|
||||||
# firefox-esr from Debian repos (with locale auto-detect)
|
|
||||||
|
|
||||||
# ── Helpers extrepo ──
|
|
||||||
_ensure_extrepo() {
|
|
||||||
if ! command -v extrepo &>/dev/null; then
|
|
||||||
_run_cmd "extrepo" "sudo apt install -y extrepo" "Installing extrepo..."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_enable_mozilla_repo() {
|
|
||||||
if [ ! -f /etc/apt/sources.list.d/extrepo_mozilla.sources ]; then
|
|
||||||
_ensure_extrepo
|
|
||||||
_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'
|
|
||||||
Package: *
|
|
||||||
Pin: origin packages.mozilla.org
|
|
||||||
Pin-Priority: 1000
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
_ensure_apt_updated
|
|
||||||
}
|
|
||||||
|
|
||||||
_enable_floorp_repo() {
|
|
||||||
if [ ! -f /etc/apt/sources.list.d/extrepo_floorp.sources ]; then
|
|
||||||
_ensure_extrepo
|
|
||||||
_run_cmd "Floorp" "sudo extrepo enable floorp" "Enabling Floorp repository..."
|
|
||||||
fi
|
|
||||||
_ensure_apt_updated
|
|
||||||
}
|
|
||||||
|
|
||||||
install_palemoon() {
|
|
||||||
if [ "$DEBIAN_VERSION" -lt 12 ] 2>/dev/null; then
|
|
||||||
_msg "Pale Moon" "Pale Moon is only available on\nDebian 12 (Bookworm) and 13 (Trixie).\n\nSkipping installation." 10 60
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
local cpu_flags cpu_label REPO_PALEMOON
|
|
||||||
cpu_flags=$(grep -m1 '^flags' /proc/cpuinfo 2>/dev/null)
|
|
||||||
|
|
||||||
if echo "$cpu_flags" | grep -q 'avx2'; then
|
|
||||||
REPO_PALEMOON="palemoon_avx2_gtk3"
|
|
||||||
cpu_label="AVX2"
|
|
||||||
elif echo "$cpu_flags" | grep -q 'avx'; then
|
|
||||||
REPO_PALEMOON="palemoon_avx_gtk3"
|
|
||||||
cpu_label="AVX"
|
|
||||||
else
|
|
||||||
REPO_PALEMOON="palemoon_sse2_gtk3"
|
|
||||||
cpu_label="SSE2"
|
|
||||||
fi
|
|
||||||
|
|
||||||
_msg "Pale Moon" "CPU detected: ${cpu_label} support.\n\nEnabling optimized repository:\n ${REPO_PALEMOON}" 10 60
|
|
||||||
|
|
||||||
if ls /etc/apt/sources.list.d/extrepo_palemoon_*.sources &>/dev/null; then
|
|
||||||
echo "Pale Moon repository already enabled."
|
|
||||||
else
|
|
||||||
_ensure_extrepo
|
|
||||||
_run_cmd "Pale Moon" "sudo extrepo enable ${REPO_PALEMOON}" "Enabling ${REPO_PALEMOON}..."
|
|
||||||
_ensure_apt_updated
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! is_installed "palemoon"; then
|
|
||||||
_run_cmd "Pale Moon" "sudo apt install -y palemoon" "Installing Pale Moon..."
|
|
||||||
echo -e "${GREEN}Pale Moon installed.${NC}"
|
|
||||||
else
|
|
||||||
echo "Pale Moon already installed."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_enable_librewolf_repo() {
|
|
||||||
if [ ! -f /etc/apt/sources.list.d/extrepo_librewolf.sources ]; then
|
|
||||||
_ensure_extrepo
|
|
||||||
_run_cmd "LibreWolf" "sudo extrepo enable librewolf" "Enabling LibreWolf repository..."
|
|
||||||
fi
|
|
||||||
_ensure_apt_updated
|
|
||||||
}
|
|
||||||
|
|
||||||
_enable_tailscale_repo() {
|
|
||||||
if [ ! -f /etc/apt/sources.list.d/extrepo_tailscale.sources ]; then
|
|
||||||
_ensure_extrepo
|
|
||||||
_run_cmd "Tailscale" "sudo extrepo enable tailscale" "Enabling Tailscale repository..."
|
|
||||||
fi
|
|
||||||
_ensure_apt_updated
|
|
||||||
}
|
|
||||||
|
|
||||||
_enable_mullvad_repo() {
|
|
||||||
if [ ! -f /etc/apt/sources.list.d/extrepo_mullvad.sources ]; then
|
|
||||||
_ensure_extrepo
|
|
||||||
_run_cmd "Mullvad" "sudo extrepo enable mullvad" "Enabling Mullvad repository..."
|
|
||||||
fi
|
|
||||||
_ensure_apt_updated
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
else
|
|
||||||
echo "ProtonVPN repository already enabled."
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! is_installed "proton-vpn-gtk-app"; then
|
|
||||||
_msg "ProtonVPN" "Installing Proton VPN GTK client\nfrom the official Proton repository." 10 60
|
|
||||||
_run_cmd "ProtonVPN" "sudo apt install -y proton-vpn-gtk-app" "Installing Proton VPN GTK app..."
|
|
||||||
echo -e "${GREEN}ProtonVPN installed.${NC}"
|
|
||||||
else
|
|
||||||
echo "Proton VPN GTK app already installed."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── Categories ──
|
|
||||||
_cat_internet() {
|
_cat_internet() {
|
||||||
local headless=false
|
local headless=false
|
||||||
_is_headless && headless=true
|
_is_headless && headless=true
|
||||||
local -a items=()
|
local chromium_state; chromium_state=$(_state "chromium")
|
||||||
if ! $headless; then
|
local dillo_state; dillo_state=$(_state "dillo")
|
||||||
local chromium_state
|
local elinks_state; elinks_state=$(_state "elinks")
|
||||||
chromium_state=$(_state "chromium")
|
local epiphany_state; epiphany_state=$(_state "epiphany-browser")
|
||||||
local dillo_state
|
local falkon_state; falkon_state=$(_state "falkon")
|
||||||
dillo_state=$(_state "dillo")
|
local firefox_state="OFF"
|
||||||
local epiphany_state
|
if command -v firefox &>/dev/null && ! command -v firefox-esr &>/dev/null; then
|
||||||
epiphany_state=$(_state "epiphany-browser")
|
firefox_state="ON"
|
||||||
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")
|
|
||||||
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"
|
|
||||||
)
|
|
||||||
fi
|
fi
|
||||||
local elinks_state
|
local floorp_state; floorp_state=$(_state "floorp")
|
||||||
elinks_state=$(_state "elinks")
|
local konqueror_state; konqueror_state=$(_state "konqueror")
|
||||||
local riseupvpn_state
|
local librewolf_state; librewolf_state=$(_state "librewolf")
|
||||||
riseupvpn_state=$(_state "riseup-vpn")
|
local privacybrowser_state; privacybrowser_state=$(_state "privacybrowser")
|
||||||
local w3m_state
|
local qutebrowser_state; qutebrowser_state=$(_state "qutebrowser")
|
||||||
w3m_state=$(_state "w3m")
|
local riseupvpn_state; riseupvpn_state=$(_state "riseup-vpn")
|
||||||
local tailscale_state
|
local thunderbird_state; thunderbird_state=$(_state "thunderbird")
|
||||||
tailscale_state=$(_state "tailscale")
|
local torbrowser_state; torbrowser_state=$(_state "torbrowser-launcher")
|
||||||
local mullvad_state
|
local w3m_state; w3m_state=$(_state "w3m")
|
||||||
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"
|
|
||||||
)
|
|
||||||
|
|
||||||
local item_count=${#items[@]}
|
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
local choices
|
||||||
choices=$(
|
choices=$(whiptail --title "Internet" --checklist \
|
||||||
_checklist "Internet" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
"Select browsers, email, and VPN tools:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
||||||
"${items[@]}"
|
"chromium" "Chromium web browser$(_inst chromium)" "$chromium_state" \
|
||||||
)
|
"dillo" "Lightweight graphical browser$(_inst dillo)" "$dillo_state" \
|
||||||
|
"elinks" "Text-mode web browser$(_inst elinks)" "$elinks_state" \
|
||||||
|
"epiphany-browser" "GNOME web browser$(_inst epiphany-browser)" "$epiphany_state" \
|
||||||
|
"falkon" "KDE web browser (QtWebEngine)$(_inst falkon)" "$falkon_state" \
|
||||||
|
"firefox" "Firefox from Mozilla (replaces ESR)" "$firefox_state" \
|
||||||
|
"floorp" "Firefox-based browser (external repo)" "$floorp_state" \
|
||||||
|
"konqueror" "KDE file manager / web browser$(_inst konqueror)" "$konqueror_state" \
|
||||||
|
"librewolf" "Privacy-focused Firefox fork$(_inst librewolf)" "$librewolf_state" \
|
||||||
|
"privacybrowser" "Privacy-focused web browser$(_inst privacybrowser)" "$privacybrowser_state" \
|
||||||
|
"qutebrowser" "Keyboard-driven browser (Qt)$(_inst qutebrowser)" "$qutebrowser_state" \
|
||||||
|
"riseup-vpn" "Riseup VPN client$(_inst riseup-vpn)" "$riseupvpn_state" \
|
||||||
|
"thunderbird" "Email client$(_inst thunderbird)" "$thunderbird_state" \
|
||||||
|
"torbrowser-launcher" "Tor Browser launcher$(_inst torbrowser-launcher)" "$torbrowser_state" \
|
||||||
|
"w3m" "Text-mode browser + deps (w3m-img)$(_inst w3m)" "$w3m_state" \
|
||||||
|
3>&1 1>&2 2>&3)
|
||||||
clear
|
clear
|
||||||
|
|
||||||
[ -z "$choices" ] && return
|
[ -z "$choices" ] && return
|
||||||
|
|
||||||
local cleaned
|
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
|
||||||
cleaned=$(echo "$choices" | tr -d '"')
|
|
||||||
|
|
||||||
local has_firefox=false
|
for pkg in $cleaned; do
|
||||||
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
|
|
||||||
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
|
|
||||||
case $pkg in
|
case $pkg in
|
||||||
firefox)
|
firefox)
|
||||||
install_firefox_mozilla "$fchoice"
|
install_firefox_mozilla
|
||||||
;;
|
;;
|
||||||
firefox-esr)
|
floorp)
|
||||||
install_firefox_esr "$fchoice"
|
if ! is_installed "floorp"; then
|
||||||
;;
|
echo "Setting up Floorp repository..."
|
||||||
floorp)
|
! is_installed "curl" && _run_install curl
|
||||||
_enable_floorp_repo
|
! is_installed "gpg" && _run_install gpg
|
||||||
_run_install floorp
|
sudo install -d -m 0755 /etc/apt/keyrings
|
||||||
echo -e "${GREEN}Floorp installed.${NC}"
|
curl -fsSL https://ppa.floorp.app/KEY.gpg | \
|
||||||
;;
|
sudo gpg --dearmor -o /usr/share/keyrings/Floorp.gpg
|
||||||
librewolf)
|
sudo curl -sS --compressed -o /etc/apt/sources.list.d/Floorp.list \
|
||||||
_enable_librewolf_repo
|
'https://ppa.floorp.app/Floorp.list'
|
||||||
_run_install librewolf
|
sudo tee /etc/apt/preferences.d/floorp > /dev/null << EOF
|
||||||
echo -e "${GREEN}LibreWolf installed.${NC}"
|
Package: *
|
||||||
;;
|
Pin: origin ppa.floorp.app
|
||||||
palemoon)
|
Pin-Priority: 1000
|
||||||
install_palemoon
|
EOF
|
||||||
;;
|
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
|
||||||
tailscale)
|
_run_install floorp
|
||||||
_enable_tailscale_repo
|
echo -e "${GREEN}Floorp installed.${NC}"
|
||||||
_run_install tailscale
|
else
|
||||||
echo -e "${GREEN}Tailscale installed.${NC}"
|
echo "Floorp already installed."
|
||||||
;;
|
fi
|
||||||
mullvad-vpn)
|
;;
|
||||||
_enable_mullvad_repo
|
librewolf)
|
||||||
_run_install mullvad-vpn
|
if ! is_installed "librewolf"; then
|
||||||
echo -e "${GREEN}Mullvad VPN installed.${NC}"
|
echo "Installing LibreWolf..."
|
||||||
;;
|
install_backports_or_stable extrepo
|
||||||
mullvad-browser)
|
sudo extrepo enable librewolf 2>/dev/null || true
|
||||||
_enable_mullvad_repo
|
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
|
||||||
_run_install mullvad-browser
|
_run_install librewolf
|
||||||
echo -e "${GREEN}Mullvad Browser installed.${NC}"
|
echo -e "${GREEN}LibreWolf installed.${NC}"
|
||||||
;;
|
else
|
||||||
protonvpn)
|
echo "LibreWolf already installed."
|
||||||
install_protonvpn
|
fi
|
||||||
;;
|
;;
|
||||||
riseup-vpn)
|
riseup-vpn)
|
||||||
install_backports_or_stable riseup-vpn
|
install_backports_or_stable riseup-vpn
|
||||||
;;
|
;;
|
||||||
w3m)
|
w3m)
|
||||||
local need=()
|
local need=()
|
||||||
! is_installed "w3m" && need+=("w3m")
|
! is_installed "w3m" && need+=("w3m")
|
||||||
! is_installed "w3m-img" && need+=("w3m-img")
|
! is_installed "w3m-img" && need+=("w3m-img")
|
||||||
! is_installed "ca-certificates" && need+=("ca-certificates")
|
! is_installed "ca-certificates" && need+=("ca-certificates")
|
||||||
! is_installed "xsel" && need+=("xsel")
|
! is_installed "xsel" && need+=("xsel")
|
||||||
if [ ${#need[@]} -gt 0 ]; then
|
if [ ${#need[@]} -gt 0 ]; then
|
||||||
_run_install_batch "${need[@]}"
|
_run_install_batch w3m w3m-img ca-certificates xsel
|
||||||
else
|
else
|
||||||
echo "w3m already installed."
|
echo "w3m already installed."
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
if ! is_installed "$pkg"; then
|
if $headless; then
|
||||||
_run_install "$pkg"
|
echo "Skipping $pkg (headless mode)"
|
||||||
else
|
continue
|
||||||
echo "$pkg already installed."
|
fi
|
||||||
fi
|
if ! is_installed "$pkg"; then
|
||||||
;;
|
_run_install "$pkg"
|
||||||
|
else
|
||||||
|
echo "$pkg already installed."
|
||||||
|
fi
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
|
|
||||||
echo -e "${GREEN}Internet tools installed.${NC}"
|
echo -e "${GREEN}Internet tools installed.${NC}"
|
||||||
}
|
}
|
||||||
|
|
||||||
install_firefox_mozilla() {
|
install_firefox_mozilla() {
|
||||||
local fchoice="${1:-}"
|
if command -v firefox &>/dev/null && ! command -v firefox-esr &>/dev/null; then
|
||||||
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
|
|
||||||
fi
|
|
||||||
|
|
||||||
if command -v firefox &>/dev/null && ! is_installed "firefox-esr"; then
|
|
||||||
echo "Firefox (Mozilla) is already installed."
|
echo "Firefox (Mozilla) is already installed."
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "Setting up Mozilla APT repository for Firefox..."
|
||||||
|
|
||||||
if is_installed "firefox-esr"; then
|
if is_installed "firefox-esr"; then
|
||||||
case "$fchoice" in
|
if _confirm "Firefox ESR" "Firefox ESR is installed.\nRemove it before installing Mozilla Firefox?"; then
|
||||||
both)
|
echo "Removing Firefox ESR..."
|
||||||
echo -e "${CYAN}[INFO] Keeping Firefox ESR (both selected).${NC}"
|
|
||||||
;;
|
|
||||||
mozilla)
|
|
||||||
echo "Removing Firefox ESR (as selected)..."
|
|
||||||
sudo apt remove -y firefox-esr
|
sudo apt remove -y firefox-esr
|
||||||
;;
|
else
|
||||||
*)
|
echo "Keeping Firefox ESR."
|
||||||
if _confirm "Firefox ESR" "Firefox ESR is installed.\nRemove it before installing Mozilla Firefox?"; then
|
fi
|
||||||
echo "Removing Firefox ESR..."
|
|
||||||
sudo apt remove -y firefox-esr
|
|
||||||
else
|
|
||||||
echo "Keeping Firefox ESR."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_enable_mozilla_repo
|
! is_installed "wget" && _run_install wget
|
||||||
_run_cmd "Firefox" "sudo apt install -y firefox" "Installing Firefox (Mozilla)..."
|
! is_installed "gpg" && _run_install gpg
|
||||||
|
|
||||||
|
sudo install -d -m 0755 /etc/apt/keyrings
|
||||||
|
|
||||||
|
wget -q https://packages.mozilla.org/apt/repo-signing-key.gpg -O- | \
|
||||||
|
sudo tee /etc/apt/keyrings/packages.mozilla.org.asc > /dev/null
|
||||||
|
|
||||||
|
local fp
|
||||||
|
fp=$(gpg -n -q --import --import-options import-show \
|
||||||
|
/etc/apt/keyrings/packages.mozilla.org.asc 2>/dev/null | \
|
||||||
|
awk '/pub/{getline; gsub(/^ +| +$/,""); print}')
|
||||||
|
if [ "$fp" != "35BAA0B33E9EB396F59CA838C0BA5CE6DC6315A3" ]; then
|
||||||
|
echo -e "${YELLOW}Warning: Mozilla key fingerprint does not match expected value.${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local use_deb822=false
|
||||||
|
[ -f /etc/apt/sources.list.d/debian.sources ] && use_deb822=true
|
||||||
|
|
||||||
|
if $use_deb822; then
|
||||||
|
sudo tee /etc/apt/sources.list.d/mozilla.sources > /dev/null << EOF
|
||||||
|
Types: deb
|
||||||
|
URIs: https://packages.mozilla.org/apt
|
||||||
|
Suites: mozilla
|
||||||
|
Components: main
|
||||||
|
Signed-By: /etc/apt/keyrings/packages.mozilla.org.asc
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
echo "deb [signed-by=/etc/apt/keyrings/packages.mozilla.org.asc] https://packages.mozilla.org/apt mozilla main" | \
|
||||||
|
sudo tee /etc/apt/sources.list.d/mozilla.list > /dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
sudo tee /etc/apt/preferences.d/mozilla > /dev/null << EOF
|
||||||
|
Package: *
|
||||||
|
Pin: origin packages.mozilla.org
|
||||||
|
Pin-Priority: 1000
|
||||||
|
EOF
|
||||||
|
|
||||||
|
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
|
||||||
|
_run_install firefox
|
||||||
|
|
||||||
echo -e "${GREEN}Firefox (Mozilla) installed.${NC}"
|
echo -e "${GREEN}Firefox (Mozilla) installed.${NC}"
|
||||||
}
|
}
|
||||||
|
|
||||||
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)..."
|
|
||||||
sudo apt remove -y firefox
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
if _confirm "Firefox" "Mozilla Firefox is installed.\nRemove it before installing Firefox ESR?"; then
|
|
||||||
echo "Removing Mozilla Firefox..."
|
|
||||||
sudo apt remove -y firefox
|
|
||||||
else
|
|
||||||
echo "Keeping Mozilla Firefox."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
_run_cmd "Firefox ESR" "sudo apt install -y firefox-esr" "Installing Firefox ESR..."
|
|
||||||
|
|
||||||
local lang_pkg
|
|
||||||
lang_pkg=$(_detect_lang_pkg "firefox-esr-l10n")
|
|
||||||
if [ -n "$lang_pkg" ]; then
|
|
||||||
_run_cmd "Firefox ESR Locale" "sudo apt install -y ${lang_pkg}" \
|
|
||||||
"Installing locale: ${lang_pkg}..."
|
|
||||||
else
|
|
||||||
echo "No matching locale package found for your language."
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "${GREEN}Firefox ESR installed.${NC}"
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# java.sh — Adoptium Temurin repository setup (extrepo) and Java version selectors
|
|
||||||
# License GPL v3
|
|
||||||
|
|
||||||
_enable_temurin_repo() {
|
|
||||||
if [ -f /etc/apt/sources.list.d/extrepo_temurin.sources ]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
if ! command -v extrepo &>/dev/null; then
|
|
||||||
_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
|
|
||||||
}
|
|
||||||
|
|
||||||
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 \
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
_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
|
|
||||||
_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() {
|
|
||||||
local ver
|
|
||||||
ver=$(_menu "Java Development Kits (JDK)" \
|
|
||||||
"Select JDK version:" 12 60 3 \
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
_enable_temurin_repo
|
|
||||||
_run_install "temurin-${ver}-jdk"
|
|
||||||
}
|
|
||||||
|
|
||||||
_any_jdk_installed_desc() {
|
|
||||||
local inst
|
|
||||||
inst=$(dpkg -l 'temurin-*-jdk' 2>/dev/null | awk '/^ii/ {print $2; exit}')
|
|
||||||
[ -n "$inst" ] && echo " ($inst installed)" || echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
_any_jdk_state() {
|
|
||||||
dpkg -l 'temurin-*-jdk' 2>/dev/null | grep -q '^ii' && echo "ON" || echo "OFF"
|
|
||||||
}
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# office.sh — Office & Productivity (OnlyOffice via extrepo, LibreOffice backports)
|
|
||||||
# License GPL v3
|
|
||||||
|
|
||||||
# ── OnlyOffice ──
|
|
||||||
_enable_onlyoffice_repo() {
|
|
||||||
if [ ! -f /etc/apt/sources.list.d/extrepo_onlyoffice-desktopeditors.sources ]; then
|
|
||||||
if ! command -v extrepo &>/dev/null; then
|
|
||||||
_run_cmd "extrepo" "sudo apt install -y extrepo" "Installing extrepo..."
|
|
||||||
fi
|
|
||||||
_run_cmd "OnlyOffice" "sudo extrepo enable onlyoffice-desktopeditors" "Enabling OnlyOffice repository..."
|
|
||||||
fi
|
|
||||||
_ensure_apt_updated
|
|
||||||
}
|
|
||||||
|
|
||||||
install_onlyoffice() {
|
|
||||||
local ver
|
|
||||||
ver=$(apt-cache policy onlyoffice-desktopeditors 2>/dev/null | awk 'NR==3 {print $2; exit}')
|
|
||||||
|
|
||||||
if _confirm "Install: OnlyOffice" "Install OnlyOffice Desktop Editors
|
|
||||||
Repository: download.onlyoffice.com
|
|
||||||
Version: ${ver:-unknown}"; then
|
|
||||||
_enable_onlyoffice_repo
|
|
||||||
_run_cmd "OnlyOffice" "sudo DEBIAN_FRONTEND=noninteractive apt install -y onlyoffice-desktopeditors" "Installing OnlyOffice..." || echo -e "${RED}OnlyOffice servers are currently slow or down. Please try again later.${NC}"
|
|
||||||
echo -e "${GREEN}OnlyOffice installed.${NC}"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── Joplin (extrepo, solo Debian 12+) ──
|
|
||||||
install_joplin() {
|
|
||||||
if [ "$DEBIAN_VERSION" != "12" ] && [ "$DEBIAN_VERSION" != "13" ]; then
|
|
||||||
_msg "Joplin" "Joplin via extrepo is only available on\nDebian 12 (Bookworm) and 13 (Trixie)." 10 60
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
_ensure_extrepo
|
|
||||||
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
|
|
||||||
if ! is_installed "joplin"; then
|
|
||||||
_run_cmd "Joplin" "sudo apt install -y joplin" "Installing Joplin..."
|
|
||||||
echo -e "${GREEN}Joplin installed.${NC}"
|
|
||||||
else
|
|
||||||
echo "Joplin already installed."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── LibreOffice (backports) ──
|
|
||||||
install_libreoffice_bpo() {
|
|
||||||
if [ "$DEBIAN_VERSION" != "12" ] && [ "$DEBIAN_VERSION" != "13" ]; then
|
|
||||||
_msg "LibreOffice" "LibreOffice backports only available on Debian 12+."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
local lang_pkg
|
|
||||||
lang_pkg=$(_detect_lang_pkg "libreoffice-l10n")
|
|
||||||
|
|
||||||
local ver
|
|
||||||
ver=$(apt-cache policy libreoffice 2>/dev/null | awk 'NR==3 {print $2; exit}')
|
|
||||||
local bpo_ver
|
|
||||||
bpo_ver=$(apt-cache madison libreoffice 2>/dev/null | grep "${DEBIAN_CODENAME}-backports" | awk '{print $3}' | head -1)
|
|
||||||
|
|
||||||
local msg="Install LibreOffice?\n\n"
|
|
||||||
msg+=" Stable: ${ver:-unknown}\n"
|
|
||||||
msg+=" Backports: ${bpo_ver:-unavailable}\n"
|
|
||||||
[ -n "$lang_pkg" ] && msg+=" Language: ${lang_pkg}\n"
|
|
||||||
|
|
||||||
if _confirm "LibreOffice" "$msg"; then
|
|
||||||
local pkgs="libreoffice${lang_pkg:+ $lang_pkg}"
|
|
||||||
if [ -n "$bpo_ver" ]; then
|
|
||||||
_run_cmd "LibreOffice" "sudo apt install -y -t ${DEBIAN_CODENAME}-backports $pkgs" \
|
|
||||||
"Installing LibreOffice from backports..."
|
|
||||||
else
|
|
||||||
_run_cmd "LibreOffice" "sudo apt install -y $pkgs" \
|
|
||||||
"Installing LibreOffice from stable..."
|
|
||||||
fi
|
|
||||||
echo -e "${GREEN}LibreOffice installed.${NC}"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_cat_office() {
|
|
||||||
local headless=false
|
|
||||||
_is_headless && headless=true
|
|
||||||
local -a items=()
|
|
||||||
if ! $headless; then
|
|
||||||
items+=(
|
|
||||||
"onlyoffice" "OnlyOffice Desktop Editors (extrepo)" OFF
|
|
||||||
"libreoffice" "LibreOffice (backports on Bookworm/Trixie)" OFF
|
|
||||||
)
|
|
||||||
if [ "$DEBIAN_VERSION" = "12" ] || [ "$DEBIAN_VERSION" = "13" ]; then
|
|
||||||
items+=(
|
|
||||||
"joplin" "Joplin note-taking (extrepo)" OFF
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
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[@]}"
|
|
||||||
)
|
|
||||||
|
|
||||||
[ -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 "Office & Productivity" "${_pkgs[@]}" || return
|
|
||||||
for pkg in "${_pkgs[@]}"; do
|
|
||||||
case $pkg in
|
|
||||||
onlyoffice) install_onlyoffice ;;
|
|
||||||
libreoffice) install_libreoffice_bpo ;;
|
|
||||||
joplin) install_joplin ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
}
|
|
||||||
@@ -4,38 +4,27 @@
|
|||||||
_cat_players() {
|
_cat_players() {
|
||||||
local headless=false
|
local headless=false
|
||||||
_is_headless && headless=true
|
_is_headless && headless=true
|
||||||
local -a items=()
|
local mpv_state; mpv_state=$(_state "mpv")
|
||||||
if ! $headless; then
|
local vlc_state; vlc_state=$(_state "vlc")
|
||||||
local mpv_state; mpv_state=$(_state "mpv")
|
|
||||||
local vlc_state; vlc_state=$(_state "vlc")
|
|
||||||
items+=(
|
|
||||||
"mpv" "Lightweight media player" "$mpv_state"
|
|
||||||
"vlc" "VLC media player" "$vlc_state"
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
|
|
||||||
local item_count=${#items[@]}
|
local TUI_ANCHO_REFORZADO=$((TUI_ANCHO + 6))
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
local choices
|
||||||
choices=$(_checklist "Media Players" "Select media players:" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
choices=$(whiptail --title "Media Players" --checklist \
|
||||||
"${items[@]}" \
|
"Select media players:" $TUI_ALTO $TUI_ANCHO_REFORZADO $TUI_ALTO_LISTA \
|
||||||
)
|
"mpv" "Lightweight media player$(_inst mpv)" "$mpv_state" \
|
||||||
|
"vlc" "VLC media player$(_inst vlc)" "$vlc_state" \
|
||||||
|
3>&1 1>&2 2>&3)
|
||||||
clear
|
clear
|
||||||
|
|
||||||
[ -z "$choices" ] && return
|
[ -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.
|
for pkg in $cleaned; do
|
||||||
local -a _pkgs=()
|
if $headless; then
|
||||||
while IFS= read -r _pkg; do
|
echo "Skipping $pkg (headless mode)"
|
||||||
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
|
continue
|
||||||
done < <(echo "$cleaned" | tr ' ' '\n')
|
fi
|
||||||
|
|
||||||
# Pre-apply summary: confirm the selection once,
|
|
||||||
# before any apt transaction starts.
|
|
||||||
_confirm_install_list "Media Players" "${_pkgs[@]}" || return
|
|
||||||
for pkg in "${_pkgs[@]}"; do
|
|
||||||
if ! is_installed "$pkg"; then
|
if ! is_installed "$pkg"; then
|
||||||
_run_install "$pkg"
|
_run_install "$pkg"
|
||||||
else
|
else
|
||||||
@@ -43,8 +32,5 @@ _cat_players() {
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
|
|
||||||
echo -e "${GREEN}Media players installed.${NC}"
|
echo -e "${GREEN}Media players installed.${NC}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,113 +4,102 @@
|
|||||||
_cat_programming() {
|
_cat_programming() {
|
||||||
local headless=false
|
local headless=false
|
||||||
_is_headless && headless=true
|
_is_headless && headless=true
|
||||||
local -a items=()
|
local vim_state; vim_state=$(_state "vim")
|
||||||
local vim_state
|
local vimgtk_state; vimgtk_state=$(_state "vim-gtk3")
|
||||||
vim_state=$(_state "vim")
|
local neovim_state; neovim_state=$(_state "neovim")
|
||||||
local neovim_state
|
local hx_state; hx_state=$(_state "hx")
|
||||||
neovim_state=$(_state "neovim")
|
local nano_state; nano_state=$(_state "nano")
|
||||||
local hx_state
|
local emacs_state; emacs_state=$(_state "emacs")
|
||||||
hx_state=$(_state "hx")
|
local kate_state; kate_state=$(_state "kate")
|
||||||
local nano_state
|
local mousepad_state; mousepad_state=$(_state "mousepad")
|
||||||
nano_state=$(_state "nano")
|
local gedit_state; gedit_state=$(_state "gedit")
|
||||||
local emacs_state
|
local geany_state; geany_state=$(_state "geany")
|
||||||
emacs_state=$(_state "emacs")
|
local gte_state; gte_state=$(_state "gnome-text-editor")
|
||||||
items+=(
|
local codium_state="OFF"
|
||||||
"vim" "Classic terminal editor" "$vim_state"
|
if command -v codium &>/dev/null; then
|
||||||
"neovim" "Modern vim fork" "$neovim_state"
|
codium_state="ON"
|
||||||
"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 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"
|
|
||||||
)
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local item_count=${#items[@]}
|
local TUI_ANCHO_REFORZADO=$((TUI_ANCHO + 6))
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
local choices
|
||||||
choices=$(
|
choices=$(whiptail --title "Programming Applications" --checklist \
|
||||||
_checklist "Programming Applications" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
"Select editors and IDEs (12 items, ↑↓ scroll):" $TUI_ALTO $TUI_ANCHO_REFORZADO $TUI_ALTO_LISTA \
|
||||||
"${items[@]}"
|
"vim" "Classic terminal editor$(_inst vim)" "$vim_state" \
|
||||||
)
|
"vim-gtk3" "Vim with GTK3 GUI$(_inst vim-gtk3)" "$vimgtk_state" \
|
||||||
|
"neovim" "Modern vim fork$(_inst neovim)" "$neovim_state" \
|
||||||
|
"hx" "Helix modal editor (Rust)$(_inst hx)" "$hx_state" \
|
||||||
|
"nano" "Simple terminal editor$(_inst nano)" "$nano_state" \
|
||||||
|
"emacs" "Extensible editor / IDE$(_inst emacs)" "$emacs_state" \
|
||||||
|
"kate" "KDE advanced text editor$(_inst kate)" "$kate_state" \
|
||||||
|
"mousepad" "Xfce text editor$(_inst mousepad)" "$mousepad_state" \
|
||||||
|
"gedit" "GNOME text editor$(_inst gedit)" "$gedit_state" \
|
||||||
|
"geany" "Lightweight IDE$(_inst geany)" "$geany_state" \
|
||||||
|
"gnome-text-editor" "GNOME modern text editor$(_inst gnome-text-editor)" "$gte_state" \
|
||||||
|
"vscodium" "VS Code open-source (external repo)$(_inst codium)" "$codium_state" \
|
||||||
|
3>&1 1>&2 2>&3)
|
||||||
clear
|
clear
|
||||||
|
|
||||||
[ -z "$choices" ] && return
|
[ -z "$choices" ] && return
|
||||||
|
|
||||||
local cleaned
|
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
|
||||||
cleaned=$(echo "$choices" | tr -d '"')
|
|
||||||
|
|
||||||
# BH-004: Convert to array to avoid word splitting and injection.
|
for pkg in $cleaned; do
|
||||||
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
|
|
||||||
case $pkg in
|
case $pkg in
|
||||||
vscodium)
|
vscodium)
|
||||||
install_vscodium
|
install_vscodium
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
if ! is_installed "$pkg"; then
|
if $headless; then
|
||||||
_run_install "$pkg"
|
echo "Skipping $pkg (headless mode)"
|
||||||
else
|
continue
|
||||||
echo "$pkg already installed."
|
fi
|
||||||
fi
|
if ! is_installed "$pkg"; then
|
||||||
;;
|
_run_install "$pkg"
|
||||||
|
else
|
||||||
|
echo "$pkg already installed."
|
||||||
|
fi
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
|
|
||||||
echo -e "${GREEN}Programming applications installed.${NC}"
|
echo -e "${GREEN}Programming applications installed.${NC}"
|
||||||
}
|
}
|
||||||
|
|
||||||
_enable_vscodium_repo() {
|
|
||||||
if [ ! -f /etc/apt/sources.list.d/extrepo_vscodium.sources ]; then
|
|
||||||
if ! command -v extrepo &>/dev/null; then
|
|
||||||
_run_cmd "extrepo" "sudo apt install -y extrepo" "Installing extrepo..."
|
|
||||||
fi
|
|
||||||
_run_cmd "VSCodium" "sudo extrepo enable vscodium" "Enabling VSCodium repository..."
|
|
||||||
fi
|
|
||||||
_ensure_apt_updated
|
|
||||||
}
|
|
||||||
|
|
||||||
install_vscodium() {
|
install_vscodium() {
|
||||||
if command -v codium &>/dev/null; then
|
if command -v codium &>/dev/null; then
|
||||||
echo "VSCodium is already installed."
|
echo "VSCodium is already installed."
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_enable_vscodium_repo
|
echo "Setting up VSCodium repository..."
|
||||||
|
|
||||||
|
! is_installed "wget" && _run_install wget
|
||||||
|
! is_installed "gpg" && _run_install gpg
|
||||||
|
|
||||||
|
sudo install -d -m 0755 /usr/share/keyrings
|
||||||
|
|
||||||
|
wget -qO - https://gitlab.com/paulcarroty/vscodium-deb-rpm-repo/raw/master/pub.gpg \
|
||||||
|
| gpg --dearmor \
|
||||||
|
| sudo dd of=/usr/share/keyrings/vscodium-archive-keyring.gpg 2>/dev/null
|
||||||
|
|
||||||
|
local use_deb822=false
|
||||||
|
[ -f /etc/apt/sources.list.d/debian.sources ] && use_deb822=true
|
||||||
|
|
||||||
|
if $use_deb822; then
|
||||||
|
sudo tee /etc/apt/sources.list.d/vscodium.sources > /dev/null << 'EOF'
|
||||||
|
Types: deb
|
||||||
|
URIs: https://download.vscodium.com/debs
|
||||||
|
Suites: vscodium
|
||||||
|
Components: main
|
||||||
|
Architectures: amd64 arm64
|
||||||
|
Signed-by: /usr/share/keyrings/vscodium-archive-keyring.gpg
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
echo "deb [arch=amd64,arm64 signed-by=/usr/share/keyrings/vscodium-archive-keyring.gpg] https://download.vscodium.com/debs vscodium main" \
|
||||||
|
| sudo tee /etc/apt/sources.list.d/vscodium.list > /dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
|
||||||
_run_install codium
|
_run_install codium
|
||||||
|
|
||||||
echo -e "${GREEN}VSCodium installed.${NC}"
|
echo -e "${GREEN}VSCodium installed.${NC}"
|
||||||
|
|||||||
@@ -4,55 +4,33 @@
|
|||||||
_cat_security() {
|
_cat_security() {
|
||||||
local headless=false
|
local headless=false
|
||||||
_is_headless && headless=true
|
_is_headless && headless=true
|
||||||
local -a items=()
|
local wireshark_state; wireshark_state=$(_state "wireshark")
|
||||||
if ! $headless; then
|
local tcpdump_state; tcpdump_state=$(_state "tcpdump")
|
||||||
local wireshark_state; wireshark_state=$(_state "wireshark")
|
local zenmap_state; zenmap_state=$(_state "zenmap")
|
||||||
local zenmap_state; zenmap_state=$(_state "zenmap")
|
local fail2ban_state; fail2ban_state=$(_state "fail2ban")
|
||||||
items+=(
|
local ufw_state; ufw_state=$(_state "ufw")
|
||||||
"wireshark" "Network protocol analyzer (GUI)" "$wireshark_state"
|
|
||||||
"zenmap" "Network scanner GUI (Nmap frontend)" "$zenmap_state"
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
local tcpdump_state; tcpdump_state=$(_state "tcpdump")
|
|
||||||
local fail2ban_state; fail2ban_state=$(_state "fail2ban")
|
|
||||||
local ufw_state; ufw_state=$(_state "ufw")
|
|
||||||
local clamav_state; clamav_state=$(_state "clamav")
|
|
||||||
items+=(
|
|
||||||
"tcpdump" "Command-line packet analyzer" "$tcpdump_state"
|
|
||||||
"fail2ban" "Brute-force protection daemon" "$fail2ban_state"
|
|
||||||
"ufw" "Uncomplicated firewall" "$ufw_state"
|
|
||||||
"clamav" "Antivirus engine (ClamAV)" "$clamav_state"
|
|
||||||
)
|
|
||||||
|
|
||||||
local item_count=${#items[@]}
|
local TUI_ANCHO_REFORZADO=$((TUI_ANCHO + 6))
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
local choices
|
||||||
choices=$(_checklist "Security & Networking" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
choices=$(whiptail --title "Security & Networking" --checklist \
|
||||||
"${items[@]}" \
|
"Select security and networking tools (5 items):" $TUI_ALTO $TUI_ANCHO_REFORZADO $TUI_ALTO_LISTA \
|
||||||
)
|
"wireshark" "Network protocol analyzer (GUI)$(_inst wireshark)" "$wireshark_state" \
|
||||||
|
"tcpdump" "Command-line packet analyzer$(_inst tcpdump)" "$tcpdump_state" \
|
||||||
|
"zenmap" "Network scanner GUI (Nmap frontend)$(_inst zenmap)" "$zenmap_state" \
|
||||||
|
"fail2ban" "Brute-force protection daemon$(_inst fail2ban)" "$fail2ban_state" \
|
||||||
|
"ufw" "Uncomplicated firewall$(_inst ufw)" "$ufw_state" \
|
||||||
|
3>&1 1>&2 2>&3)
|
||||||
clear
|
clear
|
||||||
|
|
||||||
[ -z "$choices" ] && return
|
[ -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.
|
for pkg in $cleaned; do
|
||||||
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
|
|
||||||
case $pkg in
|
case $pkg in
|
||||||
zenmap)
|
zenmap)
|
||||||
install_backports_or_stable zenmap
|
install_backports_or_stable zenmap
|
||||||
;;
|
;;
|
||||||
clamav)
|
|
||||||
_install_clamav
|
|
||||||
;;
|
|
||||||
*)
|
*)
|
||||||
if ! is_installed "$pkg"; then
|
if ! is_installed "$pkg"; then
|
||||||
_run_install "$pkg"
|
_run_install "$pkg"
|
||||||
@@ -63,8 +41,5 @@ _cat_security() {
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
|
|
||||||
echo -e "${GREEN}Security & networking tools installed.${NC}"
|
echo -e "${GREEN}Security & networking tools installed.${NC}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,113 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# software_centers.sh — Standalone Software Center + Flatpak installer
|
|
||||||
|
|
||||||
_cat_software_centers() {
|
|
||||||
local headless=false
|
|
||||||
_is_headless && headless=true
|
|
||||||
if $headless; then
|
|
||||||
echo "Software centers require a GUI — skipping."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
local de_type
|
|
||||||
de_type=$(_detect_desktop_type)
|
|
||||||
|
|
||||||
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"
|
|
||||||
)
|
|
||||||
|
|
||||||
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 \
|
|
||||||
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."
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
|
|
||||||
echo -e "${GREEN}Software center & Flatpak setup complete.${NC}"
|
|
||||||
}
|
|
||||||
@@ -1,290 +1,193 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# system.sh — System Tools (extrepo moved here from Dev & Servers)
|
# system.sh — System Tools (extrepo moved here from Dev & Servers)
|
||||||
|
|
||||||
_detect_desktop_type() {
|
|
||||||
local desktop
|
|
||||||
desktop="${XDG_CURRENT_DESKTOP:-${DESKTOP_SESSION:-}}"
|
|
||||||
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
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
echo "gtk"
|
|
||||||
}
|
|
||||||
|
|
||||||
_cat_general() {
|
_cat_general() {
|
||||||
local headless=false
|
local headless=false
|
||||||
_is_headless && headless=true
|
_is_headless && headless=true
|
||||||
local -a items=()
|
local alacritty_state; alacritty_state=$(_state "alacritty")
|
||||||
local btop_state
|
local btop_state; btop_state=$(_state "btop")
|
||||||
btop_state=$(_state "btop")
|
|
||||||
local compress_state
|
local compress_state
|
||||||
if is_installed "zip" && is_installed "unzip" && is_installed "p7zip-full"; then
|
if is_installed "zip" && is_installed "unzip" && is_installed "p7zip-full"; then
|
||||||
compress_state="ON"
|
compress_state="ON"
|
||||||
else
|
else
|
||||||
compress_state="OFF"
|
compress_state="OFF"
|
||||||
fi
|
fi
|
||||||
local cpufetch_state
|
local conky_state; conky_state=$(_state "conky")
|
||||||
cpufetch_state=$(_state "cpufetch")
|
local corectrl_state; corectrl_state=$(_state "corectrl")
|
||||||
local cpu_x_state
|
local cpufetch_state; cpufetch_state=$(_state "cpufetch")
|
||||||
cpu_x_state=$(_state "cpu-x")
|
local cpu_x_state; cpu_x_state=$(_state "cpu-x")
|
||||||
local curl_wget_state
|
local curl_wget_state
|
||||||
if is_installed "curl" && is_installed "wget"; then
|
if is_installed "curl" && is_installed "wget"; then
|
||||||
curl_wget_state="ON"
|
curl_wget_state="ON"
|
||||||
else
|
else
|
||||||
curl_wget_state="OFF"
|
curl_wget_state="OFF"
|
||||||
fi
|
fi
|
||||||
local extrepo_state
|
local dcgtk_state; dcgtk_state=$(_state "doublecmd-gtk")
|
||||||
extrepo_state=$(_state "extrepo")
|
local dcqt_state; dcqt_state=$(_state "doublecmd-qt")
|
||||||
local fwupd_state
|
local extrepo_state; extrepo_state=$(_state "extrepo")
|
||||||
fwupd_state=$(_state "fwupd")
|
local flatpak_state; flatpak_state=$(_state "flatpak")
|
||||||
local htop_state
|
local fwupd_state; fwupd_state=$(_state "fwupd")
|
||||||
htop_state=$(_state "htop")
|
local disks_state; disks_state=$(_state "gnome-disk-utility")
|
||||||
local inxi_state
|
local gparted_state; gparted_state=$(_state "gparted")
|
||||||
inxi_state=$(_state "inxi")
|
local hardinfo_state; hardinfo_state=$(_state "hardinfo")
|
||||||
local jq_state
|
local htop_state; htop_state=$(_state "htop")
|
||||||
jq_state=$(_state "jq")
|
local inxi_state; inxi_state=$(_state "inxi")
|
||||||
local kvm_state
|
local kitty_state; kitty_state=$(_state "kitty")
|
||||||
kvm_state=$(_state "virt-manager")
|
local kvm_state; kvm_state=$(_state "virt-manager")
|
||||||
local lshw_state
|
local lshw_state; lshw_state=$(_state "lshw")
|
||||||
lshw_state=$(_state "lshw")
|
local mc_state; mc_state=$(_state "mc")
|
||||||
local mc_state
|
local nala_state; nala_state=$(_state "nala")
|
||||||
mc_state=$(_state "mc")
|
local ncdu_state; ncdu_state=$(_state "ncdu")
|
||||||
local nala_state
|
local psensor_state; psensor_state=$(_state "psensor")
|
||||||
nala_state=$(_state "nala")
|
local timeshift_state; timeshift_state=$(_state "timeshift")
|
||||||
local ncdu_state
|
local tmux_state; tmux_state=$(_state "tmux")
|
||||||
ncdu_state=$(_state "ncdu")
|
local wine_state; wine_state=$(_state "wine")
|
||||||
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"
|
|
||||||
)
|
|
||||||
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")
|
|
||||||
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"
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
|
|
||||||
local item_count=${#items[@]}
|
local TUI_ANCHO_REFORZADO=$((TUI_ANCHO + 6))
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
local choices
|
||||||
choices=$(
|
choices=$(whiptail --title "System Tools" --checklist \
|
||||||
_checklist "System Tools" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
"Select system utilities to install (28 items, ↑↓ scroll):" $TUI_ALTO $TUI_ANCHO_REFORZADO $TUI_ALTO_LISTA \
|
||||||
"${items[@]}"
|
"alacritty" "GPU-accelerated terminal$(_inst alacritty)" "$alacritty_state" \
|
||||||
)
|
"btop" "Resource monitor (fancy top)$(_inst btop)" "$btop_state" \
|
||||||
|
"compress" "Compression tools (zip, unrar, 7z)$(_inst zip)" "$compress_state" \
|
||||||
|
"conky" "System monitor for desktop$(_inst conky)" "$conky_state" \
|
||||||
|
"corectrl" "AMD GPU control (CoreCtrl)$(_inst corectrl)" "$corectrl_state" \
|
||||||
|
"cpufetch" "CPU info fetcher$(_inst cpufetch)" "$cpufetch_state" \
|
||||||
|
"cpu-x" "CPU-X (alternative to CPU-Z)$(_inst cpu-x)" "$cpu_x_state" \
|
||||||
|
"curl-wget" "HTTP transfer tools (curl, wget)$(_inst curl)" "$curl_wget_state" \
|
||||||
|
"doublecmd-gtk" "Dual-panel file manager (GTK)$(_inst doublecmd-gtk)" "$dcgtk_state" \
|
||||||
|
"doublecmd-qt" "Dual-panel file manager (Qt)$(_inst doublecmd-qt)" "$dcqt_state" \
|
||||||
|
"extrepo" "External repository manager$(_inst extrepo)" "$extrepo_state" \
|
||||||
|
"flatpak" "Flatpak sandbox + Flathub$(_inst flatpak)" "$flatpak_state" \
|
||||||
|
"fwupd" "Firmware update daemon$(_inst fwupd)" "$fwupd_state" \
|
||||||
|
"gnome-disk-utility" "Disk management GUI$(_inst gnome-disk-utility)" "$disks_state" \
|
||||||
|
"gparted" "GNOME partition editor$(_inst gparted)" "$gparted_state" \
|
||||||
|
"hardinfo" "Graphical system profiler$(_inst hardinfo)" "$hardinfo_state" \
|
||||||
|
"htop" "Interactive process viewer$(_inst htop)" "$htop_state" \
|
||||||
|
"inxi" "System information tool$(_inst inxi)" "$inxi_state" \
|
||||||
|
"kitty" "GPU-based terminal emulator$(_inst kitty)" "$kitty_state" \
|
||||||
|
"kvm" "QEMU/KVM virtualization$(_inst virt-manager)" "$kvm_state" \
|
||||||
|
"lshw" "List hardware details$(_inst lshw)" "$lshw_state" \
|
||||||
|
"mc" "Midnight Commander (file manager)$(_inst mc)" "$mc_state" \
|
||||||
|
"nala" "APT frontend (parallel downloads)$(_inst nala)" "$nala_state" \
|
||||||
|
"ncdu" "Disk usage analyzer (ncurses)$(_inst ncdu)" "$ncdu_state" \
|
||||||
|
"psensor" "Hardware temperature monitor$(_inst psensor)" "$psensor_state" \
|
||||||
|
"timeshift" "System restore snapshots$(_inst timeshift)" "$timeshift_state" \
|
||||||
|
"tmux" "Terminal multiplexer$(_inst tmux)" "$tmux_state" \
|
||||||
|
"wine" "Windows compatibility layer$(_inst wine)" "$wine_state" \
|
||||||
|
3>&1 1>&2 2>&3)
|
||||||
clear
|
clear
|
||||||
|
|
||||||
[ -z "$choices" ] && return
|
[ -z "$choices" ] && return
|
||||||
|
|
||||||
local cleaned
|
local cleaned; cleaned=$(echo "$choices" | tr -d '"')
|
||||||
cleaned=$(echo "$choices" | tr -d '"')
|
|
||||||
|
|
||||||
# BH-004: Convert to array to avoid word splitting and injection.
|
for pkg in $cleaned; do
|
||||||
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
|
|
||||||
case $pkg in
|
case $pkg in
|
||||||
compress)
|
compress)
|
||||||
local need=()
|
local need=()
|
||||||
! is_installed "zip" && need+=("zip")
|
! is_installed "zip" && need+=("zip")
|
||||||
! is_installed "unzip" && need+=("unzip")
|
! is_installed "unzip" && need+=("unzip")
|
||||||
! is_installed "rar" && need+=("rar")
|
! is_installed "rar" && need+=("rar")
|
||||||
! is_installed "unrar" && need+=("unrar")
|
! is_installed "unrar" && need+=("unrar")
|
||||||
! is_installed "p7zip-full" && need+=("p7zip-full")
|
! is_installed "p7zip-full" && need+=("p7zip-full")
|
||||||
! is_installed "p7zip-rar" && need+=("p7zip-rar")
|
! is_installed "p7zip-rar" && need+=("p7zip-rar")
|
||||||
if [ ${#need[@]} -gt 0 ]; then
|
if [ ${#need[@]} -gt 0 ]; then
|
||||||
_run_install_batch "${need[@]}"
|
_run_install_batch "${need[@]}"
|
||||||
echo -e "${GREEN}Compression utilities installed.${NC}"
|
echo -e "${GREEN}Compression utilities installed.${NC}"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
curl-wget)
|
curl-wget)
|
||||||
local need=()
|
local need=()
|
||||||
! is_installed "curl" && need+=("curl")
|
! is_installed "curl" && need+=("curl")
|
||||||
! is_installed "wget" && need+=("wget")
|
! is_installed "wget" && need+=("wget")
|
||||||
if [ ${#need[@]} -gt 0 ]; then
|
if [ ${#need[@]} -gt 0 ]; then
|
||||||
_run_install_batch "${need[@]}"
|
_run_install_batch "${need[@]}"
|
||||||
else
|
else
|
||||||
echo "curl and wget already installed."
|
echo "curl and wget already installed."
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
extrepo)
|
extrepo)
|
||||||
install_backports_or_stable extrepo
|
install_backports_or_stable extrepo
|
||||||
;;
|
;;
|
||||||
fwupd)
|
flatpak)
|
||||||
if ! is_installed "fwupd"; then
|
if ! is_installed "flatpak"; then
|
||||||
_run_cmd "fwupd" "sudo apt install -y fwupd" "Installing fwupd..."
|
_run_cmd "Flatpak" "sudo apt install -y flatpak" "Installing Flatpak..."
|
||||||
else
|
if command -v plasma-discover &>/dev/null; then
|
||||||
echo "fwupd already installed."
|
_run_cmd "Flatpak" "sudo apt install -y plasma-discover-backend-flatpak" "Installing Flatpak backend..."
|
||||||
fi
|
echo "Flatpak backend for Discover installed."
|
||||||
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
|
elif command -v gnome-software &>/dev/null; then
|
||||||
_run_cmd "fwupd" "sudo fwupdmgr refresh --force" "Refreshing firmware metadata..."
|
_run_cmd "Flatpak" "sudo apt install -y gnome-software-plugin-flatpak" "Installing Flatpak plugin..."
|
||||||
echo ""
|
echo "Flatpak plugin for GNOME Software installed."
|
||||||
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
|
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "No firmware updates available."
|
echo "Flatpak already installed."
|
||||||
_pause
|
|
||||||
fi
|
fi
|
||||||
fi
|
flatpak remote-add --if-not-exists flathub \
|
||||||
echo -e "${GREEN}fwupd setup complete.${NC}"
|
https://dl.flathub.org/repo/flathub.flatpakrepo
|
||||||
_pause
|
echo "Flathub repository added."
|
||||||
;;
|
echo -e "${GREEN}A reboot is recommended.${NC}"
|
||||||
kvm)
|
;;
|
||||||
if ! is_installed "virt-manager"; then
|
fwupd)
|
||||||
_run_cmd "KVM" "sudo apt install -y qemu-system-x86 qemu-utils libvirt-daemon-system libvirt-clients bridge-utils virt-manager" "Installing KVM..."
|
if ! is_installed "fwupd"; then
|
||||||
sudo adduser "$USER" libvirt 2>/dev/null || true
|
_run_cmd "fwupd" "sudo apt install -y fwupd" "Installing fwupd..."
|
||||||
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
|
else
|
||||||
echo -e "${YELLOW}Wine installed but version check failed.${NC}"
|
echo "fwupd already installed."
|
||||||
fi
|
fi
|
||||||
else
|
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
|
||||||
echo "Wine64 already installed."
|
_run_cmd "fwupd" "sudo fwupdmgr refresh --force" "Refreshing firmware metadata..."
|
||||||
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 ""
|
echo ""
|
||||||
done
|
echo "Checking for firmware updates..."
|
||||||
_pause
|
sudo fwupdmgr get-updates 2>&1 || true
|
||||||
fi
|
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..."
|
||||||
if ! is_installed "$pkg"; then
|
else
|
||||||
_run_install "$pkg"
|
echo "Skipping firmware update."
|
||||||
else
|
fi
|
||||||
echo "$pkg already installed."
|
else
|
||||||
fi
|
echo "No firmware updates available."
|
||||||
;;
|
fi
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}fwupd setup complete.${NC}"
|
||||||
|
;;
|
||||||
|
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 "wine"; then
|
||||||
|
echo "Checking for i386 architecture..."
|
||||||
|
if ! dpkg --print-foreign-architectures 2>/dev/null | grep -q i386; then
|
||||||
|
echo "Enabling i386 architecture for Wine..."
|
||||||
|
sudo dpkg --add-architecture i386
|
||||||
|
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
|
||||||
|
fi
|
||||||
|
_run_cmd "Wine" "sudo apt install -y wine wine32 wine64 libwine libwine:i386 fonts-wine" "Installing Wine..."
|
||||||
|
echo -e "${GREEN}Wine installed. Run 'winecfg' to configure.${NC}"
|
||||||
|
else
|
||||||
|
echo "Wine already installed."
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if $headless; then
|
||||||
|
echo "Skipping $pkg (headless mode)"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if ! is_installed "$pkg"; then
|
||||||
|
_run_install "$pkg"
|
||||||
|
else
|
||||||
|
echo "$pkg already installed."
|
||||||
|
fi
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
|
|
||||||
echo -e "${GREEN}System tools installed.${NC}"
|
echo -e "${GREEN}System tools installed.${NC}"
|
||||||
_pause
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# terminals.sh — Terminal emulators
|
|
||||||
|
|
||||||
_cat_terminals() {
|
|
||||||
local headless=false
|
|
||||||
_is_headless && headless=true
|
|
||||||
if $headless; then
|
|
||||||
echo "Terminals require a GUI — skipping."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
local -a items=()
|
|
||||||
for t in kitty deepin-terminal gnome-terminal lxterminal mate-terminal \
|
|
||||||
qterminal xfce4-terminal konsole terminator guake cool-retro-term \
|
|
||||||
tilix yakuake terminology rxvt-unicode xterm; do
|
|
||||||
items+=("$t" "" "$(_state "$t")")
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$DEBIAN_VERSION" = "12" ] || [ "$DEBIAN_VERSION" = "13" ]; then
|
|
||||||
items+=("alacritty" "" "$(_state alacritty)")
|
|
||||||
items+=("zutty" "" "$(_state zutty)")
|
|
||||||
fi
|
|
||||||
if [ "$DEBIAN_VERSION" = "13" ]; then
|
|
||||||
items+=("blackbox-terminal" "" "$(_state blackbox-terminal)")
|
|
||||||
items+=("ptyxis" "" "$(_state ptyxis)")
|
|
||||||
fi
|
|
||||||
|
|
||||||
local item_count=${#items[@]}
|
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
|
||||||
choices=$(_checklist "Terminals" "Check [*] the packages you want installed/updated on your system.\n" \
|
|
||||||
$TUI_ALTO $TUI_ANCHO $lista_alto "${items[@]}")
|
|
||||||
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
|
|
||||||
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}"
|
|
||||||
}
|
|
||||||
@@ -2,48 +2,30 @@
|
|||||||
# cursors.sh — Cursor themes
|
# cursors.sh — Cursor themes
|
||||||
|
|
||||||
_cat_cursors() {
|
_cat_cursors() {
|
||||||
local headless=false
|
local bibata_state; bibata_state=$(_state "bibata-cursor-theme")
|
||||||
_is_headless && headless=true
|
local breeze_state; breeze_state=$(_state "breeze-cursor-theme")
|
||||||
local -a items=()
|
local chameleon_state; chameleon_state=$(_state "chameleon-cursor-theme")
|
||||||
if ! $headless; then
|
local dmz_state; dmz_state=$(_state "dmz-cursor-theme")
|
||||||
local bibata_state; bibata_state=$(_state "bibata-cursor-theme")
|
local xcursor_state; xcursor_state=$(_state "xcursor-themes")
|
||||||
local breeze_state; breeze_state=$(_state "breeze-cursor-theme")
|
local oxygen_state; oxygen_state=$(_state "oxygencursors")
|
||||||
local chameleon_state; chameleon_state=$(_state "chameleon-cursor-theme")
|
|
||||||
local dmz_state; dmz_state=$(_state "dmz-cursor-theme")
|
|
||||||
local xcursor_state; xcursor_state=$(_state "xcursor-themes")
|
|
||||||
local oxygen_state; oxygen_state=$(_state "oxygencursors")
|
|
||||||
items+=(
|
|
||||||
"bibata-cursor-theme" "Bibata cursors" "$bibata_state"
|
|
||||||
"breeze-cursor-theme" "Breeze cursors (KDE)" "$breeze_state"
|
|
||||||
"chameleon-cursor-theme" "Chameleon cursors" "$chameleon_state"
|
|
||||||
"dmz-cursor-theme" "DMZ cursors" "$dmz_state"
|
|
||||||
"xcursor-themes" "X11 base cursors" "$xcursor_state"
|
|
||||||
"oxygencursors" "Oxygen cursors (KDE legacy)" "$oxygen_state"
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
|
|
||||||
local item_count=${#items[@]}
|
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
local choices
|
||||||
choices=$(_checklist "Cursor Themes" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
choices=$(whiptail --title "Cursor Themes" --checklist \
|
||||||
"${items[@]}" \
|
"Select cursor themes to install:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
||||||
)
|
"bibata-cursor-theme" "Bibata cursors$(_inst bibata-cursor-theme)" "$bibata_state" \
|
||||||
|
"breeze-cursor-theme" "Breeze cursors (KDE)$(_inst breeze-cursor-theme)" "$breeze_state" \
|
||||||
|
"chameleon-cursor-theme" "Chameleon cursors$(_inst chameleon-cursor-theme)" "$chameleon_state" \
|
||||||
|
"dmz-cursor-theme" "DMZ cursors$(_inst dmz-cursor-theme)" "$dmz_state" \
|
||||||
|
"xcursor-themes" "X11 base cursors$(_inst xcursor-themes)" "$xcursor_state" \
|
||||||
|
"oxygencursors" "Oxygen cursors (KDE legacy)$(_inst oxygencursors)" "$oxygen_state" \
|
||||||
|
3>&1 1>&2 2>&3)
|
||||||
clear
|
clear
|
||||||
|
|
||||||
[ -z "$choices" ] && return
|
[ -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.
|
for pkg in $cleaned; do
|
||||||
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
|
|
||||||
if ! is_installed "$pkg"; then
|
if ! is_installed "$pkg"; then
|
||||||
_run_install "$pkg"
|
_run_install "$pkg"
|
||||||
else
|
else
|
||||||
@@ -51,8 +33,5 @@ _cat_cursors() {
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
|
|
||||||
echo -e "${GREEN}Cursor themes installed.${NC}"
|
echo -e "${GREEN}Cursor themes installed.${NC}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,48 +4,36 @@
|
|||||||
_cat_themes() {
|
_cat_themes() {
|
||||||
local headless=false
|
local headless=false
|
||||||
_is_headless && headless=true
|
_is_headless && headless=true
|
||||||
local -a items=()
|
local arc_state; arc_state=$(_state "arc-theme")
|
||||||
if ! $headless; then
|
local blackbird_state; blackbird_state=$(_state "blackbird-gtk-theme")
|
||||||
local arc_state; arc_state=$(_state "arc-theme")
|
local bluebird_state; bluebird_state=$(_state "bluebird-gtk-theme")
|
||||||
local blackbird_state; blackbird_state=$(_state "blackbird-gtk-theme")
|
local breeze_gtk_state; breeze_gtk_state=$(_state "breeze-gtk-theme")
|
||||||
local bluebird_state; bluebird_state=$(_state "bluebird-gtk-theme")
|
local greybird_state; greybird_state=$(_state "greybird-gtk-theme")
|
||||||
local breeze_gtk_state; breeze_gtk_state=$(_state "breeze-gtk-theme")
|
local numix_gtk_state; numix_gtk_state=$(_state "numix-gtk-theme")
|
||||||
local greybird_state; greybird_state=$(_state "greybird-gtk-theme")
|
local orchis_state; orchis_state=$(_state "orchis-gtk-theme")
|
||||||
local numix_gtk_state; numix_gtk_state=$(_state "numix-gtk-theme")
|
|
||||||
local orchis_state; orchis_state=$(_state "orchis-gtk-theme")
|
|
||||||
items+=(
|
|
||||||
"arc-theme" "Arc GTK theme" "$arc_state"
|
|
||||||
"blackbird-gtk-theme" "Blackbird GTK theme" "$blackbird_state"
|
|
||||||
"bluebird-gtk-theme" "Bluebird GTK theme" "$bluebird_state"
|
|
||||||
"breeze-gtk-theme" "Breeze GTK theme (KDE port)" "$breeze_gtk_state"
|
|
||||||
"greybird-gtk-theme" "Greybird GTK theme" "$greybird_state"
|
|
||||||
"numix-gtk-theme" "Numix GTK theme" "$numix_gtk_state"
|
|
||||||
"orchis-gtk-theme" "Orchis GTK theme" "$orchis_state"
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
|
|
||||||
local item_count=${#items[@]}
|
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
local choices
|
||||||
choices=$(_checklist "Desktop Themes (GTK/KDE)" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
choices=$(whiptail --title "Desktop Themes (GTK/KDE)" --checklist \
|
||||||
"${items[@]}" \
|
"Select desktop themes to install:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
||||||
)
|
"arc-theme" "Arc GTK theme$(_inst arc-theme)" "$arc_state" \
|
||||||
|
"blackbird-gtk-theme" "Blackbird GTK theme$(_inst blackbird-gtk-theme)" "$blackbird_state" \
|
||||||
|
"bluebird-gtk-theme" "Bluebird GTK theme$(_inst bluebird-gtk-theme)" "$bluebird_state" \
|
||||||
|
"breeze-gtk-theme" "Breeze GTK theme (KDE port)$(_inst breeze-gtk-theme)" "$breeze_gtk_state" \
|
||||||
|
"greybird-gtk-theme" "Greybird GTK theme$(_inst greybird-gtk-theme)" "$greybird_state" \
|
||||||
|
"numix-gtk-theme" "Numix GTK theme$(_inst numix-gtk-theme)" "$numix_gtk_state" \
|
||||||
|
"orchis-gtk-theme" "Orchis GTK theme$(_inst orchis-gtk-theme)" "$orchis_state" \
|
||||||
|
3>&1 1>&2 2>&3)
|
||||||
clear
|
clear
|
||||||
|
|
||||||
[ -z "$choices" ] && return
|
[ -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.
|
for pkg in $cleaned; do
|
||||||
local -a _pkgs=()
|
if $headless; then
|
||||||
while IFS= read -r _pkg; do
|
echo "Skipping $pkg (headless mode)"
|
||||||
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
|
continue
|
||||||
done < <(echo "$cleaned" | tr ' ' '\n')
|
fi
|
||||||
|
|
||||||
# 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
|
|
||||||
if ! is_installed "$pkg"; then
|
if ! is_installed "$pkg"; then
|
||||||
_run_install "$pkg"
|
_run_install "$pkg"
|
||||||
else
|
else
|
||||||
@@ -53,8 +41,5 @@ _cat_themes() {
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
|
|
||||||
echo -e "${GREEN}Desktop themes installed.${NC}"
|
echo -e "${GREEN}Desktop themes installed.${NC}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,52 +2,34 @@
|
|||||||
# fonts.sh — Fonts
|
# fonts.sh — Fonts
|
||||||
|
|
||||||
_cat_fonts() {
|
_cat_fonts() {
|
||||||
local headless=false
|
local bebas_state; bebas_state=$(_state "fonts-bebas-neue")
|
||||||
_is_headless && headless=true
|
local anon_state; anon_state=$(_state "fonts-anonymous-pro")
|
||||||
local -a items=()
|
local verana_state; verana_state=$(_state "fonts-adf-verana")
|
||||||
if ! $headless; then
|
local f3270_state; f3270_state=$(_state "fonts-3270")
|
||||||
local bebas_state; bebas_state=$(_state "fonts-bebas-neue")
|
local liberation_state; liberation_state=$(_state "fonts-liberation")
|
||||||
local anon_state; anon_state=$(_state "fonts-anonymous-pro")
|
local mscore_state; mscore_state=$(_state "ttf-mscorefonts-installer")
|
||||||
local verana_state; verana_state=$(_state "fonts-adf-verana")
|
local ubuntu_state; ubuntu_state=$(_state "fonts-ubuntu")
|
||||||
local f3270_state; f3270_state=$(_state "fonts-3270")
|
local recommended_state; recommended_state=$(_state "fonts-recommended")
|
||||||
local liberation_state; liberation_state=$(_state "fonts-liberation")
|
|
||||||
local mscore_state; mscore_state=$(_state "ttf-mscorefonts-installer")
|
|
||||||
local ubuntu_state; ubuntu_state=$(_state "fonts-ubuntu")
|
|
||||||
local recommended_state; recommended_state=$(_state "fonts-recommended")
|
|
||||||
items+=(
|
|
||||||
"fonts-bebas-neue" "Bebas Neue (display)" "$bebas_state"
|
|
||||||
"fonts-anonymous-pro" "Anonymous Pro (monospace)" "$anon_state"
|
|
||||||
"fonts-adf-verana" "ADF Verana (sans-serif)" "$verana_state"
|
|
||||||
"fonts-3270" "IBM 3270 terminal font" "$f3270_state"
|
|
||||||
"fonts-liberation" "Liberation (MS-compatible)" "$liberation_state"
|
|
||||||
"ttf-mscorefonts-installer" "Microsoft fonts (EULA required)" "$mscore_state"
|
|
||||||
"fonts-ubuntu" "Ubuntu font family" "$ubuntu_state"
|
|
||||||
"fonts-recommended" "Debian recommended fonts" "$recommended_state"
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
|
|
||||||
local item_count=${#items[@]}
|
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
local choices
|
||||||
choices=$(_checklist "Fonts" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
choices=$(whiptail --title "Fonts" --checklist \
|
||||||
"${items[@]}" \
|
"Select fonts to install:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
||||||
)
|
"fonts-bebas-neue" "Bebas Neue (display)$(_inst fonts-bebas-neue)" "$bebas_state" \
|
||||||
|
"fonts-anonymous-pro" "Anonymous Pro (monospace)$(_inst fonts-anonymous-pro)" "$anon_state" \
|
||||||
|
"fonts-adf-verana" "ADF Verana (sans-serif)$(_inst fonts-adf-verana)" "$verana_state" \
|
||||||
|
"fonts-3270" "IBM 3270 terminal font$(_inst fonts-3270)" "$f3270_state" \
|
||||||
|
"fonts-liberation" "Liberation (MS-compatible)$(_inst fonts-liberation)" "$liberation_state" \
|
||||||
|
"ttf-mscorefonts-installer" "Microsoft fonts (EULA required)$(_inst ttf-mscorefonts-installer)" "$mscore_state" \
|
||||||
|
"fonts-ubuntu" "Ubuntu font family$(_inst fonts-ubuntu)" "$ubuntu_state" \
|
||||||
|
"fonts-recommended" "Debian recommended fonts$(_inst fonts-recommended)" "$recommended_state" \
|
||||||
|
3>&1 1>&2 2>&3)
|
||||||
clear
|
clear
|
||||||
|
|
||||||
[ -z "$choices" ] && return
|
[ -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.
|
for pkg in $cleaned; do
|
||||||
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
|
|
||||||
if ! is_installed "$pkg"; then
|
if ! is_installed "$pkg"; then
|
||||||
_run_install "$pkg"
|
_run_install "$pkg"
|
||||||
else
|
else
|
||||||
@@ -55,8 +37,5 @@ _cat_fonts() {
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
|
|
||||||
echo -e "${GREEN}Fonts installed.${NC}"
|
echo -e "${GREEN}Fonts installed.${NC}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,65 +4,58 @@
|
|||||||
_cat_icons() {
|
_cat_icons() {
|
||||||
local headless=false
|
local headless=false
|
||||||
_is_headless && headless=true
|
_is_headless && headless=true
|
||||||
local -a items=()
|
local breeze_state; breeze_state=$(_state "breeze-icon-theme")
|
||||||
if ! $headless; then
|
local deepin_state; deepin_state=$(_state "deepin-icon-theme")
|
||||||
local breeze_state; breeze_state=$(_state "breeze-icon-theme")
|
local ele_state; ele_state=$(_state "elementary-icon-theme")
|
||||||
local deepin_state; deepin_state=$(_state "deepin-icon-theme")
|
local ele_xfce_state; ele_xfce_state=$(_state "elementary-xfce-icon-theme")
|
||||||
local ele_state; ele_state=$(_state "elementary-icon-theme")
|
local moka_state; moka_state=$(_state "moka-icon-theme")
|
||||||
local ele_xfce_state; ele_xfce_state=$(_state "elementary-xfce-icon-theme")
|
local numix_state; numix_state=$(_state "numix-icon-theme")
|
||||||
local moka_state; moka_state=$(_state "moka-icon-theme")
|
local numix_c_state; numix_c_state=$(_state "numix-icon-theme-circle")
|
||||||
local numix_state; numix_state=$(_state "numix-icon-theme")
|
local obsidian_state; obsidian_state=$(_state "obsidian-icon-theme")
|
||||||
local numix_c_state; numix_c_state=$(_state "numix-icon-theme-circle")
|
local papirus_state; papirus_state=$(_state "papirus-icon-theme")
|
||||||
local obsidian_state; obsidian_state=$(_state "obsidian-icon-theme")
|
local paper_state; paper_state=$(_state "paper-icon-theme")
|
||||||
local papirus_state; papirus_state=$(_state "papirus-icon-theme")
|
local suru_state; suru_state=$(_state "suru-icon-theme")
|
||||||
local paper_state; paper_state=$(_state "paper-icon-theme")
|
|
||||||
local suru_state; suru_state=$(_state "suru-icon-theme")
|
|
||||||
|
|
||||||
local kf6_state="OFF"
|
local kf6_state="OFF"
|
||||||
if [ "$DEBIAN_CODENAME" = "trixie" ]; then
|
local has_kf6=false
|
||||||
kf6_state=$(_state "kf6-breeze-icon-theme")
|
if [ "$DEBIAN_CODENAME" = "trixie" ]; then
|
||||||
fi
|
kf6_state=$(_state "kf6-breeze-icon-theme")
|
||||||
|
has_kf6=true
|
||||||
items=(
|
fi
|
||||||
"breeze-icon-theme" "Breeze icon theme" "$breeze_state"
|
|
||||||
"deepin-icon-theme" "Deepin icon theme" "$deepin_state"
|
local items=(
|
||||||
"elementary-icon-theme" "Elementary icon theme" "$ele_state"
|
"breeze-icon-theme" "Breeze icon theme$(_inst breeze-icon-theme)" "$breeze_state"
|
||||||
"elementary-xfce-icon-theme" "Elementary Xfce icons" "$ele_xfce_state"
|
"deepin-icon-theme" "Deepin icon theme$(_inst deepin-icon-theme)" "$deepin_state"
|
||||||
"moka-icon-theme" "Moka icon theme" "$moka_state"
|
"elementary-icon-theme" "Elementary icon theme$(_inst elementary-icon-theme)" "$ele_state"
|
||||||
"numix-icon-theme" "Numix icon theme" "$numix_state"
|
"elementary-xfce-icon-theme" "Elementary Xfce icons$(_inst elementary-xfce-icon-theme)" "$ele_xfce_state"
|
||||||
"numix-icon-theme-circle" "Numix Circle icon theme" "$numix_c_state"
|
"moka-icon-theme" "Moka icon theme$(_inst moka-icon-theme)" "$moka_state"
|
||||||
"obsidian-icon-theme" "Obsidian icon theme" "$obsidian_state"
|
"numix-icon-theme" "Numix icon theme$(_inst numix-icon-theme)" "$numix_state"
|
||||||
"papirus-icon-theme" "Papirus icon theme" "$papirus_state"
|
"numix-icon-theme-circle" "Numix Circle icon theme$(_inst numix-icon-theme-circle)" "$numix_c_state"
|
||||||
"paper-icon-theme" "Paper icon theme" "$paper_state"
|
"obsidian-icon-theme" "Obsidian icon theme$(_inst obsidian-icon-theme)" "$obsidian_state"
|
||||||
"suru-icon-theme" "Suru icon theme" "$suru_state"
|
"papirus-icon-theme" "Papirus icon theme$(_inst papirus-icon-theme)" "$papirus_state"
|
||||||
)
|
"paper-icon-theme" "Paper icon theme$(_inst paper-icon-theme)" "$paper_state"
|
||||||
if [ "$DEBIAN_CODENAME" = "trixie" ]; then
|
"suru-icon-theme" "Suru icon theme$(_inst suru-icon-theme)" "$suru_state"
|
||||||
items+=("kf6-breeze-icon-theme" "KF6 Breeze icon theme" "$kf6_state")
|
)
|
||||||
fi
|
if $has_kf6; then
|
||||||
|
items+=("kf6-breeze-icon-theme" "KF6 Breeze icon theme$(_inst kf6-breeze-icon-theme)" "$kf6_state")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local item_count=${#items[@]}
|
|
||||||
local lista_alto=$((item_count > TUI_ALTO_LISTA ? TUI_ALTO_LISTA : item_count))
|
|
||||||
local choices
|
local choices
|
||||||
choices=$(_checklist "Icon Themes" "Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $lista_alto \
|
choices=$(whiptail --title "Icon Themes" --checklist \
|
||||||
|
"Select icon themes to install:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
||||||
"${items[@]}" \
|
"${items[@]}" \
|
||||||
)
|
3>&1 1>&2 2>&3)
|
||||||
clear
|
clear
|
||||||
|
|
||||||
[ -z "$choices" ] && return
|
[ -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.
|
for pkg in $cleaned; do
|
||||||
local -a _pkgs=()
|
if $headless; then
|
||||||
while IFS= read -r _pkg; do
|
echo "Skipping $pkg (headless mode)"
|
||||||
[ -n "$_pkg" ] && _pkgs+=("$_pkg")
|
continue
|
||||||
done < <(echo "$cleaned" | tr ' ' '\n')
|
fi
|
||||||
|
|
||||||
# Pre-apply summary: confirm the selection once,
|
|
||||||
# before any apt transaction starts.
|
|
||||||
_confirm_install_list "Icon Themes" "${_pkgs[@]}" || return
|
|
||||||
for pkg in "${_pkgs[@]}"; do
|
|
||||||
if ! is_installed "$pkg"; then
|
if ! is_installed "$pkg"; then
|
||||||
_run_install "$pkg"
|
_run_install "$pkg"
|
||||||
else
|
else
|
||||||
@@ -70,8 +63,5 @@ _cat_icons() {
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Release the per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
|
|
||||||
echo -e "${GREEN}Icon themes installed.${NC}"
|
echo -e "${GREEN}Icon themes installed.${NC}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,20 +2,20 @@
|
|||||||
# themes.sh — Customization submenu dispatcher
|
# themes.sh — Customization submenu dispatcher
|
||||||
|
|
||||||
_cat_customization() {
|
_cat_customization() {
|
||||||
|
local TUI_ANCHO_REFORZADO=$((TUI_ANCHO + 6))
|
||||||
local sub
|
local sub
|
||||||
sub=$(_menu "Customization System" "Select type:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
sub=$(whiptail --title "Customization System" --menu \
|
||||||
|
"Select type:" $TUI_ALTO $TUI_ANCHO_REFORZADO $TUI_ALTO_LISTA \
|
||||||
"1" "Desktop Themes (GTK/KDE)" \
|
"1" "Desktop Themes (GTK/KDE)" \
|
||||||
"2" "Icon Themes" \
|
"2" "Icon Themes" \
|
||||||
"3" "Cursor Themes" \
|
"3" "Cursor Themes" \
|
||||||
"4" "Fonts" \
|
"4" "Fonts" \
|
||||||
"5" "Terminals" \
|
3>&1 1>&2 2>&3)
|
||||||
)
|
|
||||||
[ -z "$sub" ] && return
|
[ -z "$sub" ] && return
|
||||||
case $sub in
|
case $sub in
|
||||||
1) _cat_themes ;;
|
1) _cat_themes ;;
|
||||||
2) _cat_icons ;;
|
2) _cat_icons ;;
|
||||||
3) _cat_cursors ;;
|
3) _cat_cursors ;;
|
||||||
4) _cat_fonts ;;
|
4) _cat_fonts ;;
|
||||||
5) _cat_terminals ;;
|
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,430 +1,91 @@
|
|||||||
#!/usr/bin/env bash
|
#!/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.
|
|
||||||
_DETECTED_FW_PKGS=()
|
|
||||||
_FW_PLAN_PKG_LINES=()
|
|
||||||
|
|
||||||
# ── Firmware package mapping ──
|
|
||||||
_detect_firmware_needs() {
|
|
||||||
local -A pkg_info
|
|
||||||
_DETECTED_FW_PKGS=()
|
|
||||||
_FW_PLAN_PKG_LINES=()
|
|
||||||
|
|
||||||
local dev_list
|
|
||||||
dev_list=("${PCI_NET_DEVS[@]}" "${USB_WIFI_DEVS[@]}")
|
|
||||||
|
|
||||||
for dev in "${dev_list[@]}"; do
|
|
||||||
local raw_desc vendor_lc pkg
|
|
||||||
raw_desc=$(echo "$dev" | sed -E 's/^[^ ]+ [^:]+: //; s/ \[[0-9a-fA-F]{4}:[0-9a-fA-F]{4}\]//; s/ \(rev.*\)//')
|
|
||||||
vendor_lc=$(echo "$dev" | sed -n 's/^.*]: //p' | awk '{print $1}' | tr '[:upper:]' '[:lower:]')
|
|
||||||
|
|
||||||
[[ "$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" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
local short_dev
|
|
||||||
short_dev=$(echo "$raw_desc" | sed 's/ *\[[^]]*\]//g; s/ */ /g')
|
|
||||||
if [ -z "${pkg_info[$pkg]-}" ]; then
|
|
||||||
pkg_info[$pkg]="$short_dev"
|
|
||||||
else
|
|
||||||
pkg_info[$pkg]+=", $short_dev"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
for pkg in "${!pkg_info[@]}"; do
|
|
||||||
_DETECTED_FW_PKGS+=("$pkg")
|
|
||||||
_FW_PLAN_PKG_LINES+=(" [+] ${pkg} \xe2\x86\x90 ${pkg_info[$pkg]}")
|
|
||||||
done
|
|
||||||
|
|
||||||
mapfile -t _DETECTED_FW_PKGS < <(printf '%s\n' "${_DETECTED_FW_PKGS[@]}" | sort -u)
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── Build plan string ──
|
|
||||||
_build_firmware_plan() {
|
|
||||||
local plan=""
|
|
||||||
plan+="This section installs the essential non-free firmware stack\n"
|
|
||||||
plan+="for Debian (including CPU microcode, GPU, network, and core\n"
|
|
||||||
plan+="drivers). The script has scanned your hardware to prepare\n"
|
|
||||||
plan+="the setup:\n\n"
|
|
||||||
|
|
||||||
plan+="Detected controllers:\n"
|
|
||||||
if [ ${#_FW_PLAN_HW_LINES[@]} -eq 0 ]; then
|
|
||||||
plan+=" (none detected)\n"
|
|
||||||
else
|
|
||||||
for line in "${_FW_PLAN_HW_LINES[@]}"; do
|
|
||||||
plan+="${line}\n"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
plan+="\nPlanned firmware packages:\n"
|
|
||||||
local fw_line
|
|
||||||
if is_installed firmware-linux-nonfree; then
|
|
||||||
local cur_ver
|
|
||||||
cur_ver=$(_get_installed_version "firmware-linux-nonfree")
|
|
||||||
fw_line=" [+] firmware-linux-nonfree ${cur_ver} (already installed)"
|
|
||||||
else
|
|
||||||
fw_line=" [+] firmware-linux-nonfree (base meta-package)"
|
|
||||||
fi
|
|
||||||
plan+="${fw_line}\n"
|
|
||||||
|
|
||||||
for line in "${_FW_PLAN_PKG_LINES[@]}"; do
|
|
||||||
plan+="${line}\n"
|
|
||||||
done
|
|
||||||
|
|
||||||
local has_bt=false
|
|
||||||
[ ${#PCI_BT_DEVS[@]} -gt 0 ] && has_bt=true
|
|
||||||
[ ${#USB_BT_DEVS[@]} -gt 0 ] && has_bt=true
|
|
||||||
if ! $has_bt; then
|
|
||||||
for dev in "${USB_WIFI_DEVS[@]}"; do
|
|
||||||
if echo "$dev" | grep -qi 'bluetooth'; then
|
|
||||||
has_bt=true
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
plan+="\nBluetooth:\n"
|
|
||||||
if $has_bt; then
|
|
||||||
if is_installed bluez; then
|
|
||||||
plan+=" [+] bluez (already installed)\n"
|
|
||||||
else
|
|
||||||
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" ;;
|
|
||||||
esac
|
|
||||||
plan+=" → Bluetooth service will be enabled\n"
|
|
||||||
else
|
|
||||||
plan+=" (no Bluetooth hardware detected)\n"
|
|
||||||
fi
|
|
||||||
|
|
||||||
plan+="\nInstallation order:\n"
|
|
||||||
plan+=" 1. Base firmware (firmware-linux-nonfree)\n"
|
|
||||||
plan+=" 2. Network firmware (realtek, iwlwifi, ...)\n"
|
|
||||||
plan+=" 3. Broadcom / Bluetooth firmware\n"
|
|
||||||
|
|
||||||
echo -e "$plan"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── Install detected firmware packages ──
|
|
||||||
_install_detected_firmware() {
|
|
||||||
local to_install=()
|
|
||||||
for pkg in "${_DETECTED_FW_PKGS[@]}"; do
|
|
||||||
if is_installed "$pkg"; then
|
|
||||||
echo " --> $pkg already installed."
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
local ver
|
|
||||||
ver=$(_get_pkg_version "$pkg")
|
|
||||||
if [ -z "$ver" ] || [ "$ver" = "(none)" ]; then
|
|
||||||
echo " --> $pkg not available in repositories, skipping."
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
to_install+=("$pkg")
|
|
||||||
done
|
|
||||||
if [ ${#to_install[@]} -gt 0 ]; then
|
|
||||||
_run_cmd "Firmware" "sudo DEBIAN_FRONTEND=noninteractive apt install -y ${to_install[*]}" \
|
|
||||||
"Installing network firmware packages..." || true
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── Wireless handler (Broadcom single-path wl) ──
|
|
||||||
_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
|
|
||||||
|
|
||||||
for dev in "${PCI_NET_DEVS[@]}"; do
|
|
||||||
local bcm_id
|
|
||||||
bcm_id=$(echo "$dev" | grep -oP '14e4:[0-9a-fA-F]+' || true)
|
|
||||||
[ -z "$bcm_id" ] && continue
|
|
||||||
|
|
||||||
# --- 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"
|
|
||||||
_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
|
|
||||||
|
|
||||||
# --- 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
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
wl_build_failed=true
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
_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
|
|
||||||
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
|
|
||||||
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
|
|
||||||
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
|
|
||||||
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 [ -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
|
|
||||||
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
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── Main entry point ──
|
|
||||||
install_firmware() {
|
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}"
|
echo -e "${YELLOW}Base firmware check...${NC}"
|
||||||
|
|
||||||
if ! _ensure_nonfree_repo; then
|
|
||||||
_msg "Error" "No 'non-free' repositories were enabled and the user declined to add them.\nPlease enable non-free manually or accept the prompt in the Firmware option." 10 65
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 1. Detect
|
|
||||||
_detect_all_network_devices
|
|
||||||
_detect_firmware_needs
|
|
||||||
|
|
||||||
# 2. Plan
|
|
||||||
local plan
|
|
||||||
plan=$(_build_firmware_plan)
|
|
||||||
_msg "Firmware, Wireless & Bluetooth Setup" "$plan" 22 72
|
|
||||||
|
|
||||||
# 3. Confirm
|
|
||||||
if ! _confirm "Firmware" "Apply the network & firmware plan?"; then
|
|
||||||
echo "Firmware installation skipped."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 4. Install base firmware meta-package (unchanged logic)
|
|
||||||
local fw_pkg="firmware-linux-nonfree"
|
local fw_pkg="firmware-linux-nonfree"
|
||||||
local fw_bpo
|
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
|
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 is_installed "$fw_pkg"; then
|
||||||
if [ -n "$fw_bpo" ]; then
|
if [ -n "$fw_bpo" ]; then
|
||||||
local current_ver
|
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
|
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
|
fi
|
||||||
else
|
else
|
||||||
echo "$fw_pkg already installed."
|
echo "$fw_pkg already installed."
|
||||||
fi
|
fi
|
||||||
else
|
handle_wifi_firmware
|
||||||
local msg="firmware-linux-nonfree provides hardware drivers for:\n"
|
return
|
||||||
msg+=" WiFi, Bluetooth, GPU, audio, webcams, and more.\n\n"
|
|
||||||
if [ -n "$fw_bpo" ]; then
|
|
||||||
msg+=" ● Stable: ${fw_stable}\n"
|
|
||||||
msg+=" Ultra tested, but may lack support for\n"
|
|
||||||
msg+=" very recent hardware.\n\n"
|
|
||||||
msg+=" ● Backports: ${fw_bpo} (Recommended)\n"
|
|
||||||
msg+=" Updated firmware for modern hardware from\n"
|
|
||||||
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
|
|
||||||
else
|
|
||||||
_run_cmd "Firmware" "sudo apt install -y $fw_pkg" "Installing firmware from stable..." || true
|
|
||||||
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
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
echo -e "${GREEN}Base firmware installed.${NC}"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 5. Install specific network firmware packages
|
local msg="firmware-linux-nonfree provides hardware drivers for:\n"
|
||||||
_install_detected_firmware
|
msg+=" WiFi, Bluetooth, GPU, audio, webcams, and more.\n\n"
|
||||||
|
if [ -n "$fw_bpo" ]; then
|
||||||
|
msg+=" Backports: ${fw_bpo} (newer, recommended)\n"
|
||||||
|
msg+=" Stable: ${fw_stable}\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..."
|
||||||
|
else
|
||||||
|
_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..."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# 6. Broadcom wireless handler
|
echo -e "${GREEN}Base firmware installed.${NC}"
|
||||||
_handle_wireless
|
handle_wifi_firmware
|
||||||
|
}
|
||||||
# 7. Bluetooth stack
|
|
||||||
_install_bluetooth_stack
|
# ---------------------------
|
||||||
|
# Specific WiFi firmware
|
||||||
# 8. Summary
|
# ---------------------------
|
||||||
echo -e "${GREEN}Network & firmware setup complete.${NC}"
|
handle_wifi_firmware() {
|
||||||
_pause
|
if [ -z "$WIFI_CHIPSET" ] || [ "$WIFI_CHIPSET" = "No WiFi adapter found" ]; then
|
||||||
|
echo "No WiFi adapter to configure."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Detected WiFi: $WIFI_CHIPSET"
|
||||||
|
|
||||||
|
# Broadcom IDs often start with 14e4:
|
||||||
|
local broadcom_id
|
||||||
|
broadcom_id=$(lspci -nn | grep -i network | grep -oP '14e4:[0-9a-fA-F]+' | head -n1)
|
||||||
|
if [ -n "$broadcom_id" ]; then
|
||||||
|
echo -e "${YELLOW}Broadcom wireless device found (ID: $broadcom_id)${NC}"
|
||||||
|
|
||||||
|
# List of device IDs supported by brcmsmac/brcmfmac
|
||||||
|
local supported_ids="4357 4358 4360 4727 43a0 43a1 43a2 43b1"
|
||||||
|
local device_id
|
||||||
|
device_id=$(echo "$broadcom_id" | cut -d: -f2)
|
||||||
|
|
||||||
|
if echo "$supported_ids" | grep -qw "$device_id"; then
|
||||||
|
_run_install_pkg firmware-brcm80211
|
||||||
|
else
|
||||||
|
# Offer to install broadcom-sta-dkms for older chips
|
||||||
|
local bcm_ver
|
||||||
|
bcm_ver=$(apt-cache policy broadcom-sta-dkms 2>/dev/null | awk 'NR==3 {print $2; exit}')
|
||||||
|
local header_ver
|
||||||
|
header_ver=$(apt-cache policy linux-headers-$(uname -r) 2>/dev/null | awk 'NR==3 {print $2; exit}')
|
||||||
|
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 apt install -y linux-headers-$(uname -r) broadcom-sta-dkms" "Installing Broadcom driver..."
|
||||||
|
echo "Broadcom proprietary driver installed. A reboot may be required."
|
||||||
|
else
|
||||||
|
echo "Skipping Broadcom driver installation."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "No special WiFi firmware needed for this adapter."
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,23 +27,9 @@ ensure_contrib_repo() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! _confirm "contrib Repository" "Component 'contrib' is required for Steam.\n\nAdd 'contrib' to your APT repositories?"; then
|
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 [ -f /etc/apt/sources.list ]; 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)"
|
sudo sed -i '/^deb / { /contrib/! s/main/main contrib/ }' /etc/apt/sources.list
|
||||||
# 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
|
|
||||||
fi
|
fi
|
||||||
if [ -d /etc/apt/sources.list.d ]; then
|
if [ -d /etc/apt/sources.list.d ]; then
|
||||||
for f in /etc/apt/sources.list.d/*.sources; do
|
for f in /etc/apt/sources.list.d/*.sources; do
|
||||||
@@ -51,131 +37,133 @@ ensure_contrib_repo() {
|
|||||||
sudo sed -i '/^Components:/ { /contrib/! s/$/ contrib/ }' "$f"
|
sudo sed -i '/^Components:/ { /contrib/! s/$/ contrib/ }' "$f"
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
sudo apt update
|
||||||
|
echo -e "${GREEN}contrib repository enabled.${NC}"
|
||||||
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Verify the component was actually added before reporting success
|
echo -e "${YELLOW}contrib repository not enabled. Steam installation may fail.${NC}"
|
||||||
local contrib_ok=false
|
return 1
|
||||||
[ -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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
install_gaming() {
|
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}"
|
echo -e "${YELLOW}Gaming setup...${NC}"
|
||||||
|
|
||||||
# 1. Single checklist with ALL options (including i386 toggle)
|
# 1. 32-bit support prompt FIRST
|
||||||
|
local enable_32bit=false
|
||||||
|
if _confirm "32-bit Support" "Enable i386 architecture for 32-bit games?\n\nRequired by Steam/Proton for 32-bit games.\nInstalls matching 32-bit graphics drivers."; then
|
||||||
|
enable_32bit=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $enable_32bit; then
|
||||||
|
echo "Enabling 32-bit architecture (i386)..."
|
||||||
|
if ! dpkg --print-foreign-architectures | grep -q i386; then
|
||||||
|
sudo dpkg --add-architecture i386
|
||||||
|
fi
|
||||||
|
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
|
||||||
|
|
||||||
|
echo "Installing 32-bit graphics drivers..."
|
||||||
|
if [ "$GPU_TYPE" = "nvidia" ]; then
|
||||||
|
case "${NVIDIA_DRIVER_MODE:-stable}" in
|
||||||
|
cuda-repo)
|
||||||
|
_msg "NVIDIA 32-bit" \
|
||||||
|
"32-bit NVIDIA CUDA libraries already deployed.\n\nThe complete multiarch stack was installed\nwith the v590 driver from the official CUDA repo." 10 70
|
||||||
|
;;
|
||||||
|
backports)
|
||||||
|
local nv32_pkg="nvidia-driver-libs:i386"
|
||||||
|
local nv32_ver
|
||||||
|
nv32_ver=$(apt-cache policy "$nv32_pkg" 2>/dev/null | awk 'NR==3 {print $2; exit}')
|
||||||
|
local msg="Source: Debian ${DEBIAN_CODENAME^}-Backports\n"
|
||||||
|
msg+="NVIDIA 32-bit Libraries ${nv32_ver:-unknown}\n\n"
|
||||||
|
msg+="[+] nvidia-driver-libs:i386"
|
||||||
|
if _confirm "NVIDIA 32-bit" "$msg" 12 70; then
|
||||||
|
_run_cmd "32-bit NVIDIA" "sudo apt install -y -t ${DEBIAN_CODENAME}-backports ${nv32_pkg}" \
|
||||||
|
"Installing 32-bit NVIDIA drivers from backports..."
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
stable)
|
||||||
|
local nv32_pkg="nvidia-driver-libs:i386"
|
||||||
|
local nv32_ver
|
||||||
|
nv32_ver=$(apt-cache policy "$nv32_pkg" 2>/dev/null | awk 'NR==3 {print $2; exit}')
|
||||||
|
local use_bpo32=false
|
||||||
|
if [ "$(is_backports_enabled)" == "true" ]; then
|
||||||
|
local bpo_nv32_ver
|
||||||
|
bpo_nv32_ver=$(apt-cache madison "$nv32_pkg" 2>/dev/null | \
|
||||||
|
grep "${DEBIAN_CODENAME}-backports" | awk '{print $3}' | head -1)
|
||||||
|
if [ -n "$bpo_nv32_ver" ]; then
|
||||||
|
local msg="Source: Debian ${DEBIAN_CODENAME^} (Backports available)\n"
|
||||||
|
msg+="NVIDIA 32-bit Libraries: ${nv32_pkg}\n\n"
|
||||||
|
msg+=" Backports: ${bpo_nv32_ver}\n"
|
||||||
|
msg+=" Stable: ${nv32_ver:-unknown}\n\n"
|
||||||
|
msg+="Choose version:"
|
||||||
|
if _confirm_custom "NVIDIA 32-bit" "$msg" "Backports" "Stable" 14 70; then
|
||||||
|
use_bpo32=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if $use_bpo32; then
|
||||||
|
_run_cmd "32-bit NVIDIA" "sudo apt install -y -t ${DEBIAN_CODENAME}-backports ${nv32_pkg}" \
|
||||||
|
"Installing 32-bit NVIDIA drivers from backports..."
|
||||||
|
else
|
||||||
|
local msg="Source: Debian ${DEBIAN_CODENAME^} Stable\n"
|
||||||
|
msg+="NVIDIA 32-bit Libraries ${nv32_ver:-unknown}\n\n"
|
||||||
|
msg+="[+] nvidia-driver-libs:i386"
|
||||||
|
if _confirm "NVIDIA 32-bit" "$msg" 12 70; then
|
||||||
|
_run_cmd "32-bit NVIDIA" "sudo apt install -y ${nv32_pkg}" \
|
||||||
|
"Installing 32-bit NVIDIA drivers..."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
_install_mesa_32bit
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Gaming packages checklist
|
||||||
local choices
|
local choices
|
||||||
choices=$(_checklist "Gaming Setup" \
|
choices=$(whiptail --title "Gaming Setup" --checklist \
|
||||||
"Check [*] the packages you want installed/updated on your system.\n" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
"Select gaming packages to install:" $TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
||||||
"i386" "Enable 32-bit (i386) architecture" ON \
|
"steam" "Steam (requires 32-bit support)" ON \
|
||||||
"steam" "Steam (requires 32-bit support)" ON \
|
"gamemode" "Game performance optimization" ON \
|
||||||
"mangohud" "Performance overlay (Vulkan/OpenGL)" ON \
|
"mangohud" "Performance overlay (Vulkan/OpenGL)" ON \
|
||||||
"gamemode" "Game performance optimization" OFF \
|
"heroic" "Heroic Launcher (Epic/GOG)" OFF \
|
||||||
"goverlay" "MangoHud config GUI" ON \
|
"goverlay" "MangoHud config GUI" ON \
|
||||||
"heroic" "Heroic Launcher (Epic/GOG)" OFF \
|
"lutris" "Game launcher/manager" OFF \
|
||||||
"java" "Minecraft Java Runtime" OFF \
|
3>&1 1>&2 2>&3)
|
||||||
"openrgb" "OpenRGB (RGB lighting control)" OFF \
|
|
||||||
"lutris" "Lutris + Wine (requires 32-bit support)" OFF \
|
|
||||||
"retroarch" "RetroArch Emulator Frontend" OFF)
|
|
||||||
|
|
||||||
if [ -z "$choices" ]; then
|
if [ -z "$choices" ]; then
|
||||||
echo "No gaming packages selected."
|
echo "No gaming packages selected."
|
||||||
_pause
|
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local cleaned
|
local cleaned
|
||||||
cleaned=$(echo "$choices" | tr -d '"')
|
cleaned=$(echo "$choices" | tr -d '"')
|
||||||
|
|
||||||
# 2. Determine if 32-bit is needed (steam, lutris, or explicit i386 toggle)
|
# 3. Warn if Steam selected without 32-bit
|
||||||
local need_32bit=false
|
if echo "$cleaned" | grep -qw "steam" && ! $enable_32bit; then
|
||||||
|
echo -e "${YELLOW}Warning: Steam requires 32-bit support.${NC}"
|
||||||
local -a install_pkgs=()
|
echo "Installation may fail. Re-run this option and enable 32-bit support."
|
||||||
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
|
|
||||||
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
|
fi
|
||||||
|
|
||||||
# 3. Enable i386 architecture if needed
|
# 4. Install selected packages
|
||||||
if $need_32bit && ! dpkg --print-foreign-architectures 2>/dev/null | grep -q i386; then
|
for pkg in $cleaned; do
|
||||||
echo -e "${YELLOW}Enabling i386 architecture (required by selection)...${NC}"
|
|
||||||
sudo dpkg --add-architecture i386
|
|
||||||
_ensure_apt_updated
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 4. Install 32-bit graphics drivers only if 32-bit is needed
|
|
||||||
if $need_32bit; then
|
|
||||||
echo "Installing 32-bit graphics drivers..."
|
|
||||||
if [ "$GPU_TYPE" = "nvidia" ]; then
|
|
||||||
_install_nvidia_32bit
|
|
||||||
else
|
|
||||||
_install_mesa_32bit
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 5. Install selected packages
|
|
||||||
for pkg in "${install_list[@]}"; do
|
|
||||||
case $pkg in
|
case $pkg in
|
||||||
steam)
|
steam)
|
||||||
if ensure_contrib_repo; then
|
if ensure_contrib_repo; then
|
||||||
install_steam
|
install_steam
|
||||||
else
|
else
|
||||||
echo -e "${YELLOW}Skipping Steam installation (contrib repository not enabled).${NC}"
|
echo -e "${YELLOW}Skipping Steam installation (contrib repository not enabled).${NC}"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
heroic) install_heroic ;;
|
heroic) install_heroic ;;
|
||||||
java) install_minecraft_java ;;
|
mangohud) install_mangohud ;;
|
||||||
mangohud) install_mangohud ;;
|
gamemode) install_gamemode ;;
|
||||||
gamemode) install_gamemode ;;
|
goverlay) install_goverlay ;;
|
||||||
goverlay) install_goverlay ;;
|
lutris) install_lutris ;;
|
||||||
openrgb)
|
*) _run_install "$pkg" ;;
|
||||||
if [ "$DEBIAN_VERSION" = "11" ]; then
|
|
||||||
echo "OpenRGB requires Debian 12+."
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
install_openrgb
|
|
||||||
;;
|
|
||||||
lutris) install_lutris ;;
|
|
||||||
retroarch) install_retroarch ;;
|
|
||||||
*) _run_install "$pkg" ;;
|
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Selection confirmed once above; release the
|
|
||||||
# per-package prompt suppression.
|
|
||||||
_SELECTION_CONFIRMED=0
|
|
||||||
|
|
||||||
echo -e "${GREEN}Gaming setup complete.${NC}"
|
echo -e "${GREEN}Gaming setup complete.${NC}"
|
||||||
_pause
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,123 +2,66 @@
|
|||||||
# Shared helpers for gaming submodules
|
# Shared helpers for gaming submodules
|
||||||
|
|
||||||
_install_mesa_32bit() {
|
_install_mesa_32bit() {
|
||||||
local base_pkgs=(
|
local mesa_pkgs=(
|
||||||
"mesa-vulkan-drivers"
|
"mesa-vulkan-drivers" "mesa-vulkan-drivers:i386"
|
||||||
"libgl1-mesa-dri"
|
"libgl1-mesa-dri" "libgl1-mesa-dri:i386"
|
||||||
"libglx-mesa0"
|
"libglx-mesa0" "libglx-mesa0:i386"
|
||||||
"libegl-mesa0"
|
"libegl-mesa0" "libegl-mesa0:i386"
|
||||||
"mesa-libgallium"
|
"mesa-va-drivers" "mesa-va-drivers:i386"
|
||||||
)
|
)
|
||||||
|
|
||||||
local i386_active=false
|
local ref_ver
|
||||||
dpkg --print-foreign-architectures 2>/dev/null | grep -q i386 && i386_active=true
|
ref_ver=$(apt-cache policy mesa-vulkan-drivers:i386 2>/dev/null | awk 'NR==3 {print $2; exit}')
|
||||||
|
local ref_bpo_ver
|
||||||
|
ref_bpo_ver=$(apt-cache madison mesa-vulkan-drivers:i386 2>/dev/null | \
|
||||||
|
grep "${DEBIAN_CODENAME}-backports" | awk '{print $3}' | head -1)
|
||||||
|
local comp_line="Components: Vulkan:i386, OpenGL:i386, GLX:i386, EGL:i386, VA-API:i386"
|
||||||
|
|
||||||
local install_list=()
|
if [ "$(is_backports_enabled)" == "true" ] && [ -n "$ref_bpo_ver" ]; then
|
||||||
|
local bpo_pkgs=()
|
||||||
|
local stable_pkgs=()
|
||||||
|
|
||||||
for p in "${base_pkgs[@]}"; do
|
for mpkg in "${mesa_pkgs[@]}"; do
|
||||||
if apt-cache show "$p" >/dev/null 2>&1; then
|
local bpo_ver
|
||||||
install_list+=("$p")
|
bpo_ver=$(apt-cache madison "$mpkg" 2>/dev/null | \
|
||||||
fi
|
grep "${DEBIAN_CODENAME}-backports" | awk '{print $3}' | head -1)
|
||||||
if $i386_active && apt-cache show "${p}:i386" >/dev/null 2>&1; then
|
if [ -n "$bpo_ver" ]; then
|
||||||
install_list+=("${p}:i386")
|
bpo_pkgs+=("$mpkg")
|
||||||
fi
|
else
|
||||||
done
|
stable_pkgs+=("$mpkg")
|
||||||
|
|
||||||
if [ ${#install_list[@]} -eq 0 ]; then
|
|
||||||
echo "No Mesa 32-bit packages available for installation."
|
|
||||||
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
|
||||||
fi
|
done
|
||||||
echo "Mesa 32-bit libraries already installed."
|
|
||||||
return
|
local src_label="Debian ${DEBIAN_CODENAME^}-Backports"
|
||||||
fi
|
[ ${#stable_pkgs[@]} -gt 0 ] && src_label+=" + Stable"
|
||||||
|
|
||||||
|
local msg="Mesa 32-bit drivers required for gaming.\n\n"
|
||||||
|
msg+="Source: ${src_label}\n"
|
||||||
|
msg+="Mesa ${ref_bpo_ver:-$ref_ver}\n"
|
||||||
|
msg+="${comp_line}\n\n"
|
||||||
|
[ ${#stable_pkgs[@]} -gt 0 ] && msg+="Some packages only available in stable.\n"
|
||||||
|
msg+="Choose version:"
|
||||||
|
|
||||||
# ── 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
|
if _confirm_custom "Mesa 32-bit" "$msg" "Backports" "Stable" 14 70; then
|
||||||
apt_target="-t ${DEBIAN_CODENAME}-backports"
|
_run_cmd "Mesa 32-bit" "sudo apt install -y -t ${DEBIAN_CODENAME}-backports ${bpo_pkgs[*]}" \
|
||||||
|
"Installing 32-bit Mesa from backports..."
|
||||||
|
if [ ${#stable_pkgs[@]} -gt 0 ]; then
|
||||||
|
_run_cmd "Mesa 32-bit" "sudo apt install -y ${stable_pkgs[*]}" \
|
||||||
|
"Installing remaining 32-bit Mesa from stable..."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_run_cmd "Mesa 32-bit" "sudo apt install -y ${mesa_pkgs[*]}" \
|
||||||
|
"Installing 32-bit Mesa from stable..."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
local msg="Mesa 32-bit drivers required for gaming.\n\n"
|
||||||
|
msg+="Source: Debian Stable\n"
|
||||||
|
msg+="Mesa ${ref_ver}\n"
|
||||||
|
msg+="${comp_line}\n\n"
|
||||||
|
msg+="Install Mesa 32-bit drivers?"
|
||||||
|
if _confirm "Mesa 32-bit" "$msg" 14 70; then
|
||||||
|
_run_cmd "Mesa 32-bit" "sudo apt install -y ${mesa_pkgs[*]}" \
|
||||||
|
"Installing 32-bit Mesa..."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_run_cmd "Mesa 32-bit" "sudo apt install -y $apt_target ${install_list[*]}" \
|
|
||||||
"Installing Mesa drivers (${#install_list[@]} packages)..."
|
|
||||||
echo -e "${GREEN}Mesa 32-bit libraries installed.${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
apt_cache_exists() {
|
|
||||||
apt-cache show "$1" >/dev/null 2>&1
|
|
||||||
}
|
|
||||||
|
|
||||||
_detect_installed_nvidia_driver() {
|
|
||||||
dpkg -l 2>/dev/null | awk '
|
|
||||||
/^ii/ && ($2=="nvidia-driver" || $2=="nvidia-open" ||
|
|
||||||
$2 ~ /^nvidia-legacy-[0-9]+xx-driver$/ ||
|
|
||||||
$2 ~ /^nvidia-tesla-[0-9]+-driver$/) {print $2; exit}'
|
|
||||||
}
|
|
||||||
|
|
||||||
_nvidia_libs_pkg() {
|
|
||||||
case "$1" in
|
|
||||||
nvidia-driver|nvidia-open) echo "nvidia-driver-libs" ;;
|
|
||||||
*) echo "${1}-libs" ;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
_install_nvidia_32bit() {
|
|
||||||
local drv_pkg
|
|
||||||
drv_pkg=$(_detect_installed_nvidia_driver)
|
|
||||||
if [ -z "$drv_pkg" ]; then
|
|
||||||
_msg "NVIDIA 32-bit" \
|
|
||||||
"No NVIDIA driver detected.\n\nRun Graphics Drivers (option 5) first,\nthen return to add 32-bit libraries." 10 60
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
local drv_ver
|
|
||||||
drv_ver=$(dpkg -s "$drv_pkg" 2>/dev/null | sed -n 's/^Version: //p')
|
|
||||||
local libs_pkg
|
|
||||||
libs_pkg=$(_nvidia_libs_pkg "$drv_pkg")
|
|
||||||
|
|
||||||
if ! apt_cache_exists "${libs_pkg}:i386"; then
|
|
||||||
_msg "NVIDIA 32-bit" \
|
|
||||||
"No ${libs_pkg}:i386 available for your driver (${drv_pkg}).\nSkipping 32-bit NVIDIA libraries." 10 60
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if _confirm "NVIDIA 32-bit" \
|
|
||||||
"Driver: ${drv_pkg} ${drv_ver}\n\nInstall ${libs_pkg}:i386 (same version to prevent ABI mismatch)." 12 70; then
|
|
||||||
_run_cmd "NVIDIA 32-bit" "sudo apt install -y ${libs_pkg}:i386=${drv_ver}" \
|
|
||||||
"Installing 32-bit NVIDIA libraries (${drv_ver})..."
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,55 +2,18 @@
|
|||||||
# Heroic Games Launcher installation from GitHub releases
|
# Heroic Games Launcher installation from GitHub releases
|
||||||
|
|
||||||
install_heroic() {
|
install_heroic() {
|
||||||
local heroic_deb
|
local heroic_deb="/tmp/heroic.deb"
|
||||||
heroic_deb=$(mktemp "${TMPDIR:-/tmp}/heroic-XXXXXX.deb") || return 1
|
_run_cmd "Heroic" "sudo apt install -y curl wget" "Installing dependencies..."
|
||||||
# Remove the temp file on every exit path (RETURN
|
local gh_url
|
||||||
# traps are not inherited by called functions).
|
gh_url=$(curl -s --connect-timeout 10 https://api.github.com/repos/Heroic-Games-Launcher/\
|
||||||
trap 'rm -f "$heroic_deb"' RETURN
|
HeroicGamesLauncher/releases/latest | \
|
||||||
local ua="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
grep -oP 'https://[^"]+amd64\.deb' | head -1)
|
||||||
|
if [ -z "$gh_url" ]; then
|
||||||
_run_cmd "Heroic" "sudo apt install -y curl jq" "Installing dependencies..."
|
echo -e "${RED}Could not determine latest Heroic release.${NC}"
|
||||||
|
else
|
||||||
local json
|
_run_cmd "Heroic" "wget -O $heroic_deb $gh_url" "Downloading Heroic..."
|
||||||
# -f: HTTP >= 400 must fail; --max-time bounds the
|
_run_cmd "Heroic" "sudo apt install -y $heroic_deb" "Installing Heroic..."
|
||||||
# whole transfer (connect-timeout only covers the
|
|
||||||
# connection phase).
|
|
||||||
json=$(curl -fsS --connect-timeout 10 --max-time 30 -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
|
|
||||||
}
|
|
||||||
|
|
||||||
local deb_url
|
|
||||||
deb_url=$(echo "$json" | jq -r '
|
|
||||||
.assets[] | select(.name | endswith("amd64.deb")) | .browser_download_url
|
|
||||||
' 2>/dev/null || true)
|
|
||||||
|
|
||||||
if [ -z "$deb_url" ]; then
|
|
||||||
_msg "Heroic Error" "Could not find amd64.deb asset in latest release." 10 60
|
|
||||||
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 -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
|
|
||||||
_msg "Heroic Error" "Downloaded .deb is corrupted or truncated.\n\nRemoving file." 10 60
|
|
||||||
rm -f "$heroic_deb"
|
rm -f "$heroic_deb"
|
||||||
return 1
|
echo -e "${GREEN}Heroic Games Launcher installed.${NC}"
|
||||||
fi
|
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
|
|
||||||
rm -f "$heroic_deb"
|
|
||||||
echo -e "${GREEN}Heroic Games Launcher installed.${NC}"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
# Gaming performance tools installation
|
# Gaming performance tools installation
|
||||||
|
|
||||||
install_mangohud() {
|
install_mangohud() {
|
||||||
local pkgs="mangohud"
|
_run_cmd "MangoHud" "sudo apt install -y mangohud" "Installing MangoHud..."
|
||||||
if dpkg --print-foreign-architectures | grep -q i386; then
|
if dpkg --print-foreign-architectures | grep -q i386; then
|
||||||
pkgs+=" mangohud:i386"
|
echo "Installing 32-bit MangoHud..."
|
||||||
|
_run_cmd "MangoHud" "sudo apt install -y mangohud:i386" "Installing 32-bit MangoHud..."
|
||||||
fi
|
fi
|
||||||
_run_cmd "MangoHud" "sudo apt install -y $pkgs" "Installing MangoHud (64 + 32-bit)..."
|
|
||||||
}
|
}
|
||||||
|
|
||||||
install_gamemode() {
|
install_gamemode() {
|
||||||
@@ -18,125 +18,5 @@ install_goverlay() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
install_lutris() {
|
install_lutris() {
|
||||||
local pkgs="lutris wine64"
|
_run_install lutris
|
||||||
if dpkg --print-foreign-architectures 2>/dev/null | grep -q i386; then
|
|
||||||
pkgs+=" wine32"
|
|
||||||
fi
|
|
||||||
_run_cmd "Lutris" "sudo apt install -y $pkgs" "Installing Lutris + Wine..."
|
|
||||||
}
|
|
||||||
|
|
||||||
install_openrgb() {
|
|
||||||
local deb_suffix=""
|
|
||||||
case "$DEBIAN_CODENAME" in
|
|
||||||
bookworm) deb_suffix="bookworm" ;;
|
|
||||||
trixie) deb_suffix="trixie" ;;
|
|
||||||
*)
|
|
||||||
echo "OpenRGB requires Debian 12 (Bookworm) or 13 (Trixie)."
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
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 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 \
|
|
||||||
"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=""
|
|
||||||
while IFS=' ' read -r url hash; do
|
|
||||||
deb_url="$url"
|
|
||||||
sha256="$hash"
|
|
||||||
done < <(echo "$json" | jq -r '
|
|
||||||
.[0].assets[] | select(.name | contains("amd64_'"${deb_suffix}"'.deb")) |
|
|
||||||
"\(.browser_download_url) \(.sha256 // "")"
|
|
||||||
' 2>/dev/null)
|
|
||||||
|
|
||||||
if [ -z "$deb_url" ]; then
|
|
||||||
echo -e "${RED}Could not find OpenRGB .deb for ${DEBIAN_CODENAME^}.${NC}"
|
|
||||||
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..."
|
|
||||||
|
|
||||||
if [ -n "$sha256" ]; then
|
|
||||||
if ! echo "$sha256 $deb_path" | sha256sum -c --strict; then
|
|
||||||
echo -e "${RED}SHA256 mismatch! Downloaded file may be corrupted. Removing.${NC}"
|
|
||||||
rm -f "$deb_path"
|
|
||||||
return 1
|
|
||||||
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
|
|
||||||
echo -e "${RED}Downloaded .deb is corrupted. Removing.${NC}"
|
|
||||||
rm -f "$deb_path"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
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
|
|
||||||
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 udevadm control --reload-rules && sudo udevadm trigger
|
|
||||||
sudo setcap cap_sys_rawio=ep /usr/bin/openrgb 2>/dev/null || true
|
|
||||||
|
|
||||||
echo -e "${GREEN}OpenRGB installed. Reboot or log out/in for i2c group to take effect.${NC}"
|
|
||||||
_pause
|
|
||||||
}
|
|
||||||
|
|
||||||
install_retroarch() {
|
|
||||||
if is_installed "retroarch"; then
|
|
||||||
echo "RetroArch already installed."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
_run_cmd "RetroArch" "sudo apt install -y retroarch libretro-mgba libretro-snes9x libretro-nestopia libretro-gambatte" "Installing RetroArch and classic cores (GBA, SNES, NES, GB)..."
|
|
||||||
|
|
||||||
clear
|
|
||||||
echo "================================================================="
|
|
||||||
echo " 🎮 IMPORTANT RETROARCH NOTICE 🎮"
|
|
||||||
echo "================================================================="
|
|
||||||
echo "Good news! Nintendo (NES/SNES) and Game Boy (GB/GBA) cores"
|
|
||||||
echo "have been automatically installed and are ready to play!"
|
|
||||||
echo ""
|
|
||||||
echo "⚠️ However, due to Debian open-source guidelines:"
|
|
||||||
echo " - Core auto-updates inside the app are disabled."
|
|
||||||
echo " - Heavy/arcade cores or those requiring proprietary BIOS"
|
|
||||||
echo " (like PlayStation or Arcade) must be handled manually."
|
|
||||||
echo ""
|
|
||||||
echo "👉 To learn how to unlock the internal Online Downloader:"
|
|
||||||
echo " Please check our repository's documentation or visit:"
|
|
||||||
echo " https://wiki.debian.org/RetroArch"
|
|
||||||
echo "================================================================="
|
|
||||||
_pause
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,40 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Graphics Stack — sources submodules, provides AMD/Intel and NVIDIA top-level functions
|
# Graphics Stack — sources submodules and provides install_gpu_drivers()
|
||||||
|
|
||||||
_GPU_DIR="${MODULES_DIR}/gpu"
|
_GPU_DIR="${MODULES_DIR}/gpu"
|
||||||
source "${_GPU_DIR}/_helpers.sh"
|
source "${_GPU_DIR}/_helpers.sh"
|
||||||
source "${_GPU_DIR}/amd_intel.sh"
|
source "${_GPU_DIR}/amd_intel.sh"
|
||||||
source "${_GPU_DIR}/nvidia.sh"
|
source "${_GPU_DIR}/nvidia.sh"
|
||||||
source "${_GPU_DIR}/nvidia_manage.sh"
|
|
||||||
|
|
||||||
# Consumed by gaming.sh to know which NVIDIA driver path was taken
|
# Consumed by gaming.sh to know which NVIDIA driver path was taken
|
||||||
NVIDIA_DRIVER_MODE=""
|
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
|
install_gpu_drivers() {
|
||||||
# (GDM3 + NVIDIA + Wayland → black screen). Only informs, never
|
# Step 1: Info banner
|
||||||
# changes GDM3 configuration nor aborts installation.
|
local info="This section installs the latest Mesa graphics stack\n"
|
||||||
_warn_nvidia_gnome_wayland() {
|
info+="from Debian backports (or stable), plus GPU firmware\n"
|
||||||
command -v gdm3 &>/dev/null || return 0
|
info+="and monitoring tools tailored to your hardware.\n\n"
|
||||||
gnome-shell --version 2>/dev/null | grep -qi "shell 4[0-9]" || return 0
|
info+="Components:\n"
|
||||||
case "$NVIDIA_DRIVER_MODE" in
|
info+=" Mesa (OpenGL / Vulkan / VA-API)\n"
|
||||||
stable | backports | cuda-repo | extrepo) ;;
|
if [ "$GPU_TYPE" != "unknown" ]; then
|
||||||
*) return 0 ;;
|
info+=" GPU firmware for ${GPU_DESC}\n"
|
||||||
esac
|
fi
|
||||||
echo -e "${YELLOW}WARNING: Debian bug #1109409 may affect GDM3 + NVIDIA + Wayland.${NC}"
|
info+=" Monitoring tools (nvtop, vainfo, ...)"
|
||||||
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() {
|
if ! _confirm "Graphics Stack" "$info"; then
|
||||||
# Hardware state is detected on demand; GPU_TYPE and
|
echo "Skipping Graphics Stack."
|
||||||
# LSPCI_OUTPUT must be populated before planning.
|
return
|
||||||
_ensure_state_detected
|
fi
|
||||||
|
|
||||||
|
# Step 2: GPU detected?
|
||||||
if [ "$GPU_TYPE" = "unknown" ] || [ -z "$GPU_TYPE" ]; then
|
if [ "$GPU_TYPE" = "unknown" ] || [ -z "$GPU_TYPE" ]; then
|
||||||
|
# --- BLOQUE B: No GPU / VM (inline, no _install_mesa_backports) ---
|
||||||
local mesa_pkgs=(mesa-vulkan-drivers libgl1-mesa-dri libglx-mesa0 libegl-mesa0 mesa-va-drivers)
|
local mesa_pkgs=(mesa-vulkan-drivers libgl1-mesa-dri libglx-mesa0 libegl-mesa0 mesa-va-drivers)
|
||||||
local ref_ver
|
local ref_ver
|
||||||
ref_ver=$(apt-cache policy mesa-vulkan-drivers 2>/dev/null | awk 'NR==3 {print $2; exit}')
|
ref_ver=$(apt-cache policy mesa-vulkan-drivers 2>/dev/null | awk 'NR==3 {print $2; exit}')
|
||||||
local ref_bpo_ver
|
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 || true)
|
grep "${DEBIAN_CODENAME}-backports" | awk '{print $3}' | head -1)
|
||||||
local comp_line="Components: Vulkan, OpenGL, GLX, EGL, VA-API (64-bit)"
|
local comp_line="Components: Vulkan, OpenGL, GLX, EGL, VA-API (64-bit)"
|
||||||
|
|
||||||
if [ -n "$ref_bpo_ver" ] && [ "$(is_backports_enabled)" == "true" ]; then
|
if [ -n "$ref_bpo_ver" ] && [ "$(is_backports_enabled)" == "true" ]; then
|
||||||
@@ -49,11 +45,7 @@ _install_amd_intel_stack() {
|
|||||||
header+="${comp_line}\n\n"
|
header+="${comp_line}\n\n"
|
||||||
header+="Choose version:"
|
header+="Choose version:"
|
||||||
if _confirm_custom "No GPU Detected" "$header" "Backports" "Stable" 14 70; then
|
if _confirm_custom "No GPU Detected" "$header" "Backports" "Stable" 14 70; then
|
||||||
local _bpo_list=()
|
_run_cmd "Mesa" "sudo apt install -y -t ${DEBIAN_CODENAME}-backports ${mesa_pkgs[*]}" \
|
||||||
for _p in "${mesa_pkgs[@]}"; do
|
|
||||||
[ "$_p" != "mesa-va-drivers" ] && _bpo_list+=("$_p")
|
|
||||||
done
|
|
||||||
_run_cmd "Mesa" "sudo apt install -y -t ${DEBIAN_CODENAME}-backports ${_bpo_list[*]}" \
|
|
||||||
"Installing Mesa from backports..."
|
"Installing Mesa from backports..."
|
||||||
else
|
else
|
||||||
_run_cmd "Mesa" "sudo apt install -y ${mesa_pkgs[*]}" \
|
_run_cmd "Mesa" "sudo apt install -y ${mesa_pkgs[*]}" \
|
||||||
@@ -71,264 +63,55 @@ _install_amd_intel_stack() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
offer_generic_tools
|
offer_generic_tools
|
||||||
echo -e "${GREEN}Graphics stack setup complete.${NC}"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
local plan="The script has detected your graphics hardware.\n\n"
|
|
||||||
plan+="Detected GPUs:\n"
|
|
||||||
local gpu_count=0
|
|
||||||
while IFS= read -r gpu_line; do
|
|
||||||
gpu_count=$((gpu_count + 1))
|
|
||||||
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)
|
|
||||||
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"
|
|
||||||
plan+=" [+] Intel firmware + ${_va}\n"
|
|
||||||
fi
|
|
||||||
if $HAS_AMD; then
|
|
||||||
plan+=" [+] AMD firmware (firmware-amd-graphics)\n"
|
|
||||||
fi
|
|
||||||
plan+=" [+] Mesa (OpenGL/Vulkan/VA-API)\n"
|
|
||||||
|
|
||||||
_msg "Graphics Stack — Plan" "$plan" 16 70
|
|
||||||
|
|
||||||
if ! _confirm "Graphics Stack" "Install the planned components?"; then
|
|
||||||
echo "Skipping Graphics Stack."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
if $HAS_INTEL; then
|
|
||||||
install_intel_firmware
|
|
||||||
fi
|
|
||||||
if $HAS_AMD; then
|
|
||||||
install_amd_firmware
|
|
||||||
fi
|
|
||||||
|
|
||||||
if $HAS_AMD_LEGACY_GCN; then
|
|
||||||
local msg="An AMD GCN 1.0/GCN 1.1 GPU has been detected.\n\n"
|
|
||||||
msg+="These old GPUs use the legacy 'radeon' driver by default,\n"
|
|
||||||
msg+="but the modern 'amdgpu' driver offers better performance\n"
|
|
||||||
msg+="and Mesa support.\n\n"
|
|
||||||
msg+="Would you like to FORCE the amdgpu driver?\n"
|
|
||||||
msg+="(adds radeon.si_support=0 radeon.cik_support=0\n"
|
|
||||||
msg+=" amdgpu.si_support=1 amdgpu.cik_support=1 to GRUB)"
|
|
||||||
if _confirm "AMD Legacy GCN" "$msg" 16 72; then
|
|
||||||
_apply_amd_gcn_grub_fix
|
|
||||||
else
|
|
||||||
echo "Skipping amdgpu migration."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
_install_mesa_backports
|
|
||||||
|
|
||||||
local mesa_ver
|
|
||||||
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}"
|
|
||||||
|
|
||||||
if $HAS_INTEL; then
|
|
||||||
offer_intel_tools
|
|
||||||
fi
|
|
||||||
if $HAS_AMD; then
|
|
||||||
offer_amd_tools
|
|
||||||
fi
|
|
||||||
if ! $HAS_INTEL && ! $HAS_AMD; then
|
|
||||||
offer_generic_tools
|
|
||||||
fi
|
|
||||||
|
|
||||||
local summary="Mesa: ${GPU_VERSION:-not available}\n"
|
|
||||||
summary+="Firmware: installed for detected GPUs\n"
|
|
||||||
summary+="Tools: installed per vendor"
|
|
||||||
_msg "Graphics Stack — Complete" "$summary" 12 65
|
|
||||||
}
|
|
||||||
|
|
||||||
_apply_amd_gcn_grub_fix() {
|
|
||||||
local file="/etc/default/grub"
|
|
||||||
local backup
|
|
||||||
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
|
|
||||||
_msg "AMD GCN" "amdgpu parameters already present in GRUB.\nNo changes made." 8 50
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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
|
|
||||||
_msg "AMD GCN — Complete" "amdgpu parameters added to GRUB.\n\nBackup: ${backup}\n\nReboot to apply."
|
|
||||||
else
|
|
||||||
echo -e "${RED}update-grub failed. Restoring backup...${NC}"
|
|
||||||
sudo cp "$backup" "$file"
|
|
||||||
_msg "Error" "update-grub failed.\nBackup restored."
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
_msg "AMD GCN" "Parameters written to ${file}.\nRun 'sudo update-grub' manually."
|
# --- BLOQUE A: GPU detectada (AMD / Intel / NVIDIA) ---
|
||||||
fi
|
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)
|
||||||
|
local comp_line="Components: Vulkan, OpenGL, GLX, EGL, VA-API (64-bit)"
|
||||||
|
|
||||||
_install_nvidia_stack() {
|
local src_line="Source: Debian Stable"
|
||||||
# Hardware state is detected on demand; HAS_NVIDIA and
|
[ -n "$ref_bpo_ver" ] && [ "$(is_backports_enabled)" == "true" ] && src_line="Source: Debian ${DEBIAN_CODENAME^}-Backports"
|
||||||
# LSPCI_OUTPUT must be populated before planning.
|
|
||||||
_ensure_state_detected
|
|
||||||
|
|
||||||
if ! $HAS_NVIDIA; then
|
local plan="GPU detected: ${GPU_DESC}\n\n"
|
||||||
_msg "NVIDIA Not Found" "No NVIDIA GPU was detected.\n\nPlease check your hardware and try again." 10 60
|
plan+="${src_line}\n"
|
||||||
return
|
plan+="Mesa ${ref_bpo_ver:-$ref_ver}\n"
|
||||||
fi
|
plan+="${comp_line}\n"
|
||||||
|
|
||||||
local plan="The script has detected your NVIDIA GPU.\n\n"
|
case "$GPU_TYPE" in
|
||||||
plan+="Detected GPUs:\n"
|
amd) plan+="[+] Firmware: firmware-amd-graphics\n" ;;
|
||||||
local gpu_count=0
|
intel)
|
||||||
while IFS= read -r gpu_line; do
|
local _gen; _gen=$(get_intel_generation)
|
||||||
gpu_count=$((gpu_count + 1))
|
local _va; [ "$_gen" = "gen7-" ] && _va="i965-va-driver-shaders" || _va="intel-media-va-driver-non-free"
|
||||||
local desc
|
plan+="[+] Firmware: firmware-intel-graphics + ${_va}\n"
|
||||||
desc=$(echo "$gpu_line" | sed -E 's/.*: //; s/ *\(rev.*//')
|
;;
|
||||||
plan+=" GPU ${gpu_count}: ${desc}\n"
|
nvidia) plan+="[+] NVIDIA driver (details in next step)\n" ;;
|
||||||
done < <(echo "$LSPCI_OUTPUT" | grep -E "VGA|3D" || true)
|
esac
|
||||||
plan+="\nPlanned:\n [+] NVIDIA proprietary driver"
|
|
||||||
|
|
||||||
_msg "NVIDIA Stack — Plan" "$plan" 14 65
|
if ! _confirm "Graphics Stack — ${GPU_DESC}" "$plan" 14 70; then
|
||||||
_pause "Press Enter to continue..."
|
echo "Skipping Graphics Stack."
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
else
|
|
||||||
if ! _show_nvidia_version_menu; then
|
|
||||||
echo "Skipping NVIDIA driver installation."
|
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local nv_arch=""
|
# 3. _run_cmd (via vendor functions — each with its own pkg_versions + _confirm)
|
||||||
case "$NVIDIA_SELECTED_VERSION" in
|
case "$GPU_TYPE" in
|
||||||
470)
|
amd) install_amd_firmware ;;
|
||||||
# Legacy 470 (Kepler/Tesla) — forced, regardless of auto-detection
|
intel) install_intel_firmware ;;
|
||||||
_install_nvidia_bookworm_kepler
|
nvidia) install_nvidia_driver ;;
|
||||||
;;
|
esac
|
||||||
535)
|
|
||||||
if [ "$(is_nvidia_kepler)" = "true" ]; then
|
# Mesa (backports / stable)
|
||||||
_install_nvidia_bookworm_kepler
|
_install_mesa_backports
|
||||||
elif [ "$(is_nvidia_fermi)" = "true" ]; then
|
|
||||||
_msg "NVIDIA Fermi — Bookworm" \
|
# Vendor-specific tools
|
||||||
"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."
|
case "$GPU_TYPE" in
|
||||||
NVIDIA_DRIVER_MODE=""
|
amd) offer_amd_tools ;;
|
||||||
else
|
intel) offer_intel_tools ;;
|
||||||
_install_nvidia_standard
|
nvidia) offer_generic_tools ;;
|
||||||
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\
|
|
||||||
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=""
|
|
||||||
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
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
_install_nvidia_standard
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$NVIDIA_DRIVER_MODE" ]; then
|
echo -e "${GREEN}Graphics stack setup complete.${NC}"
|
||||||
_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
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,91 +1,69 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Shared helpers for GPU submodules
|
# Shared helpers for GPU submodules
|
||||||
|
|
||||||
declare -A NVIDIA_FAMILY_MAP=(
|
is_nvidia_kepler() {
|
||||||
# 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"
|
|
||||||
)
|
|
||||||
|
|
||||||
detect_nvidia_arch() {
|
|
||||||
[ -z "$1" ] && {
|
|
||||||
echo "unknown"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
local pci_id="${1^^}"
|
|
||||||
local prefix="${pci_id:0:2}"
|
|
||||||
echo "${NVIDIA_FAMILY_MAP[$prefix]:-unknown}"
|
|
||||||
}
|
|
||||||
|
|
||||||
_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
|
|
||||||
}
|
|
||||||
|
|
||||||
# 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_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_amd_legacy_gcn() {
|
|
||||||
local dev_id
|
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)
|
dev_id=$(lspci -nn | grep -iE "VGA|3D" | grep -i nvidia | grep -oP '10de:\K[0-9a-fA-F]+' | head -n1)
|
||||||
[ -z "$dev_id" ] && {
|
[ -z "$dev_id" ] && { echo false; return; }
|
||||||
echo false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
local legacy_ids
|
local dev_int
|
||||||
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"
|
dev_int=$((16#${dev_id,,}))
|
||||||
|
|
||||||
if echo "$dev_id" | grep -qiE "^(${legacy_ids})$"; then
|
# Known Kepler device ID ranges (GKxxx)
|
||||||
echo true
|
if [ "$dev_int" -ge $((16#0FC0)) ] && [ "$dev_int" -le $((16#0FCF)) ]; then echo true; return; fi
|
||||||
else
|
if [ "$dev_int" -ge $((16#1180)) ] && [ "$dev_int" -le $((16#118F)) ]; then echo true; return; fi
|
||||||
echo false
|
if [ "$dev_int" -ge $((16#11C0)) ] && [ "$dev_int" -le $((16#11CF)) ]; then echo true; return; fi
|
||||||
fi
|
if [ "$dev_int" -ge $((16#0FE0)) ] && [ "$dev_int" -le $((16#0FEF)) ]; then echo true; return; fi
|
||||||
|
if [ "$dev_int" -ge $((16#1280)) ] && [ "$dev_int" -le $((16#129F)) ]; then echo true; return; fi
|
||||||
|
if [ "$dev_int" -ge $((16#0EB0)) ] && [ "$dev_int" -le $((16#0EBF)) ]; then echo true; return; fi
|
||||||
|
|
||||||
|
echo false
|
||||||
|
}
|
||||||
|
|
||||||
|
is_nvidia_maxwell() {
|
||||||
|
local dev_id
|
||||||
|
dev_id=$(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 (GT 830M, GT 840M, GT 940M)
|
||||||
|
if [ "$dev_int" -ge $((16#1340)) ] && [ "$dev_int" -le $((16#134F)) ]; then echo true; return; fi
|
||||||
|
# GM107 (GTX 750 Ti, GTX 860M)
|
||||||
|
if [ "$dev_int" -ge $((16#1380)) ] && [ "$dev_int" -le $((16#138F)) ]; then echo true; return; fi
|
||||||
|
# GM200 (TITAN X, GTX 980 Ti)
|
||||||
|
if [ "$dev_int" -ge $((16#13C0)) ] && [ "$dev_int" -le $((16#13CF)) ]; then echo true; return; fi
|
||||||
|
# GM204 (GTX 980, GTX 970)
|
||||||
|
if [ "$dev_int" -ge $((16#13D0)) ] && [ "$dev_int" -le $((16#13DF)) ]; then echo true; return; fi
|
||||||
|
# GM206 (GTX 960, GTX 950)
|
||||||
|
if [ "$dev_int" -ge $((16#1480)) ] && [ "$dev_int" -le $((16#148F)) ]; then echo true; return; fi
|
||||||
|
|
||||||
|
echo false
|
||||||
|
}
|
||||||
|
|
||||||
|
is_nvidia_pascal() {
|
||||||
|
local dev_id
|
||||||
|
dev_id=$(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
|
||||||
|
# GP104 (GTX 1080, GTX 1070)
|
||||||
|
if [ "$dev_int" -ge $((16#1B00)) ] && [ "$dev_int" -le $((16#1B1F)) ]; then echo true; return; fi
|
||||||
|
# GP102 (TITAN Xp, GTX 1080 Ti)
|
||||||
|
if [ "$dev_int" -ge $((16#1B80)) ] && [ "$dev_int" -le $((16#1B8F)) ]; then echo true; return; fi
|
||||||
|
# GP106 (GTX 1060)
|
||||||
|
if [ "$dev_int" -ge $((16#1C00)) ] && [ "$dev_int" -le $((16#1C2F)) ]; then echo true; return; fi
|
||||||
|
# GP107 (GTX 1050 Ti, GTX 1050)
|
||||||
|
if [ "$dev_int" -ge $((16#1C80)) ] && [ "$dev_int" -le $((16#1C8F)) ]; then echo true; return; fi
|
||||||
|
# GP108 (GT 1030, GT 1010)
|
||||||
|
if [ "$dev_int" -ge $((16#1D00)) ] && [ "$dev_int" -le $((16#1DFF)) ]; then echo true; return; fi
|
||||||
|
|
||||||
|
echo false
|
||||||
}
|
}
|
||||||
|
|
||||||
_install_mesa_backports() {
|
_install_mesa_backports() {
|
||||||
@@ -100,7 +78,7 @@ _install_mesa_backports() {
|
|||||||
|
|
||||||
for mpkg in "${mesa_pkgs[@]}"; do
|
for mpkg in "${mesa_pkgs[@]}"; do
|
||||||
local bpo_ver
|
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)
|
grep "${DEBIAN_CODENAME}-backports" | awk '{print $3}' | head -1)
|
||||||
if [ -n "$bpo_ver" ]; then
|
if [ -n "$bpo_ver" ]; then
|
||||||
bpo_pkgs+=("$mpkg")
|
bpo_pkgs+=("$mpkg")
|
||||||
@@ -109,18 +87,10 @@ _install_mesa_backports() {
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Mesa >= 25.3.3 unified VA-API into mesa-libgallium.
|
|
||||||
# mesa-va-drivers from backports breaks with the new mesa-libgallium.
|
|
||||||
local _bpo_filtered=()
|
|
||||||
for _p in "${bpo_pkgs[@]}"; do
|
|
||||||
[ "$_p" != "mesa-va-drivers" ] && _bpo_filtered+=("$_p")
|
|
||||||
done
|
|
||||||
bpo_pkgs=("${_bpo_filtered[@]}")
|
|
||||||
|
|
||||||
local ref_ver
|
local ref_ver
|
||||||
ref_ver=$(apt-cache policy mesa-vulkan-drivers 2>/dev/null | awk 'NR==3 {print $2; exit}')
|
ref_ver=$(apt-cache policy mesa-vulkan-drivers 2>/dev/null | awk 'NR==3 {print $2; exit}')
|
||||||
local ref_bpo_ver
|
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)
|
grep "${DEBIAN_CODENAME}-backports" | awk '{print $3}' | head -1)
|
||||||
local comp_line="Components: Vulkan, OpenGL, GLX, EGL, VA-API (64-bit)"
|
local comp_line="Components: Vulkan, OpenGL, GLX, EGL, VA-API (64-bit)"
|
||||||
|
|
||||||
@@ -163,42 +133,12 @@ install_mesa_stable() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
offer_generic_tools() {
|
offer_generic_tools() {
|
||||||
if [ "$DEBIAN_VERSION" = "11" ]; then
|
|
||||||
if ! lsmod 2>/dev/null | grep -q "^nvidia "; then
|
|
||||||
echo "nvtop skipped on Bullseye — NVIDIA driver not loaded."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
local tool_pkgs
|
local tool_pkgs
|
||||||
tool_pkgs=$(pkg_versions nvtop vainfo)
|
tool_pkgs=$(pkg_versions nvtop vainfo)
|
||||||
if _confirm "GPU Tools" "Install monitoring and info tools?\n\n${tool_pkgs}"; then
|
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
|
_run_cmd "GPU Tools" "sudo apt install -y nvtop vainfo" "Installing GPU tools..."
|
||||||
if command -v vainfo &>/dev/null; then
|
vainfo
|
||||||
vainfo
|
|
||||||
_pause "vainfo output shown above."
|
|
||||||
else
|
|
||||||
echo -e "${YELLOW}vainfo not available, skipping report.${NC}"
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
echo "Skipping GPU monitoring tools."
|
echo "Skipping GPU monitoring tools."
|
||||||
fi
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,51 +5,45 @@ install_amd_firmware() {
|
|||||||
local fw_info
|
local fw_info
|
||||||
fw_info=$(pkg_versions firmware-amd-graphics)
|
fw_info=$(pkg_versions firmware-amd-graphics)
|
||||||
if _confirm "AMD Firmware" "Install AMD GPU firmware?\n\n${fw_info}"; then
|
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
|
_run_cmd "AMD" "sudo apt install -y firmware-amd-graphics" "Installing AMD GPU firmware..."
|
||||||
_msg_red "AMD Firmware" "Failed to install AMD GPU firmware."
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
offer_amd_tools() {
|
offer_amd_tools() {
|
||||||
local amd_tools=("radeontop")
|
local amd_tools=("radeontop")
|
||||||
|
local corectrl_available=false
|
||||||
|
|
||||||
local pkgs
|
if [ "$DEBIAN_CODENAME" = "trixie" ]; then
|
||||||
if [ "$DEBIAN_VERSION" = "11" ]; then
|
corectrl_available=true
|
||||||
pkgs=$(pkg_versions "${amd_tools[@]}" vainfo)
|
elif [ "$(is_backports_enabled)" = "true" ] && apt-cache madison corectrl 2>/dev/null | grep -q "bookworm-backports"; then
|
||||||
else
|
corectrl_available=true
|
||||||
pkgs=$(pkg_versions "${amd_tools[@]}" nvtop vainfo)
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! _confirm "AMD Tools" "Install AMD monitoring tools?\n\n${pkgs}"; then
|
local pkgs
|
||||||
|
pkgs=$(pkg_versions "${amd_tools[@]}" nvtop vainfo)
|
||||||
|
if $corectrl_available; then
|
||||||
|
local ctrl_ver
|
||||||
|
ctrl_ver=$(apt-cache policy corectrl 2>/dev/null | awk 'NR==3 {print $2; exit}')
|
||||||
|
pkgs+=" - corectrl ${ctrl_ver}\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _confirm "AMD Tools" "Install AMD monitoring and control tools?\n\n${pkgs}"; then
|
||||||
echo "Skipping AMD tools."
|
echo "Skipping AMD tools."
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local tools_failed=false
|
_run_cmd "AMD Tools" "sudo apt install -y ${amd_tools[*]} nvtop vainfo" "Installing AMD tools..."
|
||||||
if [ "$DEBIAN_VERSION" = "11" ]; then
|
vainfo
|
||||||
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."
|
if $corectrl_available; then
|
||||||
tools_failed=true
|
if [ "$DEBIAN_CODENAME" = "trixie" ]; then
|
||||||
|
_run_cmd "corectrl" "sudo apt install -y corectrl" "Installing corectrl..."
|
||||||
|
else
|
||||||
|
_run_cmd "corectrl" "sudo apt install -y -t ${DEBIAN_CODENAME}-backports corectrl" "Installing corectrl from backports..."
|
||||||
fi
|
fi
|
||||||
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}"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if $tools_failed; then
|
echo -e "${GREEN}AMD tools installed.${NC}"
|
||||||
echo -e "${RED}AMD tools installation failed.${NC}"
|
|
||||||
else
|
|
||||||
echo -e "${GREEN}AMD tools installed.${NC}"
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
install_intel_firmware() {
|
install_intel_firmware() {
|
||||||
@@ -65,9 +59,7 @@ install_intel_firmware() {
|
|||||||
local fw_info
|
local fw_info
|
||||||
fw_info=$(pkg_versions firmware-intel-graphics "$va_driver")
|
fw_info=$(pkg_versions firmware-intel-graphics "$va_driver")
|
||||||
if _confirm "Intel Firmware" "Install Intel GPU firmware?\n\n${fw_info}"; then
|
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
|
_run_cmd "Intel" "sudo apt install -y firmware-intel-graphics $va_driver" "Installing Intel GPU firmware..."
|
||||||
_msg_red "Intel Firmware" "Failed to install Intel GPU firmware."
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,18 +73,7 @@ offer_intel_tools() {
|
|||||||
[ -d "/sys/bus/pci/drivers/xe" ] && has_xe=true
|
[ -d "/sys/bus/pci/drivers/xe" ] && has_xe=true
|
||||||
[ -d "/sys/bus/pci/drivers/i915" ] && has_i915=true
|
[ -d "/sys/bus/pci/drivers/i915" ] && has_i915=true
|
||||||
|
|
||||||
if [ "$DEBIAN_VERSION" = "11" ]; then
|
if $has_xe; then
|
||||||
if $has_xe; then
|
|
||||||
echo "Intel Xe GPU detected. No monitoring tools available on Bullseye."
|
|
||||||
return
|
|
||||||
elif $has_i915; then
|
|
||||||
driver_info="Classic Intel GPU detected (i915 driver)."
|
|
||||||
pkg_list=("intel-gpu-tools")
|
|
||||||
else
|
|
||||||
echo "Intel GPU driver not identified. No monitoring tools available on Bullseye."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
elif $has_xe; then
|
|
||||||
driver_info="Modern Intel GPU detected (Xe driver).\nintel-gpu-tools is NOT compatible with Xe.\nOnly nvtop will be offered."
|
driver_info="Modern Intel GPU detected (Xe driver).\nintel-gpu-tools is NOT compatible with Xe.\nOnly nvtop will be offered."
|
||||||
pkg_list=("nvtop")
|
pkg_list=("nvtop")
|
||||||
elif $has_i915; then
|
elif $has_i915; then
|
||||||
@@ -106,20 +87,8 @@ offer_intel_tools() {
|
|||||||
pkg_info=$(pkg_versions "${pkg_list[@]}" vainfo)
|
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
|
if _confirm "Intel Tools" "Intel GPU monitoring tools\n\n${driver_info}\n\nPackages:\n${pkg_info}"; then
|
||||||
local intel_failed=false
|
_run_cmd "Intel Tools" "sudo apt install -y ${pkg_list[*]} vainfo" "Installing Intel monitoring tools..."
|
||||||
if ! _run_cmd "Intel Tools" "sudo apt install -y ${pkg_list[*]} vainfo" "Installing Intel monitoring tools..."; then
|
vainfo
|
||||||
_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
|
|
||||||
else
|
else
|
||||||
echo "Skipping Intel monitoring tools."
|
echo "Skipping Intel monitoring tools."
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,386 +1,276 @@
|
|||||||
#!/usr/bin/env bash
|
#!/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 A : Trixie + backports kernel → Official NVIDIA CUDA Repo (Pinned v590)
|
||||||
# CASE B : Kernel stable (any distro) → Debian stable
|
# CASE B : Bookworm + backports kernel → Debian backports (-t bookworm-backports)
|
||||||
#
|
# CASE C : Kernel stable (any distro) → Debian stable (optional backports)
|
||||||
# Note: Debian 12 (Bookworm) backports reached EOL (2026-08-09);
|
|
||||||
# the NVIDIA flow only uses the stable repo for Debian 12.
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------
|
install_nvidia_driver() {
|
||||||
# Shared helper: enable NVIDIA CUDA repo
|
echo -e "${YELLOW}NVIDIA GPU detected.${NC}"
|
||||||
# Debian 13 (Trixie): cuda-keyring (official method — broken extrepo)
|
NVIDIA_DRIVER_MODE=""
|
||||||
# Debian 12 (Bookworm): extrepo nvidia-cuda (works)
|
|
||||||
# -------------------------------------------------------------------
|
|
||||||
_enable_cuda_repo() {
|
|
||||||
_is_cuda_repo_ready && return 0
|
|
||||||
|
|
||||||
if [ "$DEBIAN_VERSION" = "13" ]; then
|
local is_bpo_kernel; is_bpo_kernel=$(is_backports_kernel)
|
||||||
# Official NVIDIA method: cuda-keyring (extrepo does not
|
local is_kepler; is_kepler=$(is_nvidia_kepler)
|
||||||
# configure the repo correctly on Trixie)
|
local is_maxwell; is_maxwell=$(is_nvidia_maxwell)
|
||||||
if dpkg -s cuda-keyring &>/dev/null; then
|
local is_pascal; is_pascal=$(is_nvidia_pascal)
|
||||||
return 0 # already installed → its .list already exists
|
|
||||||
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
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Debian 12 (y otros): extrepo nvidia-cuda
|
# ── Veto: Kepler en Trixie no tiene driver disponible ──
|
||||||
if ! command -v extrepo &>/dev/null; then
|
if [ "$is_kepler" = "true" ] && [ "$DEBIAN_CODENAME" = "trixie" ]; then
|
||||||
_run_cmd "extrepo" "sudo apt install -y extrepo" "Installing extrepo..." || return 1
|
_msg "NVIDIA Kepler" \
|
||||||
fi
|
"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
|
||||||
_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
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
dkms status 2>/dev/null | grep "^nvidia" || echo "(no nvidia DKMS module found)"
|
# ── Bloqueo: Maxwell/Pascal no son compatibles con v590 ──
|
||||||
|
if [ "$DEBIAN_CODENAME" = "trixie" ] && [ "$is_bpo_kernel" = "true" ]; then
|
||||||
if _nvidia_dkms_installed; then
|
if [ "$is_maxwell" = "true" ] || [ "$is_pascal" = "true" ]; then
|
||||||
_warn_secure_boot
|
local gpu_gen="Maxwell"
|
||||||
echo -e "${GREEN}DKMS module compiled for ${kernel}. Reboot required.${NC}"
|
[ "$is_pascal" = "true" ] && gpu_gen="Pascal"
|
||||||
echo "──────────────────────────────────────────────"
|
local block_msg="INCOMPATIBILITY DETECTED: Your NVIDIA ${gpu_gen} GPU\n"
|
||||||
return 0
|
block_msg+="is NOT supported by the modern v590 driver.\n\n"
|
||||||
fi
|
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"
|
||||||
echo -e "${YELLOW}DKMS module NOT compiled for ${kernel}. Repairing...${NC}"
|
block_msg+="standard STABLE Kernel.\n\n"
|
||||||
local pkg repaired=false
|
block_msg+="The script will automatically downgrade your path to\n"
|
||||||
for pkg in "$@"; do
|
block_msg+="Stable Kernel mode for NVIDIA."
|
||||||
if dpkg -l "$pkg" 2>/dev/null | grep -q "^ii"; then
|
_msg "NVIDIA — Trixie + Backports" "$block_msg" 14 70
|
||||||
_run_cmd "NVIDIA" "sudo dpkg-reconfigure $pkg" "Reconfiguring $pkg..."
|
is_bpo_kernel=false
|
||||||
repaired=true
|
|
||||||
break
|
|
||||||
fi
|
fi
|
||||||
done
|
|
||||||
if ! $repaired; then
|
|
||||||
echo -e "${RED}No DKMS package found to reconfigure.${NC}"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if _nvidia_dkms_installed; then
|
# ── Dispatch por casos ──
|
||||||
_warn_secure_boot
|
if [ "$DEBIAN_CODENAME" = "trixie" ] && [ "$is_bpo_kernel" = "true" ]; then
|
||||||
echo -e "${GREEN}DKMS module compiled successfully for ${kernel}. Reboot required.${NC}"
|
_install_nvidia_cuda_repo
|
||||||
echo "──────────────────────────────────────────────"
|
elif [ "$DEBIAN_CODENAME" = "bookworm" ] && [ "$is_bpo_kernel" = "true" ]; then
|
||||||
return 0
|
_install_nvidia_bookworm_bpo
|
||||||
else
|
else
|
||||||
echo -e "${RED}DKMS module still not compiled for ${kernel}.${NC}"
|
_install_nvidia_standard
|
||||||
echo "Check the build log manually:"
|
|
||||||
echo " /var/lib/dkms/nvidia*/.../build/make.log"
|
|
||||||
echo " dmesg | grep nvidia"
|
|
||||||
echo "──────────────────────────────────────────────"
|
|
||||||
return 1
|
|
||||||
fi
|
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}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
# CASE A: Trixie + Backports Kernel → Official CUDA Repo (Pinned v590)
|
# CASE A: Trixie + Backports Kernel → Official CUDA Repo (Pinned v590)
|
||||||
# -------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
_install_nvidia_cuda_repo() {
|
_install_nvidia_cuda_repo() {
|
||||||
local ver="${1:-590}"
|
local i386_active=false
|
||||||
local warn="WARNING: You are about to install NVIDIA v${ver} from\n"
|
dpkg --print-foreign-architectures | grep -q i386 && i386_active=true
|
||||||
warn+="the official NVIDIA CUDA repository.\n\n"
|
|
||||||
warn+="Source: Official NVIDIA CUDA Repo\n"
|
local warn="WARNING: Official Debian NVIDIA driver (v550)\n"
|
||||||
warn+="Driver: Production Branch v${ver} (unified metapackage)\n"
|
warn+="fails to compile on Trixie Backports Kernels.\n\n"
|
||||||
warn+="[+] nvidia-driver-pinning-${ver} (version lock)\n"
|
warn+="The script will configure the official NVIDIA CUDA\n"
|
||||||
warn+="[+] nvidia-open (driver + open kernel modules)\n\n"
|
warn+="repository and force-install the stable production\n"
|
||||||
|
warn+="branch v590.48.01 on your system.\n\n"
|
||||||
|
warn+="Source: Official NVIDIA CUDA Repo (Pinned v590.*)\n"
|
||||||
|
warn+="Driver: Production Branch 590.48.01 (Kernel 7.0+ Compliant)\n"
|
||||||
|
warn+="[+] Full 64-bit Core & Compute Stack (DKMS)\n"
|
||||||
|
if $i386_active; then
|
||||||
|
warn+="[+] 32-bit Gaming Multiarch Libraries\n"
|
||||||
|
fi
|
||||||
|
warn+="[+] APT Pinning + Package Hold will be applied\n\n"
|
||||||
warn+="Do you want to proceed at your own risk?"
|
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}"
|
echo -e "${YELLOW}NVIDIA installation aborted by user.${NC}"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Step 1: Enable CUDA repo (cuda-keyring Trixie / extrepo Bookworm)
|
# Step 1: Download & install CUDA keyring
|
||||||
if ! _enable_cuda_repo; then
|
_run_cmd "CUDA Keyring" \
|
||||||
_msg "CUDA Repo — Error" "Failed to enable the official NVIDIA CUDA repository.\n\nNo NVIDIA driver was installed." 10 60
|
"wget -q https://developer.download.nvidia.com/compute/cuda/repos/debian13/x86_64/cuda-keyring_1.1-1_all.deb -O /tmp/cuda-keyring.deb && sudo dpkg -i /tmp/cuda-keyring.deb && rm -f /tmp/cuda-keyring.deb" \
|
||||||
return 1
|
"Downloading and installing official CUDA keyring..."
|
||||||
|
|
||||||
|
# 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: Update package lists
|
||||||
|
_run_cmd "APT Update" "sudo apt update" "Updating package lists..."
|
||||||
|
|
||||||
|
# Step 4: Build version-locked package list
|
||||||
|
local pkgs=(
|
||||||
|
"cuda-drivers=590.48.01-1"
|
||||||
|
"libcuda1=590.48.01-1"
|
||||||
|
"nvidia-driver=590.48.01-1"
|
||||||
|
"nvidia-driver-libs=590.48.01-1"
|
||||||
|
"firmware-nvidia-gsp=590.48.01-1"
|
||||||
|
"libegl-nvidia0=590.48.01-1"
|
||||||
|
"libglx-nvidia0=590.48.01-1"
|
||||||
|
"libnvidia-eglcore=590.48.01-1"
|
||||||
|
"libnvidia-glcore=590.48.01-1"
|
||||||
|
"libnvidia-glvkspirv=590.48.01-1"
|
||||||
|
"libnvidia-ml1=590.48.01-1"
|
||||||
|
"nvidia-egl-icd=590.48.01-1"
|
||||||
|
"nvidia-vulkan-icd=590.48.01-1"
|
||||||
|
"libnvcuvid1=590.48.01-1"
|
||||||
|
"libnvidia-encode1=590.48.01-1"
|
||||||
|
"nvidia-kernel-dkms=590.48.01-1"
|
||||||
|
"nvidia-settings=590.48.01-1"
|
||||||
|
"nvidia-smi=590.48.01-1"
|
||||||
|
)
|
||||||
|
|
||||||
|
if $i386_active; then
|
||||||
|
pkgs+=(
|
||||||
|
"libcuda1:i386=590.48.01-1"
|
||||||
|
"nvidia-driver-libs:i386=590.48.01-1"
|
||||||
|
"libegl-nvidia0:i386=590.48.01-1"
|
||||||
|
"libglx-nvidia0:i386=590.48.01-1"
|
||||||
|
"libnvidia-eglcore:i386=590.48.01-1"
|
||||||
|
"libnvidia-glcore:i386=590.48.01-1"
|
||||||
|
"libnvidia-glvkspirv:i386=590.48.01-1"
|
||||||
|
"libnvidia-ml1:i386=590.48.01-1"
|
||||||
|
"nvidia-egl-icd:i386=590.48.01-1"
|
||||||
|
"nvidia-vulkan-icd:i386=590.48.01-1"
|
||||||
|
)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Step 2: explicit apt update — without an updated index the repo
|
_run_cmd "NVIDIA CUDA" "sudo apt install -y ${pkgs[*]}" \
|
||||||
# is invisible and apt would resolve the Debian candidate (v550)
|
"Installing NVIDIA Production Driver v590.48.01..."
|
||||||
# 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 3: Official pinning — INDEPENDENT and mandatory APT
|
# Step 5: Hold critical packages
|
||||||
# transaction. APT reads /etc/apt/preferences.d/ at startup, not
|
_run_cmd "Package Hold" \
|
||||||
# during the transaction: the pinning must be installed BEFORE
|
"sudo apt-mark hold cuda-drivers libcuda1 firmware-nvidia-gsp" \
|
||||||
# installing the driver. If the repo does not publish it, it is
|
"Locking v590 packages to prevent accidental upgrades..."
|
||||||
# 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
|
|
||||||
|
|
||||||
NVIDIA_DRIVER_MODE="cuda-repo"
|
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# -------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
# Bookworm Kepler interception — forces nvidia-legacy-470xx-driver
|
# CASE B: Bookworm + Backports Kernel → Debian backports
|
||||||
# -------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
_install_nvidia_bookworm_kepler() {
|
_install_nvidia_bookworm_bpo() {
|
||||||
local nv_pkg="nvidia-tesla-470-driver"
|
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
|
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}')
|
||||||
|
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"
|
||||||
|
|
||||||
echo -e "${YELLOW}Kepler GPU detected — forcing ${nv_pkg}.${NC}"
|
if ! _confirm "NVIDIA Driver — Backports" "$msg" 14 70; then
|
||||||
|
echo "Skipping NVIDIA driver installation."
|
||||||
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"
|
|
||||||
msg+=" ${nv_pkg} ${nv_ver:-unknown}\n"
|
|
||||||
msg+="to avoid black-screen failures.\n\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?"
|
|
||||||
|
|
||||||
if ! _confirm_custom "NVIDIA Kepler — Bookworm" "$msg" "Install" "Skip" 14 70; then
|
|
||||||
echo "Skipping Kepler driver."
|
|
||||||
NVIDIA_DRIVER_MODE=""
|
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! _run_cmd "NVIDIA Kepler" \
|
_run_cmd "NVIDIA" "sudo apt install -y -t bookworm-backports $nv_pkg firmware-misc-nonfree nvidia-vaapi-driver" \
|
||||||
"sudo apt install -y linux-headers-amd64 $nv_pkg firmware-misc-nonfree nvidia-settings" \
|
"Installing NVIDIA driver from backports..."
|
||||||
"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
|
|
||||||
fi
|
|
||||||
|
|
||||||
NVIDIA_DRIVER_MODE="${NVIDIA_DRIVER_MODE:-stable}"
|
NVIDIA_DRIVER_MODE="backports"
|
||||||
echo -e "${GREEN}Kepler driver (${nv_pkg}) installed. Reboot required.${NC}"
|
echo -e "${GREEN}NVIDIA driver installed from backports. Reboot required.${NC}"
|
||||||
|
|
||||||
_verify_nvidia_dkms_build nvidia-tesla-470-kernel-dkms || true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# -------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
# CASE B: Kernel stable (any distro) → Debian stable
|
# CASE C: Kernel stable (any distro) → Debian stable, optional backports
|
||||||
# -------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
_install_nvidia_standard() {
|
_install_nvidia_standard() {
|
||||||
# --- 1. ARCHITECTURE → KERNEL MODULE ---
|
local nv_pkg=""
|
||||||
# Only Turing+ (known) uses the open module. Unknown/empty or
|
local use_bpo=false
|
||||||
# old architecture (Kepler/Fermi/Maxwell/Pascal/Volta) →
|
local is_kepler
|
||||||
# closed module as safe fallback.
|
is_kepler=$(is_nvidia_kepler)
|
||||||
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
|
|
||||||
|
|
||||||
# --- 2. PACKAGES — a single apt install ---
|
if [ "$is_kepler" = "true" ]; then
|
||||||
local extra_pkgs="linux-headers-amd64 nvidia-driver firmware-nvidia-gsp nvidia-vaapi-driver"
|
if [ "$DEBIAN_CODENAME" = "trixie" ]; then
|
||||||
[ "$DEBIAN_VERSION" = "12" ] && extra_pkgs+=" mesa-vdpau-drivers"
|
_msg "NVIDIA Kepler" \
|
||||||
local install_pkgs="$kernel_pkg $extra_pkgs"
|
"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 ---
|
# Check for backports (optional, if repo enabled)
|
||||||
local kernel_ver msg
|
local stable_nv_ver
|
||||||
kernel_ver=$(apt-cache policy "$kernel_pkg" 2>/dev/null | awk 'NR==3 {print $2; exit}') || true
|
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"
|
local src_label="Debian ${DEBIAN_CODENAME^} Stable"
|
||||||
msg+="Kernel Module: ${kernel_pkg} ${kernel_ver:-unknown} (arch: ${fam:-unknown})\n"
|
$use_bpo && src_label="Debian ${DEBIAN_CODENAME^}-Backports"
|
||||||
msg+="[+] linux-headers-amd64\n"
|
|
||||||
msg+="[+] firmware-nvidia-gsp\n"
|
local msg="Source: ${src_label}\n"
|
||||||
msg+="[+] nvidia-vaapi-driver"
|
msg+="NVIDIA Driver: ${nv_pkg} ${stable_nv_ver:-unknown}\n"
|
||||||
[ "$DEBIAN_VERSION" = "12" ] && msg+="\n[+] mesa-vdpau-drivers"
|
msg+="[+] firmware-misc-nonfree\n"
|
||||||
|
msg+="[+] nvidia-vaapi-driver\n"
|
||||||
|
msg+="[+] mesa-vdpau-drivers"
|
||||||
|
|
||||||
if ! _confirm "NVIDIA Driver" "$msg" 14 70; then
|
if ! _confirm "NVIDIA Driver" "$msg" 14 70; then
|
||||||
echo "Skipping NVIDIA driver installation."
|
echo "Skipping NVIDIA driver installation."
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- 4. EXECUTION ---
|
local extra_pkgs="firmware-misc-nonfree nvidia-vaapi-driver mesa-vdpau-drivers"
|
||||||
if ! _run_cmd "NVIDIA" "sudo apt install -y $install_pkgs" \
|
if $use_bpo; then
|
||||||
"Installing NVIDIA driver from stable..."; then
|
_run_cmd "NVIDIA" "sudo apt install -y -t ${DEBIAN_CODENAME}-backports $nv_pkg $extra_pkgs" \
|
||||||
NVIDIA_DRIVER_MODE=""
|
"Installing NVIDIA driver from backports..."
|
||||||
_msg "NVIDIA — Error" "NVIDIA driver installation FAILED.\n\nNo NVIDIA driver was installed." 10 60
|
NVIDIA_DRIVER_MODE="backports"
|
||||||
return 1
|
else
|
||||||
fi
|
_run_cmd "NVIDIA" "sudo apt install -y $nv_pkg $extra_pkgs" \
|
||||||
NVIDIA_DRIVER_MODE="stable"
|
"Installing NVIDIA driver from stable..."
|
||||||
|
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."
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- 5. DKMS POST-INSTALL VERIFICATION ---
|
|
||||||
echo -e "${GREEN}NVIDIA driver installed. Reboot required.${NC}"
|
echo -e "${GREEN}NVIDIA driver installed. Reboot required.${NC}"
|
||||||
_verify_nvidia_dkms_build "$kernel_pkg" || true
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -1,83 +1,30 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# kernel.sh — Submenu for kernel variants: Stable, RT, Cloud, Backports
|
# kernel.sh
|
||||||
|
|
||||||
show_kernel_menu() {
|
install_kernel_backports() {
|
||||||
while true; do
|
if [ "$(is_backports_enabled)" != "true" ]; then
|
||||||
local items=("stable" "Install linux-image-amd64")
|
_msg "Kernel" "Backports repository is not enabled.\n\nUse option 3 (Configure repositories) to enable backports\nbefore installing the backports kernel."
|
||||||
[ "$DEBIAN_VERSION" = "13" ] && items+=("backports" "Install from backports")
|
return 1
|
||||||
items+=("rt" "Install linux-image-rt-amd64 (Preempt-RT)")
|
fi
|
||||||
items+=("cloud" "Install linux-image-cloud-amd64")
|
|
||||||
items+=("back" "Return to main menu")
|
|
||||||
|
|
||||||
local choice
|
local backports_kernel_ver
|
||||||
choice=$(_menu "Kernel Installation" "Select kernel variant:" \
|
backports_kernel_ver=$(get_backports_kernel_version)
|
||||||
16 65 5 "${items[@]}")
|
|
||||||
[ -z "$choice" ] && break
|
|
||||||
clear
|
|
||||||
|
|
||||||
case "$choice" in
|
local msg="Install the latest kernel from backports?"
|
||||||
stable) _install_kernel_package "linux-image-amd64" "Stable" "" ;;
|
if [ "$backports_kernel_ver" != "unknown" ]; then
|
||||||
rt) _install_kernel_package "linux-image-rt-amd64" "RT" "" ;;
|
msg+="\n\nVersion: $backports_kernel_ver"
|
||||||
cloud) _install_kernel_package "linux-image-cloud-amd64" "Cloud" "" ;;
|
fi
|
||||||
backports)
|
msg+="\nOlder kernels remain available in GRUB."
|
||||||
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() {
|
if [ "$GPU_TYPE" == "nvidia" ]; then
|
||||||
# Hardware state is detected on demand; GPU_TYPE drives
|
msg+="\n\nWARNING: may break NVIDIA driver."
|
||||||
# the NVIDIA DKMS/RT warnings below.
|
fi
|
||||||
_ensure_state_detected
|
|
||||||
|
|
||||||
local pkg_base="$1"
|
if ! _confirm "Backports Kernel" "$msg"; then
|
||||||
local flavor="$2"
|
echo "Skipping kernel installation."
|
||||||
local bpo_flag="$3"
|
|
||||||
|
|
||||||
if ! apt-cache show "$pkg_base" >/dev/null 2>&1; then
|
|
||||||
_msg "Kernel" "Kernel not available for your current version of Debian.\n\nPackage: ${pkg_base}" 10 60
|
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$flavor" = "Backports" ] && [ "$GPU_TYPE" = "nvidia" ]; then
|
_run_cmd "Kernel" "sudo apt install -y -t ${DEBIAN_CODENAME}-backports linux-image-amd64" "Installing kernel from backports..."
|
||||||
if ! _confirm "Kernel" "WARNING: Backports kernel changes the kernel version.\nYour NVIDIA driver will need recompilation (DKMS).\n\nProceed?"; then
|
echo -e "${GREEN}Kernel installed. Reboot to use it.${NC}"
|
||||||
echo "Skipping."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if [ "$flavor" = "RT" ] && [ "$GPU_TYPE" = "nvidia" ]; then
|
|
||||||
_msg "Kernel — RT" "Note: Ensure your NVIDIA driver supports the RT (Preempt-RT) kernel.\nSome proprietary drivers may not work correctly." 10 60
|
|
||||||
fi
|
|
||||||
|
|
||||||
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)
|
|
||||||
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)
|
|
||||||
fi
|
|
||||||
[ -n "$ver" ] && ver=" ($ver)"
|
|
||||||
[ -n "$headers_ver" ] && headers_ver=" ($headers_ver)"
|
|
||||||
local summary="Install ${flavor} kernel?\n Image: ${pkg_base}${ver}\n Headers: ${headers_pkg}${headers_ver}"
|
|
||||||
[ -n "$bpo_flag" ] && summary+="\n From: ${DEBIAN_CODENAME^}-backports"
|
|
||||||
|
|
||||||
if ! _confirm "Kernel — ${flavor}" "$summary"; then
|
|
||||||
echo "Skipping."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
_run_cmd "Kernel" "sudo apt install -y ${bpo_flag} ${pkg_base} ${headers_pkg}" \
|
|
||||||
"Installing ${flavor} kernel + headers..."
|
|
||||||
|
|
||||||
echo -e "${GREEN}${flavor} kernel installed. Reboot to use it.${NC}"
|
|
||||||
_pause
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,13 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
source "${MODULES_DIR}/repos/migrate.sh" 2>/dev/null || true
|
|
||||||
|
|
||||||
# State for backup/restore
|
# State for backup/restore
|
||||||
REPO_BACKUP_DIR=""
|
REPO_BACKUP_DIR=""
|
||||||
|
|
||||||
backup_current_repos() {
|
backup_current_repos() {
|
||||||
cleanup_repo_backup # never orphan a previous backup by overwriting the pointer
|
REPO_BACKUP_DIR=$(mktemp -d)
|
||||||
REPO_BACKUP_DIR=$(mktemp -d) || {
|
for f in /etc/apt/sources.list /etc/apt/sources.list.d/debian.sources; do
|
||||||
REPO_BACKUP_DIR=""
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
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
|
|
||||||
if [ -f "$f" ]; then
|
if [ -f "$f" ]; then
|
||||||
local rel="${f#/etc/apt/}"
|
cp "$f" "$REPO_BACKUP_DIR/$(basename "$f")"
|
||||||
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")"
|
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
@@ -27,32 +17,17 @@ restore_previous_repos() {
|
|||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
echo -e "${RED}Restoring previous repository configuration...${NC}"
|
echo -e "${RED}Restoring previous repository configuration...${NC}"
|
||||||
local found=false
|
if [ -f "$REPO_BACKUP_DIR/sources.list" ]; then
|
||||||
for f in /etc/apt/sources.list /etc/apt/sources.list.d/debian.sources \
|
sudo cp "$REPO_BACKUP_DIR/sources.list" /etc/apt/sources.list
|
||||||
/etc/apt/sources.list.d/debian-backports.list /etc/apt/sources.list.d/debian-backports.sources; do
|
else
|
||||||
local rel="${f#/etc/apt/}"
|
sudo rm -f /etc/apt/sources.list
|
||||||
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
|
|
||||||
found=true
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
if $found; then
|
|
||||||
sudo rm -f /etc/apt/sources.list.disabled
|
|
||||||
fi
|
fi
|
||||||
|
if [ -f "$REPO_BACKUP_DIR/debian.sources" ]; then
|
||||||
|
sudo cp "$REPO_BACKUP_DIR/debian.sources" /etc/apt/sources.list.d/debian.sources
|
||||||
|
else
|
||||||
|
sudo rm -f /etc/apt/sources.list.d/debian.sources
|
||||||
|
fi
|
||||||
|
sudo rm -f /etc/apt/sources.list.disabled
|
||||||
rm -rf "$REPO_BACKUP_DIR"
|
rm -rf "$REPO_BACKUP_DIR"
|
||||||
REPO_BACKUP_DIR=""
|
REPO_BACKUP_DIR=""
|
||||||
}
|
}
|
||||||
@@ -64,590 +39,130 @@ cleanup_repo_backup() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
finalize_deb822() {
|
||||||
# Cleanup helpers for embedded backports
|
if [ -f /etc/apt/sources.list ]; then
|
||||||
# ---------------------------------------------------------------------------
|
sudo mv /etc/apt/sources.list /etc/apt/sources.list.disabled
|
||||||
|
echo "Classic sources.list disabled (renamed to sources.list.disabled)"
|
||||||
_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
|
|
||||||
if _confirm "Clean Classic Sources" "Remove backports line from ${file}?"; then
|
|
||||||
sudo sed -i "/${codename}-backports/d" "$file"
|
|
||||||
echo "Removed backports from ${file}"
|
|
||||||
fi
|
fi
|
||||||
|
cleanup_repo_backup
|
||||||
}
|
}
|
||||||
|
|
||||||
_clean_embedded_backports_deb822() {
|
|
||||||
local codename="$1"
|
|
||||||
local file="/etc/apt/sources.list.d/debian.sources"
|
|
||||||
[ ! -f "$file" ] && return 0
|
|
||||||
|
|
||||||
# Only proceed if the Suites line contains the backports token
|
|
||||||
grep -qE "^Suites:.*${codename}-backports\b" "$file" 2>/dev/null || return 0
|
|
||||||
|
|
||||||
if _confirm "Clean deb822 Sources" "Remove backports suite from ${file}?"; then
|
|
||||||
# Remove only the backports token, leaving other suites intact
|
|
||||||
sudo sed -i "s/[[:space:]]*${codename}-backports[[:space:]]*/ /g" "$file"
|
|
||||||
# Clean up whitespace on Suites lines
|
|
||||||
sudo sed -i '/^Suites:[[:space:]]*$/d' "$file" 2>/dev/null || true
|
|
||||||
echo "Removed backports suite from ${file}"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Write functions – idempotent, with _confirm dialogs
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
_write_deb822() {
|
|
||||||
local codename="$1" action="$2" bp_enabled="$3" bp_location="$4"
|
|
||||||
local components="${5:-main contrib non-free non-free-firmware}"
|
|
||||||
|
|
||||||
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"
|
|
||||||
main_content+="Suites: ${codename} ${codename}-updates\n"
|
|
||||||
main_content+="Components: ${components}\n"
|
|
||||||
main_content+="\n"
|
|
||||||
main_content+="Types: deb\n"
|
|
||||||
main_content+="URIs: https://security.debian.org/debian-security\n"
|
|
||||||
main_content+="Suites: ${codename}-security\n"
|
|
||||||
main_content+="Components: ${components}\n"
|
|
||||||
|
|
||||||
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 "Wrote ${main_file}"
|
|
||||||
else
|
|
||||||
echo "Main repository configuration skipped."
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Backports: always in separate file
|
|
||||||
if [ "$bp_enabled" = true ]; then
|
|
||||||
_write_deb822_backports "$codename" || true
|
|
||||||
else
|
|
||||||
_remove_deb822_backports "$codename" || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# On migration from classic, disable the old file
|
|
||||||
if [ "$action" = "migrate" ] && { [ "$bp_location" = "embedded-classic" ] || [ "$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
|
|
||||||
echo "Classic sources.list disabled (renamed to sources.list.disabled)"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Clean embedded backports from old classic file if they existed there
|
|
||||||
if [ "$bp_location" = "embedded-classic" ]; then
|
|
||||||
_clean_embedded_backports_classic "$codename"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_write_deb822_backports() {
|
|
||||||
local codename="$1"
|
|
||||||
local bp_file="/etc/apt/sources.list.d/debian-backports.sources"
|
|
||||||
local bp_content=""
|
|
||||||
bp_content+="Types: deb\n"
|
|
||||||
bp_content+="URIs: https://deb.debian.org/debian\n"
|
|
||||||
bp_content+="Suites: ${codename}-backports\n"
|
|
||||||
bp_content+="Components: main contrib non-free non-free-firmware\n"
|
|
||||||
|
|
||||||
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 "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
|
|
||||||
}
|
|
||||||
|
|
||||||
_remove_deb822_backports() {
|
|
||||||
local codename="$1"
|
|
||||||
local bp_file="/etc/apt/sources.list.d/debian-backports.sources"
|
|
||||||
|
|
||||||
if [ -f "$bp_file" ]; then
|
|
||||||
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 &&
|
|
||||||
_clean_embedded_backports_deb822 "$codename"
|
|
||||||
|
|
||||||
# Clean embedded backports from classic file too (safety net)
|
|
||||||
_clean_embedded_backports_classic "$codename"
|
|
||||||
}
|
|
||||||
|
|
||||||
_write_classic() {
|
|
||||||
local codename="$1" action="$2" bp_enabled="$3" bp_location="$4"
|
|
||||||
local components="${5:-main contrib non-free non-free-firmware}"
|
|
||||||
|
|
||||||
local main_file="/etc/apt/sources.list"
|
|
||||||
local main_content=""
|
|
||||||
main_content+="# Official repository\n"
|
|
||||||
main_content+="deb https://deb.debian.org/debian ${codename} ${components}\n"
|
|
||||||
main_content+="# deb-src https://deb.debian.org/debian ${codename} ${components}\n"
|
|
||||||
main_content+="\n"
|
|
||||||
main_content+="# Updates\n"
|
|
||||||
main_content+="deb https://deb.debian.org/debian ${codename}-updates ${components}\n"
|
|
||||||
main_content+="# deb-src https://deb.debian.org/debian ${codename}-updates ${components}\n"
|
|
||||||
main_content+="\n"
|
|
||||||
main_content+="# Security\n"
|
|
||||||
main_content+="deb https://security.debian.org/debian-security ${codename}-security ${components}\n"
|
|
||||||
main_content+="# deb-src https://security.debian.org/debian-security ${codename}-security ${components}\n"
|
|
||||||
|
|
||||||
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 "Wrote ${main_file}"
|
|
||||||
else
|
|
||||||
echo "Main repository configuration skipped."
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Backports: always in separate file
|
|
||||||
if [ "$bp_enabled" = true ]; then
|
|
||||||
_write_classic_backports "$codename" || true
|
|
||||||
else
|
|
||||||
_remove_classic_backports "$codename" || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# On migration from deb822, disable the old file
|
|
||||||
if [ "$action" = "migrate" ]; then
|
|
||||||
if [ -f /etc/apt/sources.list.d/debian.sources ]; then
|
|
||||||
if _confirm "Disable Deb822" "Migrating to classic format. Disable /etc/apt/sources.list.d/debian.sources?"; then
|
|
||||||
sudo mv /etc/apt/sources.list.d/debian.sources /etc/apt/sources.list.d/debian.sources.disabled
|
|
||||||
echo "Deb822 sources disabled (renamed to debian.sources.disabled)"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Clean embedded backports from old deb822 file if they existed there
|
|
||||||
if [ "$bp_location" = "embedded-deb822" ]; then
|
|
||||||
_clean_embedded_backports_deb822 "$codename"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_write_classic_backports() {
|
|
||||||
local codename="$1"
|
|
||||||
local bp_file="/etc/apt/sources.list.d/debian-backports.list"
|
|
||||||
local bp_content=""
|
|
||||||
bp_content+="# Backports\n"
|
|
||||||
bp_content+="deb https://deb.debian.org/debian ${codename}-backports main contrib non-free non-free-firmware\n"
|
|
||||||
|
|
||||||
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 "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
|
|
||||||
}
|
|
||||||
|
|
||||||
_remove_classic_backports() {
|
|
||||||
local codename="$1"
|
|
||||||
local bp_file="/etc/apt/sources.list.d/debian-backports.list"
|
|
||||||
|
|
||||||
if [ -f "$bp_file" ]; then
|
|
||||||
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 &&
|
|
||||||
_clean_embedded_backports_classic "$codename"
|
|
||||||
|
|
||||||
# Clean embedded backports from deb822 file too (safety net)
|
|
||||||
_clean_embedded_backports_deb822 "$codename"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Main entry point
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
configure_repos() {
|
configure_repos() {
|
||||||
echo -e "${YELLOW}Repository configuration...${NC}"
|
echo -e "${YELLOW}Repository configuration...${NC}"
|
||||||
|
|
||||||
|
local use_deb822=false
|
||||||
|
if [ "$DEBIAN_CODENAME" = "trixie" ]; then
|
||||||
|
if whiptail --title "Repository Format" --defaultno --yesno "Use deb822 format (modern .sources)?" 8 60; then
|
||||||
|
use_deb822=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
local enable_backports=false
|
||||||
|
if _confirm "Backports" "Enable backports repository?\n\nProvides newer kernel, drivers, Mesa."; then
|
||||||
|
enable_backports=true
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "$DEBIAN_CODENAME" ]; then
|
if [ -z "$DEBIAN_CODENAME" ]; then
|
||||||
echo -e "${RED}Error: Could not detect Debian codename. Aborting.${NC}"
|
echo -e "${RED}Error: Could not detect Debian codename. Aborting.${NC}"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Repositories submenu ──
|
sync_system_time
|
||||||
while true; do
|
|
||||||
local repo_choice
|
|
||||||
|
|
||||||
if [ "$DEBIAN_CODENAME" = "sid" ]; then
|
backup_current_repos
|
||||||
repo_choice=$(_menu "Configure Repositories" \
|
|
||||||
"Select an option:" $TUI_ALTO $TUI_ANCHO 6 \
|
|
||||||
"1" "Enable Contrib & Non-Free Components" \
|
|
||||||
"2" "Migrate traditional sources.list to DEB822 format" \
|
|
||||||
"3" "Back to main menu")
|
|
||||||
else
|
|
||||||
repo_choice=$(_menu "Configure Repositories" \
|
|
||||||
"Select an option:" $TUI_ALTO $TUI_ANCHO 6 \
|
|
||||||
"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")
|
|
||||||
fi
|
|
||||||
|
|
||||||
[ -z "$repo_choice" ] && break
|
|
||||||
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 ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Submenu helpers
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
_components_enabled() {
|
|
||||||
if [ -f /etc/apt/sources.list.d/debian.sources ]; then
|
|
||||||
for c in $(grep "^Components:" /etc/apt/sources.list.d/debian.sources 2>/dev/null | cut -d: -f2-); do
|
|
||||||
[ "$c" = "contrib" ] || [ "$c" = "non-free" ] && return 0
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
if [ -f /etc/apt/sources.list ]; then
|
|
||||||
local components
|
|
||||||
components=$(grep "^[^#]*deb .* main" /etc/apt/sources.list 2>/dev/null | head -1 | sed 's/.*main\s*//')
|
|
||||||
for c in $components; do
|
|
||||||
[ "$c" = "contrib" ] || [ "$c" = "non-free" ] && return 0
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
_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.
|
|
||||||
if [ "$upgradable" -gt 0 ]; then
|
|
||||||
if _confirm "Upgrade System" "$upgradable packages can be upgraded. Upgrade now?"; then
|
|
||||||
_run_cmd "Upgrade" "sudo apt upgrade -y" "Upgrading system..."
|
|
||||||
sudo apt-mark unhold tzdata 2>/dev/null || true
|
|
||||||
sudo apt autoremove -y
|
|
||||||
sudo apt autoclean
|
|
||||||
echo -e "${GREEN}System upgraded.${NC}"
|
|
||||||
else
|
|
||||||
echo "Skipping upgrade."
|
|
||||||
fi
|
|
||||||
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
|
if $use_deb822; then
|
||||||
_write_deb822 "$DEBIAN_CODENAME" "write" "$bp_enabled" "$bp_location" "$components" || {
|
generate_deb822_sources "$DEBIAN_CODENAME" "$enable_backports"
|
||||||
cleanup_repo_backup
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
_write_classic "$DEBIAN_CODENAME" "write" "$bp_enabled" "$bp_location" "$components" || {
|
generate_classic_sources "$DEBIAN_CODENAME" "$enable_backports"
|
||||||
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
|
fi
|
||||||
|
|
||||||
echo "Updating package lists..."
|
echo "Updating package lists..."
|
||||||
if sudo apt update; then
|
sudo apt update
|
||||||
cleanup_repo_backup
|
local apt_rc=$?
|
||||||
echo -e "${GREEN}Repository components configured.${NC}"
|
if [ $apt_rc -eq 0 ]; then
|
||||||
return 0
|
REPOS_CONFIGURED=true
|
||||||
|
echo -e "${GREEN}Repositories configured and updated successfully.${NC}"
|
||||||
|
|
||||||
|
if $use_deb822; then
|
||||||
|
finalize_deb822
|
||||||
|
else
|
||||||
|
cleanup_repo_backup
|
||||||
|
fi
|
||||||
|
|
||||||
|
local upgradable
|
||||||
|
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
|
||||||
|
sudo apt autoclean
|
||||||
|
echo -e "${GREEN}System upgraded.${NC}"
|
||||||
|
else
|
||||||
|
echo "Skipping upgrade."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
restore_previous_repos
|
restore_previous_repos
|
||||||
echo -e "${RED}apt update failed. Previous configuration restored.${NC}"
|
echo -e "${RED}apt update failed. Previous repository configuration restored.${NC}"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
_repos_enable_components() {
|
generate_classic_sources() {
|
||||||
local current_format bp_enabled bp_location components
|
local codename="$1"
|
||||||
current_format=$(detect_repo_format)
|
local backports="$2"
|
||||||
|
|
||||||
if _components_enabled; then
|
local content=""
|
||||||
if _confirm "Disable Components" \
|
content="# Official repository\n"
|
||||||
"Contrib and non-free are already enabled. Disable them?"; then
|
content+="deb https://deb.debian.org/debian ${codename} main contrib non-free non-free-firmware\n"
|
||||||
components="main"
|
content+="# deb-src https://deb.debian.org/debian ${codename} main contrib non-free non-free-firmware\n\n"
|
||||||
else
|
|
||||||
components=$(detect_active_components)
|
content+="# Updates\n"
|
||||||
echo "Keeping current components: $components"
|
content+="deb https://deb.debian.org/debian ${codename}-updates main contrib non-free non-free-firmware\n"
|
||||||
fi
|
content+="# deb-src https://deb.debian.org/debian ${codename}-updates main contrib non-free non-free-firmware\n\n"
|
||||||
|
|
||||||
|
content+="# Security\n"
|
||||||
|
content+="deb https://security.debian.org/debian-security ${codename}-security main contrib non-free non-free-firmware\n"
|
||||||
|
content+="# deb-src https://security.debian.org/debian-security ${codename}-security main contrib non-free non-free-firmware\n\n"
|
||||||
|
|
||||||
|
if $backports; then
|
||||||
|
content+="# Backports (active)\n"
|
||||||
|
content+="deb https://deb.debian.org/debian ${codename}-backports main contrib non-free non-free-firmware\n"
|
||||||
|
content+="# deb-src https://deb.debian.org/debian ${codename}-backports main contrib non-free non-free-firmware\n"
|
||||||
else
|
else
|
||||||
if ! _confirm "Enable Components" \
|
content+="# Backports (not enabled)\n"
|
||||||
"Enable contrib and non-free components?\n\n\
|
content+="# deb https://deb.debian.org/debian ${codename}-backports main contrib non-free non-free-firmware\n"
|
||||||
Needed for proprietary drivers, firmware, and other
|
|
||||||
popular software (like gaming platforms and proprietary tools)." 12 60; then
|
|
||||||
echo "No changes made."
|
|
||||||
_pause
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
if [ "$DEBIAN_VERSION" = "11" ]; then
|
|
||||||
components="main contrib non-free"
|
|
||||||
else
|
|
||||||
components="main contrib non-free non-free-firmware"
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
bp_enabled=false
|
echo -e "$content" | sudo tee /etc/apt/sources.list > /dev/null
|
||||||
bp_location="none"
|
|
||||||
if detect_backports_status "$DEBIAN_CODENAME"; then
|
|
||||||
bp_enabled=true
|
|
||||||
bp_location=$(detect_backports_location "$DEBIAN_CODENAME")
|
|
||||||
fi
|
|
||||||
|
|
||||||
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
|
|
||||||
else
|
|
||||||
if ! _write_classic "$DEBIAN_CODENAME" "write" "$bp_enabled" "$bp_location" "$components"; then
|
|
||||||
echo "No changes made."
|
|
||||||
_pause
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Updating package lists..."
|
|
||||||
if sudo apt update; then
|
|
||||||
cleanup_repo_backup
|
|
||||||
echo -e "${GREEN}Repository components configured.${NC}"
|
|
||||||
_repos_offer_upgrade
|
|
||||||
echo -e "${GREEN}Cleaning old packages...${NC}"
|
|
||||||
sudo apt autoremove -y
|
|
||||||
sudo apt autoclean
|
|
||||||
echo -e "${GREEN}System packages cleaned.${NC}"
|
|
||||||
else
|
|
||||||
restore_previous_repos
|
|
||||||
echo -e "${RED}apt update failed. Previous configuration restored.${NC}"
|
|
||||||
fi
|
|
||||||
_pause
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_repos_migrate_format() {
|
generate_deb822_sources() {
|
||||||
local current_format bp_enabled bp_location
|
local codename="$1"
|
||||||
current_format=$(detect_repo_format)
|
local backports="$2"
|
||||||
|
|
||||||
if [ "$current_format" != "classic" ]; then
|
sudo mkdir -p /etc/apt/sources.list.d
|
||||||
echo "Repositories are already in DEB822 format."
|
|
||||||
_pause
|
local content=""
|
||||||
return
|
content="Types: deb\n"
|
||||||
|
content+="URIs: https://deb.debian.org/debian\n"
|
||||||
|
content+="Suites: ${codename} ${codename}-updates\n"
|
||||||
|
content+="Components: main contrib non-free non-free-firmware\n\n"
|
||||||
|
|
||||||
|
content+="Types: deb\n"
|
||||||
|
content+="URIs: https://security.debian.org/debian-security\n"
|
||||||
|
content+="Suites: ${codename}-security\n"
|
||||||
|
content+="Components: main contrib non-free non-free-firmware\n\n"
|
||||||
|
|
||||||
|
if $backports; then
|
||||||
|
content+="Types: deb\n"
|
||||||
|
content+="URIs: https://deb.debian.org/debian\n"
|
||||||
|
content+="Suites: ${codename}-backports\n"
|
||||||
|
content+="Components: main contrib non-free non-free-firmware\n"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$DEBIAN_CODENAME" != "trixie" ]; then
|
echo -e "$content" | sudo tee /etc/apt/sources.list.d/debian.sources > /dev/null
|
||||||
echo "Format migration is only relevant for Debian 13 (Trixie)."
|
|
||||||
_pause
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Preserve existing backports state across format migration
|
|
||||||
bp_enabled=false
|
|
||||||
bp_location="none"
|
|
||||||
if detect_backports_status "$DEBIAN_CODENAME"; then
|
|
||||||
bp_enabled=true
|
|
||||||
bp_location=$(detect_backports_location "$DEBIAN_CODENAME")
|
|
||||||
fi
|
|
||||||
|
|
||||||
backup_current_repos
|
|
||||||
_write_deb822 "$DEBIAN_CODENAME" "migrate" "$bp_enabled" "$bp_location"
|
|
||||||
|
|
||||||
echo "Updating package lists..."
|
|
||||||
if sudo apt update; then
|
|
||||||
cleanup_repo_backup
|
|
||||||
echo -e "${GREEN}Repository format migrated to DEB822.${NC}"
|
|
||||||
else
|
|
||||||
restore_previous_repos
|
|
||||||
echo -e "${RED}apt update failed. Backup restored.${NC}"
|
|
||||||
fi
|
|
||||||
_pause
|
|
||||||
}
|
|
||||||
|
|
||||||
_repos_setup_backports() {
|
|
||||||
local current_format bp_status bp_location
|
|
||||||
current_format=$(detect_repo_format)
|
|
||||||
|
|
||||||
if detect_backports_status "$DEBIAN_CODENAME"; then
|
|
||||||
bp_status="enabled"
|
|
||||||
else
|
|
||||||
bp_status="disabled"
|
|
||||||
fi
|
|
||||||
bp_location=$(detect_backports_location "$DEBIAN_CODENAME")
|
|
||||||
|
|
||||||
echo "Backports are currently $bp_status."
|
|
||||||
|
|
||||||
local enable_backports=false
|
|
||||||
if _confirm "Backports" "Do you want to enable the official Debian Backports repository?\n\n\
|
|
||||||
Backports provides newer, selectively updated packages from the next Debian testing branch, recompiled to run stably on your current system.\n
|
|
||||||
Answer NO to disable or remove backports if they are currently enabled." 16 70; then
|
|
||||||
enable_backports=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Nothing to do — already in desired state
|
|
||||||
if { $enable_backports && [ "$bp_status" = "enabled" ]; } ||
|
|
||||||
{ ! $enable_backports && [ "$bp_status" = "disabled" ]; }; then
|
|
||||||
echo "Backports are already configured as requested."
|
|
||||||
_pause
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
backup_current_repos
|
|
||||||
|
|
||||||
local write_ok=true
|
|
||||||
if $enable_backports; then
|
|
||||||
if [ "$current_format" = "deb822" ]; then
|
|
||||||
_write_deb822_backports "$DEBIAN_CODENAME" || write_ok=false
|
|
||||||
else
|
|
||||||
_write_classic_backports "$DEBIAN_CODENAME" || write_ok=false
|
|
||||||
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
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Updating package lists..."
|
|
||||||
if sudo apt update; then
|
|
||||||
cleanup_repo_backup
|
|
||||||
echo -e "${GREEN}Backports configured.${NC}"
|
|
||||||
else
|
|
||||||
restore_previous_repos
|
|
||||||
echo -e "${RED}apt update failed. Backup restored.${NC}"
|
|
||||||
fi
|
|
||||||
_pause
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,268 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# migrate.sh — Stable → Testing / SID branch migration
|
|
||||||
# License GPL v3
|
|
||||||
|
|
||||||
_MIGRATE_BACKUP=""
|
|
||||||
|
|
||||||
_persistent_backup_repos() {
|
|
||||||
local stamp
|
|
||||||
stamp=$(date +%Y%m%d-%H%M%S)
|
|
||||||
_MIGRATE_BACKUP="/var/backups/debianito-repos-${stamp}.tar.gz"
|
|
||||||
sudo mkdir -p /var/backups
|
|
||||||
local files=()
|
|
||||||
[ -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
|
|
||||||
echo "Backup: $_MIGRATE_BACKUP"
|
|
||||||
}
|
|
||||||
|
|
||||||
_restore_backup() {
|
|
||||||
if [ -z "$_MIGRATE_BACKUP" ] || [ ! -f "$_MIGRATE_BACKUP" ]; then
|
|
||||||
_msg "Restore Error" "No backup found at $_MIGRATE_BACKUP.\nCannot restore. Your system may be in an inconsistent state." 10 70
|
|
||||||
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
|
|
||||||
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+="Suites: sid\n"
|
|
||||||
else
|
|
||||||
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+="\n"
|
|
||||||
main_content+="Types: deb\n"
|
|
||||||
main_content+="URIs: https://security.debian.org/debian-security\n"
|
|
||||||
main_content+="Suites: testing-security\n"
|
|
||||||
main_content+="Components: main contrib non-free non-free-firmware\n"
|
|
||||||
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 "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\
|
|
||||||
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\
|
|
||||||
A full persistent backup will be saved to /var/backups/\n\
|
|
||||||
so you can restore if things go wrong." 16 70
|
|
||||||
|
|
||||||
if ! _confirm "Branch Migration" "Do you want to proceed with the migration?"; then
|
|
||||||
echo "Migration cancelled."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── Screen 2: Plan summary ──
|
|
||||||
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+=" 4. Run: apt update\n"
|
|
||||||
plan+=" 5. Run: apt upgrade -y\n"
|
|
||||||
plan+=" 6. Run: apt full-upgrade -y\n"
|
|
||||||
plan+=" 7. Run: apt autoremove -y\n\n"
|
|
||||||
plan+="If apt update fails, the backup is restored immediately."
|
|
||||||
|
|
||||||
_msg_red "Migration Plan" "$plan" 16 70
|
|
||||||
|
|
||||||
if ! _confirm "Migration Plan" "Proceed with the plan?"; then
|
|
||||||
echo "Migration cancelled."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── Screen 3: Branch selection ──
|
|
||||||
local branch
|
|
||||||
branch=$(_inputbox "Target Branch" \
|
|
||||||
"Type exactly TESTING or SID (case-sensitive):" 10 60 "")
|
|
||||||
|
|
||||||
[ -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
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Normalize to lowercase for internal use
|
|
||||||
local target
|
|
||||||
target=$(echo "$branch" | tr '[:upper:]' '[:lower:]')
|
|
||||||
|
|
||||||
# ── Screen 4: Execution ──
|
|
||||||
echo -e "${YELLOW}Starting branch migration to ${target}...${NC}"
|
|
||||||
|
|
||||||
# 4a. Persistent backup
|
|
||||||
echo "Creating backup..."
|
|
||||||
_persistent_backup_repos || {
|
|
||||||
_msg "Backup Error" "Failed to create backup. Aborting." 8 60
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
# 4b. Clean backports
|
|
||||||
echo "Removing backports configuration..."
|
|
||||||
[ -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
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# 4e. SID guardrails: install bug alerts before upgrade
|
|
||||||
if [ "$target" = "sid" ]; then
|
|
||||||
echo -e "${YELLOW}Installing apt-listbugs and apt-listchanges (SID guardrails)...${NC}"
|
|
||||||
sudo apt update -qq 2>/dev/null || true
|
|
||||||
sudo DEBIAN_FRONTEND=noninteractive apt install -y apt-listbugs apt-listchanges || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 4f. apt update with rollback on failure
|
|
||||||
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\
|
|
||||||
$_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
|
|
||||||
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
|
|
||||||
|
|
||||||
# 4h. Re-run detection to reflect new branch
|
|
||||||
echo -e "${YELLOW}Re-running system detection for new branch...${NC}"
|
|
||||||
detect_debian_version
|
|
||||||
detect_kernel
|
|
||||||
detect_gpu
|
|
||||||
detect_storage
|
|
||||||
|
|
||||||
echo -e "${GREEN}Branch migration to ${target} completed successfully.${NC}"
|
|
||||||
|
|
||||||
# ── Screen 5: Reboot reminder ──
|
|
||||||
_msg "Migration Complete" \
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
# shellcheck disable=SC2148
|
|
||||||
# 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.
|
|
||||||
# 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
|
|
||||||
echo "deb822"
|
|
||||||
elif [ -f "$apt_dir/sources.list" ] && grep -qE '^[^#]*\bdeb\b' "$apt_dir/sources.list" 2>/dev/null; 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)
|
|
||||||
# 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
|
|
||||||
fi
|
|
||||||
if [ -f "$apt_dir/sources.list" ]; then
|
|
||||||
grep -qE "^[^#]*${codename}-backports" "$apt_dir/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
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Locate where backports are configured
|
|
||||||
# Returns: "standalone-deb822" (debian-backports.sources),
|
|
||||||
# "standalone-classic" (debian-backports.list),
|
|
||||||
# "embedded-deb822" (inside debian.sources),
|
|
||||||
# "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
|
|
||||||
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
|
|
||||||
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
|
|
||||||
echo "embedded-deb822"
|
|
||||||
elif [ -f "$apt_dir/sources.list" ] && \
|
|
||||||
grep -qE "^[^#]*${codename}-backports" "$apt_dir/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
|
|
||||||
}
|
|
||||||
|
|
||||||
# Compare generated content vs existing file (idempotency check)
|
|
||||||
# Returns: 0 if content differs, 1 if identical
|
|
||||||
content_differs() {
|
|
||||||
local file="$1"
|
|
||||||
local content="$2"
|
|
||||||
|
|
||||||
if [ ! -f "$file" ]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
local current
|
|
||||||
current=$(cat "$file")
|
|
||||||
local generated
|
|
||||||
generated=$(echo -e "$content")
|
|
||||||
if [ "$current" = "$generated" ]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
@@ -1,189 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# rescue.sh — UEFI boot repair and system rescue utilities
|
|
||||||
|
|
||||||
_rescue_boot_sb() {
|
|
||||||
echo -e "${YELLOW}UEFI Secure Boot repair...${NC}"
|
|
||||||
|
|
||||||
if [ ! -d /sys/firmware/efi ]; then
|
|
||||||
echo -e "${RED}Not a UEFI system. Skipping.${NC}"
|
|
||||||
_pause
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
local sb_state
|
|
||||||
sb_state=$(mokutil --sb-state 2>/dev/null)
|
|
||||||
if ! echo "$sb_state" | grep -qi "enabled"; then
|
|
||||||
echo -e "${YELLOW}Secure Boot is not enabled. No refirm needed.${NC}"
|
|
||||||
_pause
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! _confirm "Secure Boot Repair" \
|
|
||||||
"Secure Boot is enabled.\n\nReinstall and refirm shim-signed + GRUB?\n\nThis fixes boot issues where Debian is skipped\nin UEFI after kernel/driver changes."; then
|
|
||||||
echo "Skipped."
|
|
||||||
_pause
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
local efi_dir="/boot/efi"
|
|
||||||
[ ! -d "$efi_dir" ] && efi_dir="/boot"
|
|
||||||
|
|
||||||
_run_cmd "Refirm" \
|
|
||||||
"sudo apt install --reinstall -y shim-signed grub-efi-amd64-signed linux-image-amd64" \
|
|
||||||
"Reinstalling signed boot packages..."
|
|
||||||
_run_cmd "GRUB Install" \
|
|
||||||
"sudo grub-install --target=x86_64-efi --efi-directory=$efi_dir --bootloader-id=debian --recheck" \
|
|
||||||
"Reinstalling GRUB to EFI partition..."
|
|
||||||
_run_cmd "GRUB Config" \
|
|
||||||
"sudo update-grub" \
|
|
||||||
"Regenerating GRUB configuration..."
|
|
||||||
|
|
||||||
echo -e "${GREEN}Boot repair complete. Reboot to verify.${NC}"
|
|
||||||
_pause
|
|
||||||
}
|
|
||||||
|
|
||||||
_rescue_initramfs() {
|
|
||||||
if ! _confirm "Initramfs" "Regenerate initramfs for all kernels?\n\nFixes boot issues caused by missing drivers\nor corrupted initrd images."; then
|
|
||||||
echo "Skipped."
|
|
||||||
_pause
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
_run_cmd "Initramfs" "sudo update-initramfs -u -k all" "Regenerating initramfs for all kernels..."
|
|
||||||
echo -e "${GREEN}Initramfs regenerated.${NC}"
|
|
||||||
_pause
|
|
||||||
}
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
|
||||||
# GRUB helpers
|
|
||||||
# ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
_set_grub_var() {
|
|
||||||
local var="$1" val="$2" file="/etc/default/grub"
|
|
||||||
if grep -q "^${var}=" "$file" 2>/dev/null; then
|
|
||||||
sudo sed -i "s/^${var}=.*/${var}=${val}/" "$file"
|
|
||||||
elif grep -q "^#${var}=" "$file" 2>/dev/null; then
|
|
||||||
sudo sed -i "s/^#${var}=.*/${var}=${val}/" "$file"
|
|
||||||
else
|
|
||||||
echo "${var}=${val}" | sudo tee -a "$file" >/dev/null
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_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 override="/etc/default/grub.d/99_script_override.cfg"
|
|
||||||
|
|
||||||
sudo cp "$file" "$backup"
|
|
||||||
|
|
||||||
_set_grub_var GRUB_TIMEOUT "$timeout"
|
|
||||||
_set_grub_var GRUB_TIMEOUT_STYLE "$style"
|
|
||||||
_set_grub_var GRUB_RECORDFAIL_TIMEOUT "$recordfail"
|
|
||||||
[ -n "$os_prober" ] && _set_grub_var GRUB_DISABLE_OS_PROBER "$os_prober"
|
|
||||||
|
|
||||||
sudo mkdir -p /etc/default/grub.d
|
|
||||||
if [ -n "$os_prober" ]; then
|
|
||||||
cat << EOF | sudo tee "$override" > /dev/null
|
|
||||||
GRUB_TIMEOUT_STYLE=$style
|
|
||||||
GRUB_TIMEOUT=$timeout
|
|
||||||
GRUB_RECORDFAIL_TIMEOUT=$recordfail
|
|
||||||
GRUB_DISABLE_OS_PROBER=$os_prober
|
|
||||||
EOF
|
|
||||||
else
|
|
||||||
cat << EOF | sudo tee "$override" > /dev/null
|
|
||||||
GRUB_TIMEOUT=$timeout
|
|
||||||
GRUB_TIMEOUT_STYLE=$style
|
|
||||||
GRUB_RECORDFAIL_TIMEOUT=$recordfail
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
|
|
||||||
local summary
|
|
||||||
summary="Settings applied:\n\n"
|
|
||||||
summary+=" GRUB_TIMEOUT=$timeout\n"
|
|
||||||
summary+=" GRUB_TIMEOUT_STYLE=$style\n"
|
|
||||||
summary+=" GRUB_RECORDFAIL_TIMEOUT=$recordfail\n"
|
|
||||||
[ -n "$os_prober" ] && summary+=" GRUB_DISABLE_OS_PROBER=$os_prober\n"
|
|
||||||
summary+="\nRunning update-grub..."
|
|
||||||
|
|
||||||
echo -e "$summary"
|
|
||||||
|
|
||||||
if sudo update-grub >/dev/null 2>&1; then
|
|
||||||
_msg "GRUB Settings" \
|
|
||||||
"GRUB settings applied successfully!\n\nBackup saved: ${backup}\n\nReboot to see the changes."
|
|
||||||
else
|
|
||||||
echo -e "${RED}update-grub failed. Restoring backup...${NC}"
|
|
||||||
sudo cp "$backup" "$file"
|
|
||||||
sudo rm -f "$override"
|
|
||||||
_msg "Error" "update-grub failed.\nBackup restored from ${backup}\nOverride removed: ${override}."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_grub_menu_settings() {
|
|
||||||
while true; do
|
|
||||||
local choice
|
|
||||||
choice=$(_menu "GRUB Boot Menu Settings" \
|
|
||||||
"Configure when and how the GRUB menu appears:" \
|
|
||||||
$TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
|
||||||
"1" "Disable GRUB menu on start (Fastest boot)" \
|
|
||||||
"2" "Faster boot (Show 3-sec countdown)" \
|
|
||||||
"3" "Default boot (Show menu for 5 secs)" \
|
|
||||||
"4" "Custom timeout (Input seconds)" \
|
|
||||||
"5" "Back")
|
|
||||||
|
|
||||||
[ -z "$choice" ] && return
|
|
||||||
clear
|
|
||||||
|
|
||||||
case "$choice" in
|
|
||||||
1)
|
|
||||||
if _confirm "Disable GRUB Menu" \
|
|
||||||
"GRUB menu will be hidden entirely for fastest boot.\n\nTo access the menu on next boot, press and hold ESC\nimmediately after powering on.\n\nProceed?"; then
|
|
||||||
_apply_grub_setting 0 hidden 0 true
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
2)
|
|
||||||
_apply_grub_setting 3 menu 3 ""
|
|
||||||
;;
|
|
||||||
3)
|
|
||||||
_apply_grub_setting 5 menu 5 ""
|
|
||||||
;;
|
|
||||||
4)
|
|
||||||
local custom_timeout
|
|
||||||
custom_timeout=$(whiptail --title "Custom GRUB Timeout" \
|
|
||||||
--inputbox "Enter the GRUB timeout in seconds.\n\nUse -1 for indefinite wait." \
|
|
||||||
10 60 "" 3>&1 1>&2 2>&3 || true)
|
|
||||||
if [ -n "$custom_timeout" ]; then
|
|
||||||
if [[ "$custom_timeout" =~ ^-?[0-9]+$ ]]; then
|
|
||||||
_apply_grub_setting "$custom_timeout" menu "$custom_timeout" ""
|
|
||||||
else
|
|
||||||
_msg "Invalid Input" "Please enter a valid integer (e.g. 0, 5, -1)."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
5) return ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
rescue_boot() {
|
|
||||||
while true; do
|
|
||||||
local choice
|
|
||||||
choice=$(_menu "Boot Rescue + GRUB Configuration" \
|
|
||||||
"Select an operation:" \
|
|
||||||
$TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
|
||||||
"1" "GRUB Boot Menu Settings" \
|
|
||||||
"2" "Refirm Secure Boot (shim + GRUB)" \
|
|
||||||
"3" "Regenerate initramfs (all kernels)" \
|
|
||||||
"4" "Return to main menu")
|
|
||||||
|
|
||||||
[ -z "$choice" ] && return
|
|
||||||
clear
|
|
||||||
|
|
||||||
case "$choice" in
|
|
||||||
1) _grub_menu_settings ;;
|
|
||||||
2) _rescue_boot_sb ;;
|
|
||||||
3) _rescue_initramfs ;;
|
|
||||||
4) return ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
}
|
|
||||||
@@ -1,171 +1,30 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# sudo_config.sh — User Privileges & Feedback submenu
|
# Adds user to sudo group and optionally enables pwfeedback
|
||||||
# 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() {
|
config_sudo() {
|
||||||
echo -e "${YELLOW}User Privileges & Feedback${NC}"
|
echo -e "${YELLOW}Configuring sudo...${NC}"
|
||||||
|
|
||||||
while true; do
|
|
||||||
local choice
|
|
||||||
choice=$(_menu "User Privileges & Feedback" \
|
|
||||||
"Select an option:" $TUI_ALTO $TUI_ANCHO 6 \
|
|
||||||
"1" "Sudo Group Membership" \
|
|
||||||
"2" "Passwordless Sudo (maintenance commands)" \
|
|
||||||
"3" "Repair Home Directory Ownership" \
|
|
||||||
"4" "Sudo Password Feedback (asterisks)" \
|
|
||||||
"5" "Back to main menu")
|
|
||||||
|
|
||||||
[ -z "$choice" ] && return
|
if groups "$USER" | grep -qE '\bsudo\b'; then
|
||||||
clear
|
echo "User is already in the sudo group."
|
||||||
|
|
||||||
case "$choice" in
|
|
||||||
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."
|
|
||||||
else
|
else
|
||||||
if _confirm "Sudo Group" \
|
echo "Adding user '$USER' to sudo group..."
|
||||||
"User '$TARGET_USER' is NOT in the sudo group.\n\nAdd to sudo group?"; then
|
if sudo usermod -aG sudo "$USER"; then
|
||||||
if sudo usermod -aG sudo "$TARGET_USER"; then
|
echo -e "${GREEN}Done. Note: you need to log out and back in for group changes to take effect.${NC}"
|
||||||
_msg "Sudo Group" \
|
|
||||||
"User added to sudo group.\n\nLog out and back in for\ngroup changes to take effect." 10 60
|
|
||||||
else
|
|
||||||
_msg "Sudo Group" "Failed to add user to sudo group." 7 60
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── 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"
|
|
||||||
|
|
||||||
if [ -f "$nopasswd_file" ]; then
|
|
||||||
if _confirm "NOPASSWD" \
|
|
||||||
"Passwordless sudo is already configured.\n\nRemove it to restore password prompts?"; then
|
|
||||||
sudo rm -f "$nopasswd_file"
|
|
||||||
echo -e "${GREEN}Passwordless sudo removed.${NC}"
|
|
||||||
fi
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
if _confirm "NOPASSWD" \
|
|
||||||
"Configure passwordless sudo for maintenance commands?\n\n\
|
|
||||||
- apt / apt-get (package management)\n\
|
|
||||||
- systemctl (service management)\n\
|
|
||||||
- shutdown / reboot / halt (power commands)\n\n\
|
|
||||||
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 \
|
|
||||||
"systemctl" "Systemd service management" ON \
|
|
||||||
"power" "Shutdown, reboot, halt" ON)
|
|
||||||
clear
|
|
||||||
|
|
||||||
[ -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
|
|
||||||
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"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
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
|
else
|
||||||
|
echo -e "${RED}Failed to add user to sudo group.${NC}"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
|
||||||
|
|
||||||
# ── Option 3: Repair Home Directory Ownership ──
|
if _confirm "Sudo Password Feedback" "Show asterisks when typing the sudo password?"; then
|
||||||
_repair_home_ownership() {
|
echo 'Defaults pwfeedback' | sudo tee /etc/sudoers.d/pwfeedback > /dev/null
|
||||||
local home
|
if [ $? -eq 0 ]; then
|
||||||
home=$(getent passwd "$TARGET_USER" | cut -d: -f6)
|
echo -e "${GREEN}Password feedback enabled.${NC}"
|
||||||
|
else
|
||||||
if [ ! -d "$home" ]; then
|
echo -e "${RED}Failed to create /etc/sudoers.d/pwfeedback.${NC}"
|
||||||
_msg "Home Directory" "Home directory '$home' does not exist." 8 60
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
local uid uid_owner
|
|
||||||
uid=$(id -u "$TARGET_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
|
|
||||||
echo -e "${GREEN}Home directory ownership repaired.${NC}"
|
|
||||||
else
|
|
||||||
echo -e "${RED}Failed to repair home directory ownership.${NC}"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
_msg "Home Permissions" "Home directory ownership is correct\n(owner: $TARGET_USER)." 8 60
|
echo "Skipping password feedback setting."
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── Option 4: Sudo Password Feedback (pwfeedback) ──
|
|
||||||
_toggle_pwfeedback() {
|
|
||||||
local fb_file="/etc/sudoers.d/pwfeedback"
|
|
||||||
|
|
||||||
if [ -f "$fb_file" ]; then
|
|
||||||
if _confirm "Password Feedback" \
|
|
||||||
"Asterisks are currently ENABLED when typing sudo password.\n\nDisable them?"; then
|
|
||||||
sudo rm -f "$fb_file"
|
|
||||||
echo -e "${GREEN}Password feedback disabled.${NC}"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
if _confirm "Password Feedback" \
|
|
||||||
"Show asterisks when typing the sudo password?"; then
|
|
||||||
if _validate_sudoers 'Defaults pwfeedback' "$fb_file"; then
|
|
||||||
echo -e "${GREEN}Password feedback enabled.${NC}"
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,190 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# swap.sh — Disk-based swap management (swapfile + swappiness)
|
|
||||||
# Independent of zram.sh. Uses pri=10 to coexist with ZRAM priority=100.
|
|
||||||
|
|
||||||
readonly SWAP_FILE="/swapfile"
|
|
||||||
readonly SWAP_FSTAB_TAG="# debianito-managed-swap"
|
|
||||||
readonly SWAP_PRIORITY="10"
|
|
||||||
readonly SWAP_CONF="/etc/sysctl.d/99-swappiness-debianito.conf"
|
|
||||||
readonly SWAP_LOCK="/run/lock/debianito-swap.lock"
|
|
||||||
|
|
||||||
# ── Helpers ──
|
|
||||||
|
|
||||||
_swap_valid_fstab() {
|
|
||||||
local tmp="$1"
|
|
||||||
sudo findmnt --verify --fstab "$tmp" >/dev/null 2>&1
|
|
||||||
}
|
|
||||||
|
|
||||||
_swap_safe_write_fstab() {
|
|
||||||
local line="$1"
|
|
||||||
local tmp
|
|
||||||
tmp=$(mktemp) || return 1
|
|
||||||
sudo cp /etc/fstab "$tmp"
|
|
||||||
sudo sed -i "/$SWAP_FSTAB_TAG/d" "$tmp"
|
|
||||||
[ -n "$line" ] && printf '%s\n' "$line" >> "$tmp"
|
|
||||||
if _swap_valid_fstab "$tmp"; then
|
|
||||||
sudo install -o root -g root -m 0644 "$tmp" /etc/fstab
|
|
||||||
else
|
|
||||||
local err
|
|
||||||
err=$(sudo findmnt --verify --fstab "$tmp" 2>&1 || true)
|
|
||||||
rm -f "$tmp"
|
|
||||||
_msg "fstab Error" "Validation failed — changes NOT applied.\n\n$err" 12 70
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
rm -f "$tmp"
|
|
||||||
}
|
|
||||||
|
|
||||||
_swap_recommend_gb() {
|
|
||||||
if [ -z "${RAM_GB:-}" ]; then
|
|
||||||
echo 2
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
local ram
|
|
||||||
ram=$(echo "$RAM_GB" | awk '{print int($1)}')
|
|
||||||
if [ "$ram" -ge 16 ]; then
|
|
||||||
echo 2
|
|
||||||
elif [ "$ram" -ge 8 ]; then
|
|
||||||
echo 4
|
|
||||||
else
|
|
||||||
local rec
|
|
||||||
rec=$((ram * 2))
|
|
||||||
[ "$rec" -lt 1 ] && rec=1
|
|
||||||
echo "$rec"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── Menu options ──
|
|
||||||
|
|
||||||
_swap_current_status() {
|
|
||||||
local info="── Current swap ──\n"
|
|
||||||
info+="$(sudo swapon --show 2>/dev/null || echo '(none active)')\n\n"
|
|
||||||
info+="swappiness: $(cat /proc/sys/vm/swappiness)\n\n"
|
|
||||||
info+="fstab swap entries:\n"
|
|
||||||
info+="$(grep -E 'swap|SWAP' /etc/fstab 2>/dev/null || echo '(none in fstab)')"
|
|
||||||
_msg "Swap Status" "$info" 14 65
|
|
||||||
}
|
|
||||||
|
|
||||||
_swap_create_file() {
|
|
||||||
local fstype rec_gb
|
|
||||||
fstype=$(stat -f -c %T "$(dirname "$SWAP_FILE")" 2>/dev/null || echo "ext4")
|
|
||||||
rec_gb=$(_swap_recommend_gb)
|
|
||||||
|
|
||||||
local warn=""
|
|
||||||
if [ "$fstype" = "btrfs" ]; then
|
|
||||||
warn="Filesystem: Btrfs\nSwapfile on Btrfs needs extra steps (nodatacow).\nHibernation requires swap ≥ RAM and manual resume= config.\n\n"
|
|
||||||
fi
|
|
||||||
warn+="ZRAM (priority 100) is used first.\nDisk swap (priority $SWAP_PRIORITY) is only consumed after ZRAM fills."
|
|
||||||
|
|
||||||
_msg "Swapfile Info" "$warn" 14 65
|
|
||||||
|
|
||||||
local size_gb
|
|
||||||
size_gb=$(_inputbox "Swapfile Size" "Size in GB for $SWAP_FILE:" 10 60 "$rec_gb")
|
|
||||||
[ -z "$size_gb" ] && return
|
|
||||||
[[ "$size_gb" =~ ^[0-9]+$ ]] && [ "$size_gb" -gt 0 ] || {
|
|
||||||
_msg "Error" "Enter a positive integer." 8 40
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ -f "$SWAP_FILE" ]; then
|
|
||||||
_confirm "Swapfile exists" "$SWAP_FILE already exists.\nRecreate it (${size_gb} GB)?" || return
|
|
||||||
sudo swapoff "$SWAP_FILE" 2>/dev/null || true
|
|
||||||
sudo rm -f "$SWAP_FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "${YELLOW}Allocating ${size_gb}G swapfile...${NC}"
|
|
||||||
if [ "$fstype" = "btrfs" ]; then
|
|
||||||
sudo touch "$SWAP_FILE"
|
|
||||||
sudo chattr +C "$SWAP_FILE" 2>/dev/null || true
|
|
||||||
sudo dd if=/dev/zero of="$SWAP_FILE" bs=1M count=$((size_gb * 1024)) status=progress
|
|
||||||
else
|
|
||||||
sudo fallocate -l "${size_gb}G" "$SWAP_FILE" || \
|
|
||||||
sudo dd if=/dev/zero of="$SWAP_FILE" bs=1M count=$((size_gb * 1024)) status=progress
|
|
||||||
fi
|
|
||||||
|
|
||||||
sudo chmod 600 "$SWAP_FILE"
|
|
||||||
sudo mkswap "$SWAP_FILE" >/dev/null
|
|
||||||
sudo swapon "$SWAP_FILE"
|
|
||||||
|
|
||||||
local fstab_line
|
|
||||||
fstab_line="$SWAP_FILE none swap sw,pri=$SWAP_PRIORITY 0 0 $SWAP_FSTAB_TAG # pri=$SWAP_PRIORITY (below zram 100)"
|
|
||||||
if _swap_safe_write_fstab "$fstab_line"; then
|
|
||||||
_msg "Swapfile" "Swapfile created and added to fstab (persistent).\n\nPriority: $SWAP_PRIORITY (below ZRAM 100)." 12 65
|
|
||||||
else
|
|
||||||
sudo swapoff "$SWAP_FILE" 2>/dev/null || true
|
|
||||||
sudo rm -f "$SWAP_FILE"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_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" -eq 0 ] && [ ! -f "$SWAP_FILE" ] && {
|
|
||||||
_msg "Swap" "No managed swapfile found." 8 50
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_confirm "Remove Swapfile" "Disable and delete $SWAP_FILE?\n\nRemoves fstab entry and deletes the file." || return
|
|
||||||
|
|
||||||
sudo swapoff "$SWAP_FILE" 2>/dev/null || true
|
|
||||||
_swap_safe_write_fstab ""
|
|
||||||
sudo rm -f "$SWAP_FILE"
|
|
||||||
_msg "Swap" "Swapfile removed and fstab entry cleaned." 8 60
|
|
||||||
}
|
|
||||||
|
|
||||||
_swap_set_swappiness() {
|
|
||||||
local cur val
|
|
||||||
cur=$(cat /proc/sys/vm/swappiness 2>/dev/null || echo 60)
|
|
||||||
val=$(_inputbox "Swappiness" "Current value: $cur\nEnter new value (0-100):" 10 60 "$cur")
|
|
||||||
[ -z "$val" ] && return
|
|
||||||
[[ "$val" =~ ^[0-9]+$ ]] && [ "$val" -le 100 ] || {
|
|
||||||
_msg "Error" "Value must be 0-100." 8 40
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
local tmp
|
|
||||||
tmp=$(mktemp) || return
|
|
||||||
printf 'vm.swappiness=%s\n' "$val" > "$tmp"
|
|
||||||
sudo install -o root -g root -m 0644 "$tmp" "$SWAP_CONF"
|
|
||||||
sudo sysctl -w "vm.swappiness=$val" >/dev/null
|
|
||||||
rm -f "$tmp"
|
|
||||||
_msg "Swappiness" "swappiness set to $val (persistent)." 8 60
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── 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
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
while true; do
|
|
||||||
local choice
|
|
||||||
choice=$(_menu "Swap Management" \
|
|
||||||
"Select an option:" $TUI_ALTO $TUI_ANCHO 6 \
|
|
||||||
"1" "Show current swap & swappiness" \
|
|
||||||
"2" "Create / resize swapfile" \
|
|
||||||
"3" "Remove swapfile" \
|
|
||||||
"4" "Change swappiness" \
|
|
||||||
"5" "Back to main menu")
|
|
||||||
|
|
||||||
[ -z "$choice" ] && break
|
|
||||||
clear
|
|
||||||
|
|
||||||
case "$choice" in
|
|
||||||
1) _swap_current_status ;;
|
|
||||||
2) _swap_create_file ;;
|
|
||||||
3) _swap_remove_file ;;
|
|
||||||
4) _swap_set_swappiness ;;
|
|
||||||
5) break ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
exec 9>&-
|
|
||||||
}
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
#!/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 ──
|
|
||||||
msg+="OS: ${DEBIAN_VERSION} (${DEBIAN_CODENAME})\n"
|
|
||||||
msg+="Kernel: ${KERNEL_VERSION}\n"
|
|
||||||
msg+="Display: ${DISPLAY_SERVER}\n"
|
|
||||||
msg+="\n"
|
|
||||||
|
|
||||||
# ── Hardware Block ──
|
|
||||||
msg+="CPU: ${CPU_SUMMARY}\n"
|
|
||||||
msg+="RAM: ${RAM_SUMMARY}\n"
|
|
||||||
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
|
|
||||||
msg+="GPU: No GPU detected\n"
|
|
||||||
fi
|
|
||||||
msg+="\n"
|
|
||||||
|
|
||||||
# ── Network Block (cached by detect_network) ──
|
|
||||||
msg+="─── Network ───\n"
|
|
||||||
|
|
||||||
if ! command -v ip &>/dev/null; then
|
|
||||||
msg+="(install iproute2 for interface details)\n"
|
|
||||||
else
|
|
||||||
local i desc
|
|
||||||
local has_net=false
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# 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]}\""
|
|
||||||
msg+="\n"
|
|
||||||
else
|
|
||||||
msg+="${WIFI_NAMES[$i]}: ${desc}\n ↓\n"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
if ! $has_net && [ ${#ETH_DESCS[@]} -eq 0 ] && [ ${#WIFI_DESCS[@]} -eq 0 ]; then
|
|
||||||
msg+="No network interfaces detected\n"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ════════════════════════════════════════════════════════════
|
|
||||||
# Dynamic dimension calculation
|
|
||||||
# ════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
local term_cols
|
|
||||||
term_cols=$(tput cols 2>/dev/null || echo 80)
|
|
||||||
[ "$term_cols" -lt 50 ] && term_cols=50
|
|
||||||
|
|
||||||
local max_pct=$((term_cols * 95 / 100))
|
|
||||||
[ "$max_pct" -lt 50 ] && max_pct=50
|
|
||||||
|
|
||||||
local longest=0
|
|
||||||
while IFS= read -r line; do
|
|
||||||
local len=${#line}
|
|
||||||
[ "$len" -gt "$longest" ] && longest=$len
|
|
||||||
done < <(echo -e "$msg")
|
|
||||||
|
|
||||||
local width=$((longest + 6))
|
|
||||||
[ "$width" -lt 80 ] && width=80
|
|
||||||
[ "$width" -gt "$max_pct" ] && width=$max_pct
|
|
||||||
|
|
||||||
local truncated=""
|
|
||||||
while IFS= read -r line; do
|
|
||||||
if [ ${#line} -gt "$width" ]; then
|
|
||||||
truncated+="${line:0:$((width - 4))}...\\n"
|
|
||||||
else
|
|
||||||
truncated+="${line}\\n"
|
|
||||||
fi
|
|
||||||
done < <(echo -e "$msg")
|
|
||||||
|
|
||||||
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
|
|
||||||
[ "$height" -lt 10 ] && height=10
|
|
||||||
|
|
||||||
_msg "System Information" "$truncated" "$height" "$width"
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -1,146 +1,56 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# zram.sh — ZRAM submenu: view, create/reconfigure, remove
|
# zram.sh - Configure compressed swap in RAM
|
||||||
|
|
||||||
zram_menu() {
|
install_zram() {
|
||||||
# Hardware state is detected on demand; RAM_KB
|
echo -e "${YELLOW}ZRAM Configuration${NC}"
|
||||||
# and RAM_SUMMARY are populated by the battery.
|
|
||||||
_ensure_state_detected
|
|
||||||
|
|
||||||
while true; do
|
|
||||||
local choice
|
|
||||||
choice=$(_menu "ZRAM Configuration" \
|
|
||||||
"Select an operation:" $TUI_ALTO $TUI_ANCHO 4 \
|
|
||||||
"1" "View ZRAM status" \
|
|
||||||
"2" "Create / Reconfigure ZRAM" \
|
|
||||||
"3" "Remove ZRAM" \
|
|
||||||
"4" "Back to main menu")
|
|
||||||
|
|
||||||
[ -z "$choice" ] && break
|
|
||||||
clear
|
|
||||||
|
|
||||||
case "$choice" in
|
|
||||||
1) _zram_view ;;
|
|
||||||
2) _zram_create ;;
|
|
||||||
3) _zram_remove ;;
|
|
||||||
4) break ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
_zram_view() {
|
|
||||||
if ! is_installed "zram-tools"; then
|
|
||||||
_msg "ZRAM Status" "ZRAM is not installed.\n\nUse option 2 (Create / Reconfigure ZRAM)\nto set it up." 10 55
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
local algo="" size="" priority=""
|
|
||||||
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 ;;
|
|
||||||
esac
|
|
||||||
done </etc/default/zramswap
|
|
||||||
fi
|
|
||||||
|
|
||||||
local info="ZRAM Configuration:\n"
|
|
||||||
info+=" Algorithm: ${algo:-not set}\n"
|
|
||||||
info+=" Size: ${size:-not set} MB\n"
|
|
||||||
info+=" Priority: ${priority:-not set}\n\n"
|
|
||||||
info+="Active ZRAM devices:\n"
|
|
||||||
|
|
||||||
local zram_out
|
|
||||||
zram_out=$(sudo zramctl 2>/dev/null || true)
|
|
||||||
if [ -n "$zram_out" ]; then
|
|
||||||
info+="$zram_out"
|
|
||||||
else
|
|
||||||
info+=" (none — service may be stopped)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
_msg "ZRAM Status" "$info" 16 70
|
|
||||||
}
|
|
||||||
|
|
||||||
_zram_create() {
|
|
||||||
if [ -z "$RAM_KB" ] || [ "$RAM_KB" -eq 0 ]; then
|
if [ -z "$RAM_KB" ] || [ "$RAM_KB" -eq 0 ]; then
|
||||||
echo -e "${RED}Could not determine RAM size. Aborting.${NC}"
|
echo -e "${RED}Could not determine RAM size. Aborting.${NC}"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if is_installed "zram-tools"; then
|
local half_ram_mb=$((RAM_KB / 2 / 1024))
|
||||||
local cur_algo="" cur_size="" cur_prio=""
|
|
||||||
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 ;;
|
|
||||||
esac
|
|
||||||
done </etc/default/zramswap
|
|
||||||
fi
|
|
||||||
local cur="ZRAM is already configured:\n"
|
|
||||||
cur+=" Algorithm: ${cur_algo:-not set}\n"
|
|
||||||
cur+=" Size: ${cur_size:-not set} MB\n"
|
|
||||||
cur+=" Priority: ${cur_prio:-not set}\n\n"
|
|
||||||
cur+="Continuing will overwrite this configuration."
|
|
||||||
if ! _confirm "ZRAM — Reconfigure" "$cur"; then
|
|
||||||
echo "ZRAM reconfiguration cancelled."
|
|
||||||
return
|
|
||||||
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 algo
|
local algo
|
||||||
algo=$(_menu "ZRAM Configuration" \
|
algo=$(whiptail --title "ZRAM Compression" --menu \
|
||||||
"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:" \
|
"ZRAM compresses a portion of RAM into compressed swap,\
|
||||||
|
effectively increasing available memory.
|
||||||
|
|
||||||
|
Useful for systems with limited RAM or when you need
|
||||||
|
more swap space without disk writes.
|
||||||
|
|
||||||
|
Choose compression algorithm:" \
|
||||||
$TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
$TUI_ALTO $TUI_ANCHO $TUI_ALTO_LISTA \
|
||||||
"lz4" "Fastest compression. Lowest CPU overhead. (Default)" \
|
"lz4" "Fastest, low CPU (recommended)" \
|
||||||
"zstd" "Higher compression ratio. Saves more RAM, uses more CPU.")
|
"zstd" "Better ratio, more CPU" \
|
||||||
|
3>&1 1>&2 2>&3)
|
||||||
|
|
||||||
if [ -z "$algo" ]; then
|
if [ -z "$algo" ]; then
|
||||||
echo "ZRAM configuration cancelled."
|
echo "ZRAM configuration cancelled."
|
||||||
return
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local zram_size
|
local zram_size
|
||||||
if _confirm "ZRAM Size" "Use recommended size for ZRAM? (${recommended_mb} MB out of ${RAM_SUMMARY})"; then
|
if _confirm "ZRAM Size" "Use 50% of RAM for ZRAM? (${half_ram_mb} MB out of ${RAM_SUMMARY})"; then
|
||||||
zram_size=$recommended_mb
|
zram_size=$half_ram_mb
|
||||||
else
|
else
|
||||||
zram_size=$(_inputbox "ZRAM Size" "Enter ZRAM size in MB:" 8 60 "$recommended_mb")
|
zram_size=$(whiptail --title "ZRAM Size" --inputbox \
|
||||||
if [ -z "$zram_size" ] || ! [[ "$zram_size" =~ ^[0-9]+$ ]] || [ "$zram_size" -eq 0 ]; then
|
"Enter ZRAM size in MB:" 8 60 "$half_ram_mb" 3>&1 1>&2 2>&3)
|
||||||
|
if [ -z "$zram_size" ] || ! [[ "$zram_size" =~ ^[0-9]+$ ]]; then
|
||||||
echo "ZRAM configuration cancelled."
|
echo "ZRAM configuration cancelled."
|
||||||
return
|
return 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! _confirm "ZRAM Summary" "Algorithm: ${algo}\nSize: ${zram_size} MB\nPriority: 100\n\nApply?"; then
|
if ! _confirm "ZRAM Summary" "Algorithm: ${algo}\nSize: ${zram_size} MB\nPriority: 100\n\nApply?"; then
|
||||||
echo "ZRAM configuration cancelled."
|
echo "ZRAM configuration cancelled."
|
||||||
return
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_run_cmd "ZRAM" "sudo apt install -y zram-tools" "Installing zram-tools..."
|
_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..."
|
echo "Writing configuration..."
|
||||||
# SECURITY: Validate algo and size before writing to system file.
|
sudo tee /etc/default/zramswap > /dev/null <<EOF
|
||||||
[[ "$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
|
|
||||||
ALGO=$algo
|
ALGO=$algo
|
||||||
SIZE=$zram_size
|
SIZE=$zram_size
|
||||||
PRIORITY=100
|
PRIORITY=100
|
||||||
@@ -149,42 +59,10 @@ EOF
|
|||||||
echo "Restarting zramswap service..."
|
echo "Restarting zramswap service..."
|
||||||
sudo systemctl restart zramswap
|
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 ""
|
||||||
echo -e "${GREEN}ZRAM configured successfully.${NC}"
|
echo -e "${GREEN}ZRAM configured successfully.${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
sudo zramctl
|
sudo zramctl
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${GREEN}You can verify with: sudo zramctl${NC}"
|
echo -e "${GREEN}You can verify with: sudo zramctl${NC}"
|
||||||
_pause
|
|
||||||
}
|
|
||||||
|
|
||||||
_zram_remove() {
|
|
||||||
if ! is_installed "zram-tools"; then
|
|
||||||
_msg "ZRAM Remove" "ZRAM is not installed.\n\nNothing to remove." 10 50
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! _confirm "Remove ZRAM" \
|
|
||||||
"This will:\n - Stop the zramswap service\n - Purge zram-tools\n - Remove /etc/default/zramswap\n\nProceed?"; then
|
|
||||||
echo "ZRAM removal cancelled."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
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..."
|
|
||||||
sudo rm -f /etc/default/zramswap
|
|
||||||
|
|
||||||
_msg "ZRAM Removed" "ZRAM has been removed successfully.\n\nThe zramswap service is stopped and disabled." 10 55
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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" ]
|
|
||||||
}
|
|
||||||
@@ -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" ]
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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"* ]]
|
|
||||||
}
|
|
||||||