Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c35f49d9fc
|
||
|
|
37a2c49855
|
||
|
|
74803cd258
|
||
|
|
d0f88a3608
|
||
|
|
5889acd1e3
|
@@ -20,10 +20,12 @@ Dieses Repository ist ein modulares, automatisiertes Setup- und Konfigurations-F
|
|||||||
|
|
||||||
```
|
```
|
||||||
.
|
.
|
||||||
|
├── install.sh # Web-Installer & Bootstrap-Skript (curl | bash, klont nach /tmp)
|
||||||
├── setup.sh # Zentraler Orchestrator (CLI Flags & whiptail TUI)
|
├── setup.sh # Zentraler Orchestrator (CLI Flags & whiptail TUI)
|
||||||
├── lib/ # Wiederverwendbare Funktionsbibliotheken
|
├── lib/ # Wiederverwendbare Funktionsbibliotheken
|
||||||
│ ├── utils.sh # Logging, Error-Traps, Benutzer- und Pfadermittlung, Backups
|
│ ├── utils.sh # Logging, Error-Traps, Benutzer- und Pfadermittlung, Backups
|
||||||
│ ├── apt.sh # Deb822-Quellen, Keyrings (/etc/apt/keyrings), APT-Pinning, Paketinstallation
|
│ ├── apt.sh # Deb822-Quellen, Keyrings (/etc/apt/keyrings), APT-Pinning, Paketinstallation
|
||||||
|
│ ├── btrfs.sh # Btrfs Root- & Subvolume-Erkennung, Migration (@, @home), fstab-Verwaltung
|
||||||
│ └── tui.sh # Whiptail-TUI-Menüs und Dialoge
|
│ └── tui.sh # Whiptail-TUI-Menüs und Dialoge
|
||||||
├── config/ # Konfigurationsdateien & Paketlisten
|
├── config/ # Konfigurationsdateien & Paketlisten
|
||||||
│ ├── setup.conf # Globale Umgebungsvariablen und Standardwerte
|
│ ├── setup.conf # Globale Umgebungsvariablen und Standardwerte
|
||||||
@@ -132,6 +134,17 @@ Dieses Repository ist ein modulares, automatisiertes Setup- und Konfigurations-F
|
|||||||
- `install_apt_keyring "name" "url"`: Lädt einen GPG-Schlüssel herunter, de-armort ihn sauber und speichert ihn in `/etc/apt/keyrings/<name>.gpg`.
|
- `install_apt_keyring "name" "url"`: Lädt einen GPG-Schlüssel herunter, de-armort ihn sauber und speichert ihn in `/etc/apt/keyrings/<name>.gpg`.
|
||||||
- `install_packages_from_file "filepath"`: Liest eine `.list`-Datei ein (ignoriert Kommentare `#` und Leerzeilen) und installiert alle Pakete via `apt-get install -y`.
|
- `install_packages_from_file "filepath"`: Liest eine `.list`-Datei ein (ignoriert Kommentare `#` und Leerzeilen) und installiert alle Pakete via `apt-get install -y`.
|
||||||
|
|
||||||
|
### `lib/btrfs.sh`
|
||||||
|
- `is_root_btrfs`: Prüft, ob das Root-Dateisystem auf Btrfs liegt.
|
||||||
|
- `is_root_subvolume_configured`: Prüft, ob Root bereits in einem Subvolume (`@`) läuft.
|
||||||
|
- `update_btrfs_fstab "fstab_file" "root_id"`: Aktualisiert Mount-Optionen in `/etc/fstab` für `@` und `@home`.
|
||||||
|
- `check_and_setup_btrfs_subvolumes`: Erkennt flache Btrfs-Root-Volumes und migriert `/` und `/home` transparent in Subvolumes `@` und `@home`.
|
||||||
|
- `ensure_timeshift_btrfs_config`: Erstellt bzw. initialisiert die Timeshift-Btrfs-Konfiguration (`/etc/timeshift/timeshift.json`, automatische Boot-Snapshots, Retention auf 5 Snapshots) für die Root-UUID.
|
||||||
|
- `ensure_timeshift_boot_service`: Richtet den `timeshift-boot.service` und Cron-Einträge für automatische Boot-Snapshots ein.
|
||||||
|
- `ensure_timeshift_apt_hook`: Richtet einen DPkg/APT-Hook (`/etc/apt/apt.conf.d/80timeshift-auto-snapshot` und `/usr/local/bin/timeshift-apt-hook`) für automatische Snapshots vor System-Updates ein.
|
||||||
|
- `ensure_grub_btrfs`: Installiert und konfiguriert `grub-btrfs` sowie den `grub-btrfsd`-Dienst für die automatische Generierung von Snapshot-Bootmenüeinträgen in GRUB.
|
||||||
|
- `create_timeshift_snapshot "comment" [tags]`: Erstellt einen Timeshift-Btrfs-Snapshot an kritischen Setup-Meilensteinen (idempotent, steuerbar über `ENABLE_TIMESHIFT_SNAPSHOTS` in `config/setup.conf`).
|
||||||
|
|
||||||
### `lib/tui.sh`
|
### `lib/tui.sh`
|
||||||
- `tui_show_stage_selection`: Zeigt ein `whiptail` Checklist-Menü zur interaktiven Auswahl der auszuführenden Phasen.
|
- `tui_show_stage_selection`: Zeigt ein `whiptail` Checklist-Menü zur interaktiven Auswahl der auszuführenden Phasen.
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,22 @@ Ein modulares, sicheres und erweiterbares Framework zur automatisierten Einricht
|
|||||||
|
|
||||||
## 🚀 Schnellstart
|
## 🚀 Schnellstart
|
||||||
|
|
||||||
### 1. One-Liner (Klonen & Starten)
|
### 1. Web-Installer (Curl One-Liner)
|
||||||
Klonen des Repositories und direkter Start des Setups in einem einzigen Befehl:
|
Das Setup kann direkt ohne vorheriges manuelles Klonen via `curl` gestartet werden. Das Installationsskript (`install.sh`) installiert bei Bedarf `git`, klont das Repository automatisch in ein temporäres Verzeichnis (`/tmp`), startet den Orchestrator und räumt nach Abschluss sauber auf:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Interaktiver Modus (whiptail Menü)
|
||||||
|
curl -fsSL https://gitea.creative-dragonslayer.de/Scripts/Setup/raw/branch/main/install.sh | bash
|
||||||
|
|
||||||
|
# Vollautomatischer Modus (alle Stages 00 bis 10)
|
||||||
|
curl -fsSL https://gitea.creative-dragonslayer.de/Scripts/Setup/raw/branch/main/install.sh | bash -s -- --all
|
||||||
|
|
||||||
|
# Gezielte Modulauswahl (z. B. Paketlisten und Schriften)
|
||||||
|
curl -fsSL https://gitea.creative-dragonslayer.de/Scripts/Setup/raw/branch/main/install.sh | bash -s -- --stages 04,05
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Manuelles Klonen & Starten
|
||||||
|
Alternativ kann das Repository manuell geklont und ausgeführt werden:
|
||||||
```bash
|
```bash
|
||||||
git clone https://gitea.creative-dragonslayer.de/Scripts/Setup.git && cd Setup && sudo ./setup.sh
|
git clone https://gitea.creative-dragonslayer.de/Scripts/Setup.git && cd Setup && sudo ./setup.sh
|
||||||
```
|
```
|
||||||
@@ -16,25 +30,25 @@ Oder direkt vollautomatisch alle Stages (`00` bis `10`) ausführen:
|
|||||||
git clone https://gitea.creative-dragonslayer.de/Scripts/Setup.git && cd Setup && sudo ./setup.sh --all
|
git clone https://gitea.creative-dragonslayer.de/Scripts/Setup.git && cd Setup && sudo ./setup.sh --all
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Interaktiver TUI-Modus (Empfohlen)
|
### 3. Interaktiver TUI-Modus
|
||||||
Starte das grafische Terminal-Menü (`whiptail`) zur flexiblen Auswahl einzelner Module:
|
Starte das grafische Terminal-Menü (`whiptail`) zur flexiblen Auswahl einzelner Module:
|
||||||
```bash
|
```bash
|
||||||
sudo ./setup.sh
|
sudo ./setup.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Vollautomatischer CLI-Modus
|
### 4. Vollautomatischer CLI-Modus
|
||||||
Führe alle Phasen (`00` bis `10`) ohne Interaktion aus:
|
Führe alle Phasen (`00` bis `10`) ohne Interaktion aus:
|
||||||
```bash
|
```bash
|
||||||
sudo ./setup.sh --all
|
sudo ./setup.sh --all
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. Gezielte Modulauswahl
|
### 5. Gezielte Modulauswahl
|
||||||
Installiere nur bestimmte Phasen (z. B. nur Paketlisten und Schriften):
|
Installiere nur bestimmte Phasen (z. B. nur Paketlisten und Schriften):
|
||||||
```bash
|
```bash
|
||||||
sudo ./setup.sh --stages 04,05
|
sudo ./setup.sh --stages 04,05
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5. Simulationslauf (Dry-Run)
|
### 6. Simulationslauf (Dry-Run)
|
||||||
Überprüfe geplante Aktionen, ohne Änderungen am System vorzunehmen:
|
Überprüfe geplante Aktionen, ohne Änderungen am System vorzunehmen:
|
||||||
```bash
|
```bash
|
||||||
./setup.sh --dry-run --stages 01,02,04
|
./setup.sh --dry-run --stages 01,02,04
|
||||||
@@ -58,10 +72,12 @@ sudo ./setup.sh --stages 04,05
|
|||||||
|
|
||||||
```
|
```
|
||||||
.
|
.
|
||||||
|
├── install.sh # Web-Installer & Bootstrap-Skript (curl | bash, klont nach /tmp)
|
||||||
├── setup.sh # Zentraler Orchestrator (CLI Flags & whiptail TUI)
|
├── setup.sh # Zentraler Orchestrator (CLI Flags & whiptail TUI)
|
||||||
├── lib/ # Wiederverwendbare Hilfsbibliotheken
|
├── lib/ # Wiederverwendbare Hilfsbibliotheken
|
||||||
│ ├── utils.sh # Logging, Error-Traps (set -euo pipefail), Root/User-Erkennung & Abfrage
|
│ ├── utils.sh # Logging, Error-Traps (set -euo pipefail), Root/User-Erkennung & Abfrage
|
||||||
│ ├── apt.sh # APT-Keyring-Management (/etc/apt/keyrings/), Deb822, Pinning
|
│ ├── apt.sh # APT-Keyring-Management (/etc/apt/keyrings/), Deb822, Pinning
|
||||||
|
│ ├── btrfs.sh # Btrfs Root- & Subvolume-Erkennung, Migration (@, @home), Timeshift-Snapshots
|
||||||
│ └── tui.sh # Whiptail-Menüs und Dialoge
|
│ └── tui.sh # Whiptail-Menüs und Dialoge
|
||||||
├── config/ # Konfigurationsdateien & Paketlisten
|
├── config/ # Konfigurationsdateien & Paketlisten
|
||||||
│ ├── setup.conf # Globale Variablen (Kernel, GRUB-Theme, Plymouth)
|
│ ├── setup.conf # Globale Variablen (Kernel, GRUB-Theme, Plymouth)
|
||||||
@@ -105,9 +121,11 @@ sudo ./setup.sh --stages 04,05
|
|||||||
2. **Keine gefährlichen Login-Hooks**:
|
2. **Keine gefährlichen Login-Hooks**:
|
||||||
- Vollständige Beseitigung unkontrollierter Live-Downloads (`curl | sh`) beim Login.
|
- Vollständige Beseitigung unkontrollierter Live-Downloads (`curl | sh`) beim Login.
|
||||||
- Standardkonforme Bereitstellung über `/etc/skel` für neue und bestehende Benutzer.
|
- Standardkonforme Bereitstellung über `/etc/skel` für neue und bestehende Benutzer.
|
||||||
3. **Idempotenz & Backups**:
|
3. **Idempotenz, Backups & Timeshift Btrfs-Snapshots**:
|
||||||
- Alle Phasen können beliebig oft wiederholt werden.
|
- Alle Phasen können beliebig oft wiederholt werden.
|
||||||
- Vorhandene Dotfiles (`~/.zshrc`) und Konfigurationen (`/etc/default/grub`) werden vor Änderungen automatisch mit Zeitstempel (`.backup_YYYYMMDD_HHMMSS`) gesichert.
|
- Vorhandene Dotfiles (`~/.zshrc`) und Konfigurationen (`/etc/default/grub`) werden vor Änderungen automatisch mit Zeitstempel (`.backup_YYYYMMDD_HHMMSS`) gesichert.
|
||||||
|
- Auf Btrfs-Systemen werden mit Timeshift automatisch Rollback-Punkte vor und nach kritischen Operationen (Dist-Upgrade, Kernel-Installation, Paketinstallation, Desktop-Setup), bei jedem System-Update (automatischer APT/DPkg-Pre-Invoke-Hook) sowie bei jedem Systemstart (`timeshift-boot.service`) erstellt, wobei standardmäßig die letzten 5 Snapshots vorgehalten werden.
|
||||||
|
- Mittels `grub-btrfs` und dem Hintergrunddienst `grub-btrfsd` werden Btrfs-Snapshots automatisch in das GRUB-Bootmenü integriert, sodass direkt beim Systemstart in beliebige Snapshots gebootet werden kann.
|
||||||
4. **Debian Unstable Absicherung**:
|
4. **Debian Unstable Absicherung**:
|
||||||
- Moderne Deb822-Quellen und dedizierte Keyrings in `/etc/apt/keyrings/`.
|
- Moderne Deb822-Quellen und dedizierte Keyrings in `/etc/apt/keyrings/`.
|
||||||
- Installation von `apt-listbugs` und `apt-listchanges` vor dem `dist-upgrade` schützt vor bekannten kritischen Paketproblemen.
|
- Installation von `apt-listbugs` und `apt-listchanges` vor dem `dist-upgrade` schützt vor bekannten kritischen Paketproblemen.
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ gparted
|
|||||||
htop
|
htop
|
||||||
hunspell
|
hunspell
|
||||||
hunspell-de-de-frami
|
hunspell-de-de-frami
|
||||||
|
inotify-tools
|
||||||
irqbalance
|
irqbalance
|
||||||
keepassxc
|
keepassxc
|
||||||
lsd
|
lsd
|
||||||
|
|||||||
+9
-2
@@ -7,11 +7,18 @@
|
|||||||
XANMOD_BRANCH="main"
|
XANMOD_BRANCH="main"
|
||||||
CPU_PSTATE_AUTO=1
|
CPU_PSTATE_AUTO=1
|
||||||
|
|
||||||
|
# Backup & System Snapshots (Timeshift Btrfs)
|
||||||
|
ENABLE_TIMESHIFT_SNAPSHOTS=1
|
||||||
|
TIMESHIFT_COUNT_BOOT=5
|
||||||
|
TIMESHIFT_SCHEDULE_BOOT="true"
|
||||||
|
TIMESHIFT_SNAPSHOT_ON_UPDATE="true"
|
||||||
|
ENABLE_GRUB_BTRFS=1
|
||||||
|
|
||||||
# Bootloader & Appearance
|
# Bootloader & Appearance
|
||||||
GRUB_THEME="vimix"
|
GRUB_THEME="vimix"
|
||||||
PLYMOUTH_THEME="solar"
|
PLYMOUTH_THEME="solar"
|
||||||
GRUB_TIMEOUT=0
|
GRUB_TIMEOUT=5
|
||||||
GRUB_TIMEOUT_STYLE="hidden"
|
GRUB_TIMEOUT_STYLE="menu"
|
||||||
|
|
||||||
# Shell & Userland
|
# Shell & Userland
|
||||||
DEFAULT_SHELL="zsh"
|
DEFAULT_SHELL="zsh"
|
||||||
|
|||||||
Executable
+134
@@ -0,0 +1,134 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# install.sh - Web installer and bootstrap script for Debian Setup Framework
|
||||||
|
# ==============================================================================
|
||||||
|
# Usage:
|
||||||
|
# curl -fsSL https://gitea.creative-dragonslayer.de/Scripts/Setup/raw/branch/main/install.sh | bash
|
||||||
|
# curl -fsSL https://gitea.creative-dragonslayer.de/Scripts/Setup/raw/branch/main/install.sh | bash -s -- --all
|
||||||
|
# ==============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ANSI Color Codes
|
||||||
|
CLR_RESET='\033[0m'
|
||||||
|
CLR_BOLD='\033[1m'
|
||||||
|
CLR_RED='\033[0;31m'
|
||||||
|
CLR_GREEN='\033[0;32m'
|
||||||
|
CLR_YELLOW='\033[0;33m'
|
||||||
|
CLR_BLUE='\033[0;34m'
|
||||||
|
CLR_CYAN='\033[0;36m'
|
||||||
|
|
||||||
|
if [[ ! -t 1 ]]; then
|
||||||
|
CLR_RESET=""
|
||||||
|
CLR_BOLD=""
|
||||||
|
CLR_RED=""
|
||||||
|
CLR_GREEN=""
|
||||||
|
CLR_YELLOW=""
|
||||||
|
CLR_BLUE=""
|
||||||
|
CLR_CYAN=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info() {
|
||||||
|
printf "${CLR_BLUE}[INFO]${CLR_RESET} %s\n" "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
printf "${CLR_GREEN}[✓]${CLR_RESET} %s\n" "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warn() {
|
||||||
|
printf "${CLR_YELLOW}[WARN]${CLR_RESET} %s\n" "$*" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
printf "${CLR_RED}[ERROR]${CLR_RESET} %s\n" "$*" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
log_step() {
|
||||||
|
printf "\n${CLR_BOLD}${CLR_CYAN}==>${CLR_RESET} ${CLR_BOLD}%s${CLR_RESET}\n" "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
# If stdin is a pipe (e.g. curl ... | bash), reconnect to /dev/tty if available for interactive prompts & TUI
|
||||||
|
if [[ ! -t 0 ]]; then
|
||||||
|
if (exec < /dev/tty) 2>/dev/null; then
|
||||||
|
exec < /dev/tty
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
REPO_URL="${REPO_URL:-https://gitea.creative-dragonslayer.de/Scripts/Setup.git}"
|
||||||
|
REPO_BRANCH="${REPO_BRANCH:-}"
|
||||||
|
|
||||||
|
log_step "Debian Unstable & Hyprland Setup Framework Installer"
|
||||||
|
log_info "Repository: $REPO_URL ${REPO_BRANCH:+(Branch: $REPO_BRANCH)}"
|
||||||
|
|
||||||
|
# Ensure git is available
|
||||||
|
if ! command -v git >/dev/null 2>&1; then
|
||||||
|
log_info "Installing git..."
|
||||||
|
if [[ "$(id -u)" -eq 0 ]]; then
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get update -y
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends git ca-certificates
|
||||||
|
else
|
||||||
|
if ! command -v sudo >/dev/null 2>&1; then
|
||||||
|
log_error "sudo is required to install prerequisites. Please run as root or install sudo."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sudo DEBIAN_FRONTEND=noninteractive apt-get update -y
|
||||||
|
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends git ca-certificates
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create temporary directory
|
||||||
|
TMP_DIR="$(mktemp -d /tmp/debian-setup-XXXXXX)"
|
||||||
|
cleanup() {
|
||||||
|
if [[ -d "$TMP_DIR" ]]; then
|
||||||
|
log_info "Cleaning up temporary installation files..."
|
||||||
|
rm -rf "$TMP_DIR"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
|
log_info "Cloning repository into temporary directory: $TMP_DIR..."
|
||||||
|
CLONE_SUCCESS=0
|
||||||
|
if [[ -n "$REPO_BRANCH" ]]; then
|
||||||
|
if git clone --depth 1 -b "$REPO_BRANCH" "$REPO_URL" "$TMP_DIR" 2>/dev/null; then
|
||||||
|
CLONE_SUCCESS=1
|
||||||
|
else
|
||||||
|
log_warn "Failed to clone branch '$REPO_BRANCH', falling back to default branch..."
|
||||||
|
rm -rf "${TMP_DIR:?}"/* "${TMP_DIR:?}"/.* 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$CLONE_SUCCESS" -eq 0 ]]; then
|
||||||
|
if git clone --depth 1 "$REPO_URL" "$TMP_DIR"; then
|
||||||
|
CLONE_SUCCESS=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$CLONE_SUCCESS" -eq 0 ]]; then
|
||||||
|
log_error "Failed to clone repository from $REPO_URL"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$TMP_DIR"
|
||||||
|
chmod +x setup.sh
|
||||||
|
|
||||||
|
log_success "Repository cloned successfully. Starting setup orchestrator..."
|
||||||
|
|
||||||
|
# Check if dry-run flag is requested
|
||||||
|
is_dry_run=0
|
||||||
|
for arg in "$@"; do
|
||||||
|
if [[ "$arg" == "-d" || "$arg" == "--dry-run" ]]; then
|
||||||
|
is_dry_run=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Execute setup.sh with root privileges (or directly if dry-run) while preserving arguments
|
||||||
|
if [[ "$(id -u)" -eq 0 ]] || [[ "$is_dry_run" -eq 1 ]]; then
|
||||||
|
./setup.sh "$@"
|
||||||
|
else
|
||||||
|
if ! command -v sudo >/dev/null 2>&1; then
|
||||||
|
log_error "sudo is required to run setup.sh. Please install sudo or run as root."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sudo ./setup.sh "$@"
|
||||||
|
fi
|
||||||
+619
@@ -0,0 +1,619 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# lib/btrfs.sh - Btrfs root volume detection, subvolume management & migration
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
# shellcheck source=lib/utils.sh
|
||||||
|
source "$SCRIPT_DIR/lib/utils.sh"
|
||||||
|
|
||||||
|
# Check if the root filesystem is Btrfs
|
||||||
|
is_root_btrfs() {
|
||||||
|
local fstype
|
||||||
|
fstype="$(findmnt -n -o FSTYPE / 2>/dev/null || true)"
|
||||||
|
[[ "$fstype" == "btrfs" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Resolve the root block device (stripping any subvolume bracket suffix)
|
||||||
|
get_root_btrfs_device() {
|
||||||
|
local dev
|
||||||
|
dev="$(findmnt -n -o SOURCE --canonical / 2>/dev/null || findmnt -n -o SOURCE / 2>/dev/null || true)"
|
||||||
|
dev="${dev%%\[*}"
|
||||||
|
echo "$dev"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Resolve UUID of root filesystem
|
||||||
|
get_root_btrfs_uuid() {
|
||||||
|
local uuid dev
|
||||||
|
uuid="$(findmnt -n -o UUID / 2>/dev/null || true)"
|
||||||
|
if [[ -z "$uuid" ]]; then
|
||||||
|
dev="$(get_root_btrfs_device)"
|
||||||
|
if [[ -n "$dev" && -b "$dev" ]]; then
|
||||||
|
uuid="$(blkid -s UUID -o value "$dev" 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo "$uuid"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if /home is mounted on a separate filesystem / partition from /
|
||||||
|
is_home_separate_filesystem() {
|
||||||
|
local root_dev home_dev
|
||||||
|
root_dev="$(get_root_btrfs_device)"
|
||||||
|
home_dev="$(findmnt -n -o SOURCE --canonical /home 2>/dev/null || findmnt -n -o SOURCE /home 2>/dev/null || true)"
|
||||||
|
home_dev="${home_dev%%\[*}"
|
||||||
|
if [[ -n "$home_dev" && -n "$root_dev" && "$home_dev" != "$root_dev" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if root is already running from a dedicated subvolume (@ or @rootfs)
|
||||||
|
is_root_subvolume_configured() {
|
||||||
|
local fsroot opts
|
||||||
|
fsroot="$(findmnt -n -o FSROOT / 2>/dev/null || true)"
|
||||||
|
opts="$(findmnt -n -o OPTIONS / 2>/dev/null || true)"
|
||||||
|
if [[ "$fsroot" == "/@" || "$fsroot" == "/@rootfs" ]] || \
|
||||||
|
[[ "$opts" =~ subvol=@ || "$opts" =~ subvol=/@ ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Update /etc/fstab to mount / with subvol=@ and /home with subvol=@home
|
||||||
|
update_btrfs_fstab() {
|
||||||
|
local fstab_file="$1"
|
||||||
|
local root_id="$2"
|
||||||
|
|
||||||
|
[[ -f "$fstab_file" ]] || return 0
|
||||||
|
|
||||||
|
backup_file_or_dir "$fstab_file"
|
||||||
|
|
||||||
|
local tmp_fstab
|
||||||
|
tmp_fstab="$(mktemp)"
|
||||||
|
|
||||||
|
local found_root=0
|
||||||
|
local found_home=0
|
||||||
|
local root_spec="${root_id:-}"
|
||||||
|
local root_opts="defaults,subvol=@"
|
||||||
|
|
||||||
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||||
|
# Retain comments and blank lines
|
||||||
|
if [[ "$line" =~ ^[[:space:]]*# || -z "${line// /}" ]]; then
|
||||||
|
echo "$line" >> "$tmp_fstab"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Parse fields
|
||||||
|
local spec mnt type opts freq pass
|
||||||
|
read -r spec mnt type opts freq pass <<< "$line"
|
||||||
|
|
||||||
|
if [[ "$mnt" == "/" && "$type" == "btrfs" ]]; then
|
||||||
|
found_root=1
|
||||||
|
root_spec="${root_id:-$spec}"
|
||||||
|
local new_opts="$opts"
|
||||||
|
if [[ "$new_opts" =~ subvol= ]] || [[ "$new_opts" =~ subvolid= ]]; then
|
||||||
|
new_opts="$(echo "$new_opts" | sed -E 's/(subvol|subvolid)=[^,]*/subvol=@/g')"
|
||||||
|
else
|
||||||
|
new_opts="${new_opts},subvol=@"
|
||||||
|
fi
|
||||||
|
new_opts="$(echo "$new_opts" | sed -E -e 's/,,+/,/g' -e 's/^,//' -e 's/,$//')"
|
||||||
|
root_opts="$new_opts"
|
||||||
|
spec="$root_spec"
|
||||||
|
printf "%s\t%s\t%s\t%s\t%s\t%s\n" "$spec" "$mnt" "$type" "$new_opts" "${freq:-0}" "${pass:-0}" >> "$tmp_fstab"
|
||||||
|
elif [[ "$mnt" == "/home" && "$type" == "btrfs" ]]; then
|
||||||
|
found_home=1
|
||||||
|
local new_opts="$opts"
|
||||||
|
if [[ "$new_opts" =~ subvol= ]] || [[ "$new_opts" =~ subvolid= ]]; then
|
||||||
|
new_opts="$(echo "$new_opts" | sed -E 's/(subvol|subvolid)=[^,]*/subvol=@home/g')"
|
||||||
|
else
|
||||||
|
new_opts="${new_opts},subvol=@home"
|
||||||
|
fi
|
||||||
|
new_opts="$(echo "$new_opts" | sed -E -e 's/,,+/,/g' -e 's/^,//' -e 's/,$//')"
|
||||||
|
spec="${root_id:-$spec}"
|
||||||
|
printf "%s\t%s\t%s\t%s\t%s\t%s\n" "$spec" "$mnt" "$type" "$new_opts" "${freq:-0}" "${pass:-0}" >> "$tmp_fstab"
|
||||||
|
else
|
||||||
|
echo "$line" >> "$tmp_fstab"
|
||||||
|
fi
|
||||||
|
done < "$fstab_file"
|
||||||
|
|
||||||
|
# If home was not present in fstab and root was found/specified, add /home entry
|
||||||
|
if [[ "$found_home" -eq 0 && ( "$found_root" -eq 1 || -n "$root_spec" ) ]]; then
|
||||||
|
local home_opts="${root_opts/subvol=@/subvol=@home}"
|
||||||
|
if [[ "$home_opts" == "$root_opts" ]]; then
|
||||||
|
home_opts="defaults,subvol=@home"
|
||||||
|
fi
|
||||||
|
printf "%s\t%s\t%s\t%s\t%s\t%s\n" "$root_spec" "/home" "btrfs" "$home_opts" "0" "0" >> "$tmp_fstab"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! cat "$tmp_fstab" > "$fstab_file" 2>/dev/null; then
|
||||||
|
log_warn "Could not write to $fstab_file (permission denied)."
|
||||||
|
fi
|
||||||
|
rm -f "$tmp_fstab"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main function to check and configure Btrfs subvolumes for root and home
|
||||||
|
check_and_setup_btrfs_subvolumes() {
|
||||||
|
if ! is_root_btrfs; then
|
||||||
|
local fstype
|
||||||
|
fstype="$(findmnt -n -o FSTYPE / 2>/dev/null || echo "unknown")"
|
||||||
|
log_info "Root filesystem is not Btrfs ($fstype). Skipping Btrfs subvolume migration."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Btrfs root filesystem detected."
|
||||||
|
|
||||||
|
if is_root_subvolume_configured; then
|
||||||
|
local fsroot
|
||||||
|
fsroot="$(findmnt -n -o FSROOT / 2>/dev/null || true)"
|
||||||
|
log_success "Root filesystem is already running inside subvolume '$fsroot'."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure btrfs utility is available
|
||||||
|
if ! command_exists btrfs; then
|
||||||
|
log_info "Installing btrfs-progs..."
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get update -y >/dev/null 2>&1 || true
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends btrfs-progs || {
|
||||||
|
log_error "btrfs-progs could not be installed."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
local root_dev root_uuid
|
||||||
|
root_dev="$(get_root_btrfs_device)"
|
||||||
|
root_uuid="$(get_root_btrfs_uuid)"
|
||||||
|
|
||||||
|
if [[ -z "$root_dev" ]]; then
|
||||||
|
log_warn "Could not determine root block device for Btrfs. Skipping migration."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local root_fstab_id
|
||||||
|
if [[ -n "$root_uuid" ]]; then
|
||||||
|
root_fstab_id="UUID=$root_uuid"
|
||||||
|
else
|
||||||
|
root_fstab_id="$root_dev"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_step "Btrfs Migration: Creating subvolumes '@' and '@home'..."
|
||||||
|
|
||||||
|
local tmp_mnt
|
||||||
|
tmp_mnt="$(mktemp -d /tmp/btrfs-toplevel.XXXXXX)"
|
||||||
|
|
||||||
|
# Trap to safely unmount and remove temporary mountpoint
|
||||||
|
cleanup_tmp_mnt() {
|
||||||
|
if [[ -d "$tmp_mnt" ]]; then
|
||||||
|
if mountpoint -q "$tmp_mnt"; then
|
||||||
|
umount "$tmp_mnt" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
rm -rf "$tmp_mnt"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Mount top-level btrfs root (subvolid=5)
|
||||||
|
if ! mount -t btrfs -o subvolid=5 "$root_dev" "$tmp_mnt"; then
|
||||||
|
log_error "Failed to mount top-level Btrfs volume on $tmp_mnt."
|
||||||
|
rm -rf "$tmp_mnt"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 1. Create or snapshot '@' subvolume for root
|
||||||
|
if [[ ! -d "$tmp_mnt/@" ]]; then
|
||||||
|
log_info "Creating snapshot of top-level filesystem into '@' subvolume..."
|
||||||
|
btrfs subvolume snapshot "$tmp_mnt" "$tmp_mnt/@"
|
||||||
|
log_success "Created '@' subvolume snapshot."
|
||||||
|
else
|
||||||
|
log_info "'@' subvolume already exists in top-level Btrfs tree."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Create '@home' subvolume if /home is on the same volume
|
||||||
|
if ! is_home_separate_filesystem; then
|
||||||
|
if [[ ! -d "$tmp_mnt/@home" ]]; then
|
||||||
|
log_info "Creating '@home' subvolume..."
|
||||||
|
btrfs subvolume create "$tmp_mnt/@home"
|
||||||
|
log_success "Created '@home' subvolume."
|
||||||
|
|
||||||
|
# Move existing home data into @home
|
||||||
|
if [[ -d "$tmp_mnt/@/home" && "$(ls -A "$tmp_mnt/@/home" 2>/dev/null)" ]]; then
|
||||||
|
log_info "Moving existing /home data to '@home' subvolume..."
|
||||||
|
cp -a --reflink=auto "$tmp_mnt/@/home/." "$tmp_mnt/@home/"
|
||||||
|
# Clean up the directory inside @/home so it acts as an empty mountpoint
|
||||||
|
rm -rf "$tmp_mnt/@/home"/* "$tmp_mnt/@/home"/.[!.]* "$tmp_mnt/@/home"/..?* 2>/dev/null || true
|
||||||
|
chmod 755 "$tmp_mnt/@/home" 2>/dev/null || true
|
||||||
|
chown root:root "$tmp_mnt/@/home" 2>/dev/null || true
|
||||||
|
log_success "Moved /home data to '@home' subvolume."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_info "'@home' subvolume already exists in top-level Btrfs tree."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_info "/home is mounted on a separate filesystem. Skipping '@home' subvolume creation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Set default subvolume to '@'
|
||||||
|
local subvol_at_id
|
||||||
|
subvol_at_id="$(btrfs subvolume list "$tmp_mnt" 2>/dev/null | awk '$NF == "@" {print $2}' | tail -n1)"
|
||||||
|
if [[ -n "$subvol_at_id" ]]; then
|
||||||
|
btrfs subvolume set-default "$subvol_at_id" "$tmp_mnt"
|
||||||
|
log_success "Set default Btrfs subvolume to '@' (ID: $subvol_at_id)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. Update /etc/fstab (and the snapshotted @/etc/fstab)
|
||||||
|
log_info "Updating /etc/fstab entries for '@' and '@home'..."
|
||||||
|
update_btrfs_fstab "/etc/fstab" "$root_fstab_id"
|
||||||
|
if [[ -f "$tmp_mnt/@/etc/fstab" ]]; then
|
||||||
|
update_btrfs_fstab "$tmp_mnt/@/etc/fstab" "$root_fstab_id"
|
||||||
|
fi
|
||||||
|
log_success "Updated /etc/fstab with subvolume mount options."
|
||||||
|
|
||||||
|
# 5. Mount '@home' at /home for the current live session if not already mounted
|
||||||
|
if ! is_home_separate_filesystem; then
|
||||||
|
if ! findmnt /home >/dev/null 2>&1; then
|
||||||
|
log_info "Mounting '@home' subvolume at /home for current setup session..."
|
||||||
|
mount -t btrfs -o subvol=@home "$root_dev" /home || log_warn "Could not mount @home at /home live."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up temporary top-level mount
|
||||||
|
cleanup_tmp_mnt
|
||||||
|
|
||||||
|
log_success "Btrfs subvolume setup and migration completed successfully."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ensure Timeshift APT hook is configured for automatic snapshots on system updates
|
||||||
|
ensure_timeshift_apt_hook() {
|
||||||
|
local root_prefix="${ROOT_PREFIX:-}"
|
||||||
|
local snapshot_on_update="${TIMESHIFT_SNAPSHOT_ON_UPDATE:-true}"
|
||||||
|
local apt_conf_file="${root_prefix}/etc/apt/apt.conf.d/80timeshift-auto-snapshot"
|
||||||
|
local hook_script="${root_prefix}/usr/local/bin/timeshift-apt-hook"
|
||||||
|
|
||||||
|
# If disabled in configuration, remove hook if present
|
||||||
|
if [[ "$snapshot_on_update" != "1" && "$snapshot_on_update" != "true" ]]; then
|
||||||
|
if [[ -f "$apt_conf_file" || -f "$hook_script" ]]; then
|
||||||
|
log_info "Disabling Timeshift APT update snapshot hook..."
|
||||||
|
rm -f "$apt_conf_file" "$hook_script"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "${root_prefix}/usr/local/bin" "${root_prefix}/etc/apt/apt.conf.d"
|
||||||
|
|
||||||
|
# Create hook script
|
||||||
|
log_info "Configuring Timeshift APT update hook ($hook_script)..."
|
||||||
|
cat > "$hook_script" <<'EOF'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# /usr/local/bin/timeshift-apt-hook - Automatic Timeshift Snapshot on APT updates
|
||||||
|
# ==============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Check if timeshift binary is available
|
||||||
|
if ! command -v timeshift >/dev/null 2>&1; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if timeshift configuration exists
|
||||||
|
if [[ ! -f /etc/timeshift/timeshift.json && ! -f /etc/timeshift.json ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if root filesystem is Btrfs
|
||||||
|
if command -v findmnt >/dev/null 2>&1; then
|
||||||
|
if [[ "$(findmnt -n -o FSTYPE / 2>/dev/null || true)" != "btrfs" ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prevent duplicate snapshots within 60 seconds (rate limiting)
|
||||||
|
STAMP_FILE="/run/timeshift-apt-hook.stamp"
|
||||||
|
if [[ -f "$STAMP_FILE" ]]; then
|
||||||
|
LAST_RUN="$(stat -c %Y "$STAMP_FILE" 2>/dev/null || stat -f %m "$STAMP_FILE" 2>/dev/null || echo 0)"
|
||||||
|
CURRENT_TIME="$(date +%s)"
|
||||||
|
if (( CURRENT_TIME - LAST_RUN < 60 )); then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ">> [Timeshift] Creating automatic pre-update system snapshot..."
|
||||||
|
if timeshift --create --scripted --tags O --comments "Automatic snapshot before system update (APT)" 2>&1; then
|
||||||
|
touch "$STAMP_FILE" 2>/dev/null || true
|
||||||
|
echo ">> [Timeshift] Pre-update snapshot created successfully."
|
||||||
|
else
|
||||||
|
echo ">> [Timeshift] Warning: Automatic snapshot could not be created (continuing update)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
chmod 755 "$hook_script"
|
||||||
|
|
||||||
|
# Create APT configuration hook
|
||||||
|
cat > "$apt_conf_file" <<'EOF'
|
||||||
|
// Automatic Timeshift snapshot before APT package operations / updates
|
||||||
|
DPkg::Pre-Invoke { "[ -x /usr/local/bin/timeshift-apt-hook ] && /usr/local/bin/timeshift-apt-hook || true"; };
|
||||||
|
EOF
|
||||||
|
chmod 644 "$apt_conf_file"
|
||||||
|
|
||||||
|
log_success "Timeshift APT update hook configured: $apt_conf_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ensure Timeshift systemd boot service and cron jobs are configured and enabled
|
||||||
|
ensure_timeshift_boot_service() {
|
||||||
|
local root_prefix="${ROOT_PREFIX:-}"
|
||||||
|
local service_file="${root_prefix}/etc/systemd/system/timeshift-boot.service"
|
||||||
|
local timeshift_bin
|
||||||
|
timeshift_bin="$(command -v timeshift || echo "/usr/bin/timeshift")"
|
||||||
|
|
||||||
|
mkdir -p "${root_prefix}/etc/systemd/system"
|
||||||
|
|
||||||
|
local needs_service_update=0
|
||||||
|
if [[ ! -f "$service_file" ]]; then
|
||||||
|
needs_service_update=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$needs_service_update" -eq 1 ]]; then
|
||||||
|
log_info "Creating Timeshift boot snapshot service ($service_file)..."
|
||||||
|
cat > "$service_file" <<EOF
|
||||||
|
[Unit]
|
||||||
|
Description=Timeshift Boot Snapshot Service
|
||||||
|
Documentation=man:timeshift(1)
|
||||||
|
After=local-fs.target
|
||||||
|
ConditionPathExists=/etc/timeshift/timeshift.json
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=${timeshift_bin} --check --scripted
|
||||||
|
RemainAfterExit=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
chmod 644 "$service_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$root_prefix" ]] && command_exists systemctl; then
|
||||||
|
systemctl daemon-reload 2>/dev/null || true
|
||||||
|
systemctl enable timeshift-boot.service 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Also install cron entries if cron directory exists
|
||||||
|
if [[ -d "${root_prefix}/etc/cron.d" ]]; then
|
||||||
|
local cron_boot="${root_prefix}/etc/cron.d/timeshift-boot"
|
||||||
|
local cron_hourly="${root_prefix}/etc/cron.d/timeshift-hourly"
|
||||||
|
|
||||||
|
if [[ ! -f "$cron_boot" ]]; then
|
||||||
|
cat > "$cron_boot" <<EOF
|
||||||
|
SHELL=/bin/bash
|
||||||
|
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
|
||||||
|
MAILTO=""
|
||||||
|
@reboot root sleep 10m && ${timeshift_bin} --create --scripted --tags B
|
||||||
|
EOF
|
||||||
|
chmod 644 "$cron_boot"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$cron_hourly" ]]; then
|
||||||
|
cat > "$cron_hourly" <<EOF
|
||||||
|
SHELL=/bin/bash
|
||||||
|
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
|
||||||
|
MAILTO=""
|
||||||
|
0 * * * * root ${timeshift_bin} --check --scripted
|
||||||
|
EOF
|
||||||
|
chmod 644 "$cron_hourly"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ensure Timeshift is configured for Btrfs mode on the root filesystem
|
||||||
|
ensure_timeshift_btrfs_config() {
|
||||||
|
local root_prefix="${ROOT_PREFIX:-}"
|
||||||
|
local root_uuid
|
||||||
|
root_uuid="$(get_root_btrfs_uuid)"
|
||||||
|
|
||||||
|
mkdir -p "${root_prefix}/etc/timeshift"
|
||||||
|
|
||||||
|
local config_file="${root_prefix}/etc/timeshift/timeshift.json"
|
||||||
|
local count_boot="${TIMESHIFT_COUNT_BOOT:-5}"
|
||||||
|
local schedule_boot="${TIMESHIFT_SCHEDULE_BOOT:-true}"
|
||||||
|
local needs_update=0
|
||||||
|
|
||||||
|
if [[ ! -f "$config_file" ]]; then
|
||||||
|
needs_update=1
|
||||||
|
elif grep -q '"btrfs_mode"[[:space:]]*:[[:space:]]*"false"' "$config_file" 2>/dev/null; then
|
||||||
|
needs_update=1
|
||||||
|
elif grep -q '"backup_device_uuid"[[:space:]]*:[[:space:]]*""' "$config_file" 2>/dev/null && [[ -n "$root_uuid" ]]; then
|
||||||
|
needs_update=1
|
||||||
|
elif ! grep -q "\"schedule_boot\"[[:space:]]*:[[:space:]]*\"${schedule_boot}\"" "$config_file" 2>/dev/null; then
|
||||||
|
needs_update=1
|
||||||
|
elif ! grep -q "\"count_boot\"[[:space:]]*:[[:space:]]*\"${count_boot}\"" "$config_file" 2>/dev/null; then
|
||||||
|
needs_update=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$needs_update" -eq 1 ]]; then
|
||||||
|
log_info "Initializing Timeshift Btrfs configuration ($config_file)..."
|
||||||
|
backup_file_or_dir "$config_file"
|
||||||
|
cat > "$config_file" <<EOF
|
||||||
|
{
|
||||||
|
"backup_device_uuid" : "${root_uuid}",
|
||||||
|
"parent_device_uuid" : "",
|
||||||
|
"do_first_run" : "false",
|
||||||
|
"btrfs_mode" : "true",
|
||||||
|
"include_btrfs_home_for_backup" : "false",
|
||||||
|
"include_btrfs_home_for_restore" : "false",
|
||||||
|
"stop_cron_emails" : "true",
|
||||||
|
"schedule_monthly" : "false",
|
||||||
|
"schedule_weekly" : "false",
|
||||||
|
"schedule_daily" : "false",
|
||||||
|
"schedule_hourly" : "false",
|
||||||
|
"schedule_boot" : "${schedule_boot}",
|
||||||
|
"count_monthly" : "2",
|
||||||
|
"count_weekly" : "3",
|
||||||
|
"count_daily" : "5",
|
||||||
|
"count_hourly" : "6",
|
||||||
|
"count_boot" : "${count_boot}",
|
||||||
|
"snapshot_size" : "0",
|
||||||
|
"snapshot_count" : "0",
|
||||||
|
"exclude" : [
|
||||||
|
],
|
||||||
|
"exclude-apps" : [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
chmod 644 "$config_file"
|
||||||
|
if [[ ! -e "${root_prefix}/etc/timeshift.json" ]]; then
|
||||||
|
ln -sf /etc/timeshift/timeshift.json "${root_prefix}/etc/timeshift.json" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
ensure_timeshift_boot_service
|
||||||
|
ensure_timeshift_apt_hook
|
||||||
|
ensure_grub_btrfs
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ensure grub-btrfs is installed and configured to generate GRUB snapshot boot entries
|
||||||
|
ensure_grub_btrfs() {
|
||||||
|
local root_prefix="${ROOT_PREFIX:-}"
|
||||||
|
local enable_grub_btrfs="${ENABLE_GRUB_BTRFS:-1}"
|
||||||
|
|
||||||
|
# Check if disabled in configuration
|
||||||
|
if [[ "$enable_grub_btrfs" != "1" && "$enable_grub_btrfs" != "true" ]]; then
|
||||||
|
log_info "GRUB Btrfs snapshot booting disabled in configuration."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Dry-run check
|
||||||
|
if [[ "${FLAG_DRY_RUN:-0}" -eq 1 ]]; then
|
||||||
|
log_info "[DRY-RUN] Would install and configure grub-btrfs for GRUB snapshot booting."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if root is on Btrfs
|
||||||
|
if ! is_root_btrfs; then
|
||||||
|
log_info "Root filesystem is not Btrfs. Skipping grub-btrfs setup."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Configuring GRUB Btrfs snapshot support (grub-btrfs)..."
|
||||||
|
|
||||||
|
# Ensure required dependencies are installed
|
||||||
|
if ! command_exists inotifywait || ! command_exists git || ! command_exists make; then
|
||||||
|
log_info "Installing dependencies for grub-btrfs (inotify-tools, git, make)..."
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get update -y >/dev/null 2>&1 || true
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends inotify-tools git make 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
local repo_dir="${root_prefix}/usr/share/debian-gaming/grub/grub-btrfs"
|
||||||
|
mkdir -p "$(dirname "$repo_dir")"
|
||||||
|
|
||||||
|
if [[ ! -d "$repo_dir/.git" ]]; then
|
||||||
|
log_info "Cloning grub-btrfs repository..."
|
||||||
|
if ! git clone --depth 1 https://github.com/Antynea/grub-btrfs.git "$repo_dir" 2>/dev/null; then
|
||||||
|
log_warn "Failed to clone grub-btrfs repository. Snapshot boot menu may not be available."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_info "grub-btrfs repository already cloned; pulling latest updates..."
|
||||||
|
(cd "$repo_dir" && git pull 2>/dev/null) || log_warn "grub-btrfs git pull skipped."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install grub.d script
|
||||||
|
mkdir -p "${root_prefix}/etc/grub.d"
|
||||||
|
if [[ -f "$repo_dir/41_snapshots-btrfs" ]]; then
|
||||||
|
cp -f "$repo_dir/41_snapshots-btrfs" "${root_prefix}/etc/grub.d/41_snapshots-btrfs"
|
||||||
|
chmod 755 "${root_prefix}/etc/grub.d/41_snapshots-btrfs"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install default config
|
||||||
|
mkdir -p "${root_prefix}/etc/default/grub-btrfs"
|
||||||
|
if [[ -f "$repo_dir/config" && ! -f "${root_prefix}/etc/default/grub-btrfs/config" ]]; then
|
||||||
|
cp -f "$repo_dir/config" "${root_prefix}/etc/default/grub-btrfs/config"
|
||||||
|
chmod 644 "${root_prefix}/etc/default/grub-btrfs/config"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install grub-btrfsd binary
|
||||||
|
mkdir -p "${root_prefix}/usr/bin"
|
||||||
|
if [[ -f "$repo_dir/grub-btrfsd" ]]; then
|
||||||
|
cp -f "$repo_dir/grub-btrfsd" "${root_prefix}/usr/bin/grub-btrfsd"
|
||||||
|
chmod 755 "${root_prefix}/usr/bin/grub-btrfsd"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install and configure systemd service for automatic snapshot tracking with Timeshift
|
||||||
|
mkdir -p "${root_prefix}/etc/systemd/system"
|
||||||
|
local service_file="${root_prefix}/etc/systemd/system/grub-btrfsd.service"
|
||||||
|
cat > "$service_file" <<'EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=Regenerate grub-btrfs.cfg on snapshot changes
|
||||||
|
After=local-fs.target
|
||||||
|
ConditionPathExists=/etc/default/grub-btrfs/config
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
LogLevelMax=notice
|
||||||
|
Environment="PATH=/sbin:/bin:/usr/sbin:/usr/bin"
|
||||||
|
EnvironmentFile=-/etc/default/grub-btrfs/config
|
||||||
|
ExecStart=/usr/bin/grub-btrfsd --syslog --timeshift-auto
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5s
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
chmod 644 "$service_file"
|
||||||
|
|
||||||
|
if [[ -z "$root_prefix" ]] && command_exists systemctl; then
|
||||||
|
systemctl daemon-reload 2>/dev/null || true
|
||||||
|
systemctl enable grub-btrfsd.service 2>/dev/null || true
|
||||||
|
systemctl restart grub-btrfsd.service 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "grub-btrfs installed and configured successfully."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create a Btrfs snapshot using Timeshift
|
||||||
|
create_timeshift_snapshot() {
|
||||||
|
local comment="${1:-Setup Snapshot}"
|
||||||
|
local tags="${2:-O}"
|
||||||
|
|
||||||
|
# Check if disabled via configuration
|
||||||
|
if [[ "${ENABLE_TIMESHIFT_SNAPSHOTS:-1}" != "1" && "${ENABLE_TIMESHIFT_SNAPSHOTS:-1}" != "true" ]]; then
|
||||||
|
log_info "Timeshift snapshots disabled in configuration. Skipping: '$comment'"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Dry-run check
|
||||||
|
if [[ "${FLAG_DRY_RUN:-0}" -eq 1 ]]; then
|
||||||
|
log_info "[DRY-RUN] Would create Timeshift Btrfs snapshot: '$comment' (Tag: $tags)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if root is on Btrfs
|
||||||
|
if ! is_root_btrfs; then
|
||||||
|
log_info "Root filesystem is not Btrfs. Skipping Timeshift snapshot: '$comment'"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure timeshift is installed
|
||||||
|
if ! command_exists timeshift; then
|
||||||
|
log_info "Timeshift not found. Attempting to install timeshift..."
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get update -y >/dev/null 2>&1 || true
|
||||||
|
if ! DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends timeshift 2>/dev/null; then
|
||||||
|
log_warn "Timeshift is not available and could not be installed. Skipping snapshot: '$comment'"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure Timeshift configuration for Btrfs
|
||||||
|
ensure_timeshift_btrfs_config
|
||||||
|
|
||||||
|
log_substep "Creating Timeshift Btrfs snapshot: '$comment' (Tag: $tags)..."
|
||||||
|
|
||||||
|
local root_dev
|
||||||
|
root_dev="$(get_root_btrfs_device)"
|
||||||
|
|
||||||
|
if timeshift --create --scripted --comments "$comment" --tags "$tags" 2>/dev/null || \
|
||||||
|
([[ -n "$root_dev" ]] && timeshift --create --scripted --snapshot-device "$root_dev" --btrfs --comments "$comment" --tags "$tags" 2>/dev/null); then
|
||||||
|
log_success "Timeshift Btrfs snapshot created: '$comment'"
|
||||||
|
else
|
||||||
|
log_warn "Timeshift snapshot could not be created for '$comment' (non-fatal)."
|
||||||
|
fi
|
||||||
|
}
|
||||||
+12
-1
@@ -250,6 +250,10 @@ run_as_target_user() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
run_as_user() {
|
||||||
|
run_as_target_user "$@"
|
||||||
|
}
|
||||||
|
|
||||||
# Create a safe temporary directory with auto-cleanup
|
# Create a safe temporary directory with auto-cleanup
|
||||||
create_temp_dir() {
|
create_temp_dir() {
|
||||||
local prefix="${1:-setup-temp}"
|
local prefix="${1:-setup-temp}"
|
||||||
@@ -265,7 +269,14 @@ backup_file_or_dir() {
|
|||||||
timestamp="$(date +%Y%m%d_%H%M%S)"
|
timestamp="$(date +%Y%m%d_%H%M%S)"
|
||||||
if [[ -e "$target" ]]; then
|
if [[ -e "$target" ]]; then
|
||||||
local backup_path="${target}.backup_${timestamp}"
|
local backup_path="${target}.backup_${timestamp}"
|
||||||
cp -r "$target" "$backup_path"
|
if cp -r "$target" "$backup_path" 2>/dev/null; then
|
||||||
log_info "Created backup: $backup_path"
|
log_info "Created backup: $backup_path"
|
||||||
|
else
|
||||||
|
log_warn "Could not create backup of $target (permission denied)."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backup_file() {
|
||||||
|
backup_file_or_dir "$@"
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|||||||
# Source Libraries & Config
|
# Source Libraries & Config
|
||||||
source "$SCRIPT_DIR/lib/utils.sh"
|
source "$SCRIPT_DIR/lib/utils.sh"
|
||||||
source "$SCRIPT_DIR/lib/apt.sh"
|
source "$SCRIPT_DIR/lib/apt.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/btrfs.sh"
|
||||||
source "$SCRIPT_DIR/lib/tui.sh"
|
source "$SCRIPT_DIR/lib/tui.sh"
|
||||||
|
|
||||||
if [[ -f "$SCRIPT_DIR/config/setup.conf" ]]; then
|
if [[ -f "$SCRIPT_DIR/config/setup.conf" ]]; then
|
||||||
@@ -58,6 +59,7 @@ EOF
|
|||||||
FLAG_ALL=0
|
FLAG_ALL=0
|
||||||
FLAG_STAGES=""
|
FLAG_STAGES=""
|
||||||
FLAG_DRY_RUN=0
|
FLAG_DRY_RUN=0
|
||||||
|
export FLAG_DRY_RUN
|
||||||
CLI_USER=""
|
CLI_USER=""
|
||||||
|
|
||||||
# Parse CLI arguments
|
# Parse CLI arguments
|
||||||
@@ -183,6 +185,9 @@ if [[ "$FLAG_DRY_RUN" -eq 1 ]]; then
|
|||||||
log_warn "DRY-RUN MODE: No changes will be made to the system."
|
log_warn "DRY-RUN MODE: No changes will be made to the system."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Create pre-setup baseline Timeshift snapshot if on Btrfs
|
||||||
|
create_timeshift_snapshot "Pre-Setup Baseline: Before executing stages (${SELECTED_STAGES[*]})" "B"
|
||||||
|
|
||||||
# Execute Selected Stages
|
# Execute Selected Stages
|
||||||
for stage in "${SELECTED_STAGES[@]}"; do
|
for stage in "${SELECTED_STAGES[@]}"; do
|
||||||
# Skip 00 if it was manually added since it already ran
|
# Skip 00 if it was manually added since it already ran
|
||||||
@@ -207,4 +212,7 @@ for stage in "${SELECTED_STAGES[@]}"; do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Create final completion Timeshift snapshot if on Btrfs
|
||||||
|
create_timeshift_snapshot "Post-Setup Final: All stages completed successfully (${SELECTED_STAGES[*]})" "O"
|
||||||
|
|
||||||
log_step "All selected setup stages completed successfully!"
|
log_step "All selected setup stages completed successfully!"
|
||||||
|
|||||||
@@ -7,6 +7,12 @@ set -euo pipefail
|
|||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
source "$SCRIPT_DIR/lib/utils.sh"
|
source "$SCRIPT_DIR/lib/utils.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/btrfs.sh"
|
||||||
|
|
||||||
|
if [[ -f "$SCRIPT_DIR/config/setup.conf" ]]; then
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source "$SCRIPT_DIR/config/setup.conf"
|
||||||
|
fi
|
||||||
|
|
||||||
setup_err_trap
|
setup_err_trap
|
||||||
|
|
||||||
@@ -67,4 +73,11 @@ else
|
|||||||
log_success "Sufficient disk space available ($(( AVAILABLE_KB / 1024 / 1024 )) GB free)."
|
log_success "Sufficient disk space available ($(( AVAILABLE_KB / 1024 / 1024 )) GB free)."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# 6. Check & Setup Btrfs Subvolumes (@, @home)
|
||||||
|
log_substep "Checking Btrfs root volume & subvolume layout..."
|
||||||
|
check_and_setup_btrfs_subvolumes
|
||||||
|
|
||||||
|
# 7. Create Timeshift Btrfs Snapshot after preflight & subvolume setup
|
||||||
|
create_timeshift_snapshot "Stage 00: Pre-flight checks & Btrfs baseline" "B"
|
||||||
|
|
||||||
log_success "Stage 00: Pre-flight checks passed successfully."
|
log_success "Stage 00: Pre-flight checks passed successfully."
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ set -euo pipefail
|
|||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
source "$SCRIPT_DIR/lib/utils.sh"
|
source "$SCRIPT_DIR/lib/utils.sh"
|
||||||
source "$SCRIPT_DIR/lib/apt.sh"
|
source "$SCRIPT_DIR/lib/apt.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/btrfs.sh"
|
||||||
|
|
||||||
|
if [[ -f "$SCRIPT_DIR/config/setup.conf" ]]; then
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source "$SCRIPT_DIR/config/setup.conf"
|
||||||
|
fi
|
||||||
|
|
||||||
setup_err_trap
|
setup_err_trap
|
||||||
|
|
||||||
@@ -70,6 +76,8 @@ DEBIAN_FRONTEND=noninteractive apt-get install -y apt-listbugs apt-listchanges
|
|||||||
|
|
||||||
# 7. Perform Full Dist-Upgrade
|
# 7. Perform Full Dist-Upgrade
|
||||||
log_substep "Performing full distribution upgrade to Debian Sid..."
|
log_substep "Performing full distribution upgrade to Debian Sid..."
|
||||||
|
create_timeshift_snapshot "Stage 01: Pre-dist-upgrade to Debian Sid" "B"
|
||||||
apt_dist_upgrade
|
apt_dist_upgrade
|
||||||
|
create_timeshift_snapshot "Stage 01: Post-dist-upgrade to Debian Sid" "O"
|
||||||
|
|
||||||
log_success "Stage 01: Debian Unstable migration and upgrade completed successfully!"
|
log_success "Stage 01: Debian Unstable migration and upgrade completed successfully!"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ set -euo pipefail
|
|||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
source "$SCRIPT_DIR/lib/utils.sh"
|
source "$SCRIPT_DIR/lib/utils.sh"
|
||||||
source "$SCRIPT_DIR/lib/apt.sh"
|
source "$SCRIPT_DIR/lib/apt.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/btrfs.sh"
|
||||||
|
|
||||||
if [[ -f "$SCRIPT_DIR/config/setup.conf" ]]; then
|
if [[ -f "$SCRIPT_DIR/config/setup.conf" ]]; then
|
||||||
# shellcheck source=/dev/null
|
# shellcheck source=/dev/null
|
||||||
@@ -33,6 +34,7 @@ apt_update
|
|||||||
|
|
||||||
# 2. Determine XanMod Kernel Package
|
# 2. Determine XanMod Kernel Package
|
||||||
log_substep "Detecting CPU microarchitecture level and XanMod branch..."
|
log_substep "Detecting CPU microarchitecture level and XanMod branch..."
|
||||||
|
create_timeshift_snapshot "Stage 02: Pre-XanMod kernel installation" "B"
|
||||||
BRANCH="${XANMOD_BRANCH:-main}"
|
BRANCH="${XANMOD_BRANCH:-main}"
|
||||||
log_info "Configured XanMod branch: $BRANCH"
|
log_info "Configured XanMod branch: $BRANCH"
|
||||||
|
|
||||||
@@ -118,4 +120,6 @@ if compgen -G "$SCRIPT_DIR/data/etc/environment.d/*.conf" > /dev/null; then
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
create_timeshift_snapshot "Stage 02: Post-kernel and hardware tuning" "O"
|
||||||
|
|
||||||
log_success "Stage 02: Kernel and Hardware Tuning completed successfully!"
|
log_success "Stage 02: Kernel and Hardware Tuning completed successfully!"
|
||||||
|
|||||||
+13
-3
@@ -7,6 +7,7 @@ set -euo pipefail
|
|||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
source "$SCRIPT_DIR/lib/utils.sh"
|
source "$SCRIPT_DIR/lib/utils.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/btrfs.sh"
|
||||||
|
|
||||||
if [[ -f "$SCRIPT_DIR/config/setup.conf" ]]; then
|
if [[ -f "$SCRIPT_DIR/config/setup.conf" ]]; then
|
||||||
# shellcheck source=/dev/null
|
# shellcheck source=/dev/null
|
||||||
@@ -20,8 +21,8 @@ log_step "Running Stage 03: Bootloader & Splash Screen Configuration"
|
|||||||
require_root
|
require_root
|
||||||
|
|
||||||
GRUB_CFG="/etc/default/grub"
|
GRUB_CFG="/etc/default/grub"
|
||||||
GRUB_TIMEOUT="${GRUB_TIMEOUT:-0}"
|
GRUB_TIMEOUT="${GRUB_TIMEOUT:-5}"
|
||||||
GRUB_TIMEOUT_STYLE="${GRUB_TIMEOUT_STYLE:-hidden}"
|
GRUB_TIMEOUT_STYLE="${GRUB_TIMEOUT_STYLE:-menu}"
|
||||||
GRUB_CMDLINE_LINUX_DEFAULT_BASE="quiet splash apparmor=1 usbcore.autosuspend=-1 vt.global_cursor_default=0 loglevel=3 rd.luks.options=discard plymouth.ignore-serial-consoles threadirqs"
|
GRUB_CMDLINE_LINUX_DEFAULT_BASE="quiet splash apparmor=1 usbcore.autosuspend=-1 vt.global_cursor_default=0 loglevel=3 rd.luks.options=discard plymouth.ignore-serial-consoles threadirqs"
|
||||||
GRUB_CMDLINE_LINUX="rcutree.rcu_idle_gp_delay=1"
|
GRUB_CMDLINE_LINUX="rcutree.rcu_idle_gp_delay=1"
|
||||||
GRUB_THEME_DIR="/usr/share/debian-gaming/grub/grub2-themes"
|
GRUB_THEME_DIR="/usr/share/debian-gaming/grub/grub2-themes"
|
||||||
@@ -100,7 +101,16 @@ else
|
|||||||
(cd "$GRUB_THEME_DIR" && git pull 2>/dev/null && ./install.sh -b -t "${GRUB_THEME:-vimix}" 2>/dev/null) || log_warn "Vimix theme update skipped."
|
(cd "$GRUB_THEME_DIR" && git pull 2>/dev/null && ./install.sh -b -t "${GRUB_THEME:-vimix}" 2>/dev/null) || log_warn "Vimix theme update skipped."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 5. Update GRUB & Initramfs
|
# 5. Configure GRUB Btrfs Snapshot Booting (grub-btrfs)
|
||||||
|
log_substep "Configuring GRUB Btrfs snapshot booting support..."
|
||||||
|
if is_root_btrfs; then
|
||||||
|
ensure_grub_btrfs
|
||||||
|
log_success "GRUB Btrfs snapshot booting configured."
|
||||||
|
else
|
||||||
|
log_info "Root filesystem is not Btrfs. Skipping grub-btrfs setup."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 6. Update GRUB & Initramfs
|
||||||
log_substep "Running update-grub and update-initramfs..."
|
log_substep "Running update-grub and update-initramfs..."
|
||||||
if command_exists update-grub; then
|
if command_exists update-grub; then
|
||||||
update-grub
|
update-grub
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ set -euo pipefail
|
|||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
source "$SCRIPT_DIR/lib/utils.sh"
|
source "$SCRIPT_DIR/lib/utils.sh"
|
||||||
source "$SCRIPT_DIR/lib/apt.sh"
|
source "$SCRIPT_DIR/lib/apt.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/btrfs.sh"
|
||||||
|
|
||||||
|
if [[ -f "$SCRIPT_DIR/config/setup.conf" ]]; then
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source "$SCRIPT_DIR/config/setup.conf"
|
||||||
|
fi
|
||||||
|
|
||||||
setup_err_trap
|
setup_err_trap
|
||||||
|
|
||||||
@@ -22,6 +28,8 @@ if [[ ! -d "$PACKAGES_DIR" ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
create_timeshift_snapshot "Stage 04: Pre-thematic package installation" "B"
|
||||||
|
|
||||||
# List of APT package lists to process (excluding flatpaks which are handled in stage 08)
|
# List of APT package lists to process (excluding flatpaks which are handled in stage 08)
|
||||||
APT_LISTS=(
|
APT_LISTS=(
|
||||||
"01-base.list"
|
"01-base.list"
|
||||||
@@ -47,4 +55,6 @@ for list_name in "${APT_LISTS[@]}"; do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
create_timeshift_snapshot "Stage 04: Post-thematic package installation" "O"
|
||||||
|
|
||||||
log_success "Stage 04: Thematic package installation completed successfully!"
|
log_success "Stage 04: Thematic package installation completed successfully!"
|
||||||
|
|||||||
+13
-1
@@ -8,6 +8,7 @@ set -euo pipefail
|
|||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
source "$SCRIPT_DIR/lib/utils.sh"
|
source "$SCRIPT_DIR/lib/utils.sh"
|
||||||
source "$SCRIPT_DIR/lib/apt.sh"
|
source "$SCRIPT_DIR/lib/apt.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/btrfs.sh"
|
||||||
|
|
||||||
if [[ -f "$SCRIPT_DIR/config/setup.conf" ]]; then
|
if [[ -f "$SCRIPT_DIR/config/setup.conf" ]]; then
|
||||||
# shellcheck source=/dev/null
|
# shellcheck source=/dev/null
|
||||||
@@ -135,7 +136,18 @@ if command_exists systemctl; then
|
|||||||
log_success "Ollama systemd service configured and enabled."
|
log_success "Ollama systemd service configured and enabled."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 6. User Group Memberships
|
# 6. Timeshift Boot Snapshot Service, APT Update Hook & GRUB Btrfs Daemon (if on Btrfs)
|
||||||
|
log_substep "Configuring Timeshift Boot Snapshot Service, APT Update Hook and GRUB Btrfs Snapshot Daemon..."
|
||||||
|
if command_exists timeshift && is_root_btrfs; then
|
||||||
|
ensure_timeshift_btrfs_config
|
||||||
|
ensure_grub_btrfs
|
||||||
|
log_success "Timeshift and GRUB Btrfs snapshot services configured and enabled."
|
||||||
|
elif is_root_btrfs; then
|
||||||
|
ensure_grub_btrfs
|
||||||
|
log_info "Timeshift will be configured when installed; grub-btrfs configured."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 7. User Group Memberships
|
||||||
log_substep "Updating user group memberships for $TARGET_USER..."
|
log_substep "Updating user group memberships for $TARGET_USER..."
|
||||||
groupadd -f sudo
|
groupadd -f sudo
|
||||||
groupadd -f docker
|
groupadd -f docker
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ set -euo pipefail
|
|||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
# shellcheck source=lib/utils.sh
|
# shellcheck source=lib/utils.sh
|
||||||
source "$SCRIPT_DIR/lib/utils.sh"
|
source "$SCRIPT_DIR/lib/utils.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/btrfs.sh"
|
||||||
|
|
||||||
|
if [[ -f "$SCRIPT_DIR/config/setup.conf" ]]; then
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source "$SCRIPT_DIR/config/setup.conf"
|
||||||
|
fi
|
||||||
|
|
||||||
setup_err_trap
|
setup_err_trap
|
||||||
|
|
||||||
@@ -22,6 +28,7 @@ log_info "Configuring Hyprland Desktop for target user: $TARGET_USER ($TARGET_HO
|
|||||||
|
|
||||||
# 1. Execute LinuxBeginnings Debian-Hyprland installer
|
# 1. Execute LinuxBeginnings Debian-Hyprland installer
|
||||||
log_substep "Downloading and executing LinuxBeginnings Debian-Hyprland installer..."
|
log_substep "Downloading and executing LinuxBeginnings Debian-Hyprland installer..."
|
||||||
|
create_timeshift_snapshot "Stage 10: Pre-Hyprland desktop installation" "B"
|
||||||
if command_exists curl; then
|
if command_exists curl; then
|
||||||
log_info "Running Debian-Hyprland installer as target user '$TARGET_USER'..."
|
log_info "Running Debian-Hyprland installer as target user '$TARGET_USER'..."
|
||||||
if run_as_target_user bash -c 'sh <(curl -L https://raw.githubusercontent.com/LinuxBeginnings/Debian-Hyprland/main/auto-install.sh)'; then
|
if run_as_target_user bash -c 'sh <(curl -L https://raw.githubusercontent.com/LinuxBeginnings/Debian-Hyprland/main/auto-install.sh)'; then
|
||||||
@@ -68,4 +75,7 @@ log_success "Rudimentary Hyprland configuration deployed:"
|
|||||||
log_info " - SUPER+ENTER: Kitty Terminal"
|
log_info " - SUPER+ENTER: Kitty Terminal"
|
||||||
log_info " - SUPER+Q: Close Active Window"
|
log_info " - SUPER+Q: Close Active Window"
|
||||||
log_info " - Login Prompt: Asks user after login to choose Dotfiles setup & installs JetBrains Junie CLI + AwesomeJunieSkills"
|
log_info " - Login Prompt: Asks user after login to choose Dotfiles setup & installs JetBrains Junie CLI + AwesomeJunieSkills"
|
||||||
|
|
||||||
|
create_timeshift_snapshot "Stage 10: Post-Hyprland desktop setup" "O"
|
||||||
|
|
||||||
log_success "Stage 10: Hyprland Desktop installation & configuration completed successfully!"
|
log_success "Stage 10: Hyprland Desktop installation & configuration completed successfully!"
|
||||||
|
|||||||
Reference in New Issue
Block a user