Compare commits
160
Commits
arch
..
7296489c11
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7296489c11
|
||
|
|
7932bb2482
|
||
|
|
0cc710f997
|
||
|
|
fec46ef46f
|
||
|
|
287e4d815b
|
||
|
|
c1f3851086
|
||
|
|
0bf2e81063
|
||
|
|
950eb5d361
|
||
|
|
9278241dd0
|
||
|
|
1fa3770e6e
|
||
|
|
775b03e9de
|
||
|
|
d9ec12dba3
|
||
|
|
1723803e9c
|
||
|
|
2e470d676d
|
||
|
|
8761e1c673
|
||
|
|
5a8dd73d32
|
||
|
|
116a33a876
|
||
|
|
e87342d3b7
|
||
|
|
c219988011
|
||
|
|
174014cae0
|
||
|
|
2f02c1b74a
|
||
|
|
f093cce7dc
|
||
|
|
85408020c6
|
||
|
|
c35f49d9fc
|
||
|
|
37a2c49855
|
||
|
|
74803cd258
|
||
|
|
d0f88a3608
|
||
|
|
5889acd1e3
|
||
|
|
a949899168
|
||
|
|
20d4952740
|
||
|
|
579489fa9c
|
||
|
|
d69534d286
|
||
|
|
6cd6e789ba
|
||
|
|
8e36e4ce0d
|
||
|
|
4afce9cdb1
|
||
|
|
bb3e42a8e3
|
||
|
|
c4241d7015
|
||
|
|
28319183b6
|
||
|
|
04a68df15c
|
||
|
|
3c1601fc2f
|
||
|
|
d424b5eed7
|
||
|
|
90dd188555
|
||
|
|
24d8ba379f
|
||
|
|
ad7b31a67a
|
||
|
|
b237de1fc5
|
||
|
|
6e303b041f
|
||
|
|
3788422797
|
||
|
|
10aec5458a
|
||
|
|
4feb815d97
|
||
|
|
279a137132
|
||
|
|
fe02dd23c6
|
||
|
|
8a5c4094ba
|
||
|
|
9b0dce0d84
|
||
|
|
8d2e4b3b0e
|
||
|
|
f810149f95
|
||
|
|
f32e1ca6d4
|
||
|
|
da0367a43c
|
||
|
|
3a9cf54b2f
|
||
|
|
4f87611c66
|
||
|
|
ace5cfee5c
|
||
|
|
858b3a4cb5
|
||
|
|
b92c49048b
|
||
|
|
394746f658
|
||
|
|
d86a1dde3d
|
||
|
|
22b4ca5c9e
|
||
|
|
c48ebeda9c
|
||
|
|
b74436b7c0
|
||
|
|
13a40d5ac5
|
||
|
|
f1ba8513fc
|
||
|
|
3d06340314
|
||
|
|
c2611debfa
|
||
|
|
9cb077e4fb
|
||
|
|
5c4b9ad08d
|
||
|
|
5bc8227773
|
||
|
|
0c0b6da0ea
|
||
|
|
7c2ca5b6b2
|
||
|
|
4b0047484d
|
||
|
|
d5b1da9745
|
||
|
|
dc8c4c9062
|
||
|
|
aa68e0e1f7
|
||
|
|
bff9822c86
|
||
|
|
1aa3a164ab
|
||
|
|
742cee14f1
|
||
|
|
532d34a6ae
|
||
|
|
61034975db
|
||
|
|
d4de7d0056
|
||
|
|
93f0757f49
|
||
|
|
697196c99b
|
||
|
|
a3630b9d28
|
||
|
|
176240f3b7
|
||
|
|
7af43e0850
|
||
|
|
5b3fd6848d
|
||
|
|
ef08a0221b
|
||
|
|
29948841cd
|
||
|
|
cf24a033da
|
||
|
|
4516129bec
|
||
|
|
7176a8d796
|
||
|
|
01e7b63c7f
|
||
|
|
934dbd1ea6
|
||
|
|
fc6385d400
|
||
|
|
6fdd50341c
|
||
|
|
aab24c5d1d
|
||
|
|
5c4097bb6e
|
||
|
|
6885535b72 | ||
|
|
0c9cc435d5 | ||
|
|
1b1be169cf | ||
|
|
07fe48a33e | ||
|
|
13da1e980b | ||
|
|
b13a0831fa | ||
|
|
f60be958f6 | ||
|
|
b8c856a7d3 | ||
|
|
94996e1bb8 | ||
|
|
2481f515b8 | ||
|
|
39b9de3443 | ||
|
|
eca438f7cb | ||
|
|
ddf8585a16 | ||
|
|
cb89d422e7 | ||
|
|
8c1b7168bc | ||
|
|
f8cbcc1cc6 | ||
|
|
36e56b67b7 | ||
|
|
7c4db527cc | ||
|
|
a12f135d3f | ||
|
|
12240a0ec5 | ||
|
|
4a77bb10d6 | ||
|
|
fbea2fef38 | ||
|
|
6fbc4ff9d0 | ||
|
|
6f67a5b034 | ||
|
|
9b7f391cb0 | ||
|
|
7290ffdc4a | ||
|
|
1dfd42dd1a | ||
|
|
82f23a9651 | ||
|
|
c1873b34bf | ||
|
|
b8efa35307 | ||
|
|
778c845db1 | ||
|
|
bf976533ba | ||
|
|
504c1f0494 | ||
|
|
5f6851c65e | ||
|
|
9d6ed39b5e | ||
|
|
468b900561 | ||
|
|
f0ee6a0dce | ||
|
|
ddc142da78 | ||
|
|
33d5c78cd5 | ||
|
|
0aeb0fd221 | ||
|
|
9fdb2c4982 | ||
|
|
21115bbcd1 | ||
|
|
3f1a3fb554 | ||
|
|
d2579c8f45 | ||
|
|
70d6d09c65 | ||
|
|
23494013c8 | ||
|
|
173f9ecbe4 | ||
|
|
2c70d0a709 | ||
|
|
0cf512fc4f | ||
|
|
7f7431029f | ||
|
|
cef7039f29 | ||
|
|
338393580a | ||
|
|
60191445bc | ||
|
|
9c1e62dfe8 | ||
|
|
6fbc98bc98 | ||
|
|
f9c892baee | ||
|
|
51a7a9a927 |
+15
-2
@@ -77,5 +77,18 @@ fabric.properties
|
|||||||
# Android studio 3.1+ serialized cache file
|
# Android studio 3.1+ serialized cache file
|
||||||
.idea/caches/build_file_checksums.ser
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
.fleet
|
# ---> VisualStudioCode
|
||||||
.idea
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
!.vscode/*.code-snippets
|
||||||
|
|
||||||
|
# Local History for Visual Studio Code
|
||||||
|
.history/
|
||||||
|
|
||||||
|
# Built Visual Studio Code Extensions
|
||||||
|
*.vsix
|
||||||
|
|
||||||
|
.junie/plans
|
||||||
|
|||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
-221
@@ -1,221 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Root-Rechte prüfen
|
|
||||||
if [ "$(id -u)" -ne 0 ]; then
|
|
||||||
echo "❌ Bitte als root ausführen!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# System aktualisieren und benötigte Pakete installieren
|
|
||||||
pacman -Syu --noconfirm git sudo p7zip unrar unzip networkmanager tree bluez bluez-utils wget curl less ineturils
|
|
||||||
|
|
||||||
# Aktivieren und Starten von NetworkManager und Bluetooth-Diensten
|
|
||||||
systemctl enable --now NetworkManager
|
|
||||||
systemctl enable --now bluetooth
|
|
||||||
|
|
||||||
# yay Installation
|
|
||||||
read -p "❓ Soll der Paketmanager Yay für AUR-Pakete installiert werden? (j/n) [n]: " answer
|
|
||||||
answer=${answer,,}
|
|
||||||
answer=${answer:-n}
|
|
||||||
|
|
||||||
if [[ "$answer" == "j" ]] || [[ "$answer" == "y" ]]; then
|
|
||||||
# Tatsächlichen Benutzer ermitteln (nicht root)
|
|
||||||
if [[ -z "$SUDO_USER" || "$SUDO_USER" == "root" ]]; then
|
|
||||||
echo "❌ Fehler: Das Skript muss mit 'sudo' von einem normalen Benutzer ausgeführt werden."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
CURRENT_DIR=$(pwd)
|
|
||||||
USER_NAME="$SUDO_USER"
|
|
||||||
AUR_CODE_DIR="/var/lib/user_aur_code"
|
|
||||||
|
|
||||||
# Sicherstellen, dass benötigte Pakete installiert sind
|
|
||||||
pacman -Syu --noconfirm glib2 git base-devel --needed
|
|
||||||
|
|
||||||
# AUR-Verzeichnis erstellen
|
|
||||||
mkdir -p $AUR_CODE_DIR
|
|
||||||
sudo chmod -R 777 $AUR_CODE_DIR
|
|
||||||
|
|
||||||
# Installiere yay
|
|
||||||
cd $AUR_CODE_DIR
|
|
||||||
sudo -u $USER_NAME git clone https://aur.archlinux.org/yay.git
|
|
||||||
cd yay
|
|
||||||
sudo -u $USER_NAME makepkg -sic --noconfirm || { echo "❌ Fehler beim Erstellen von Yay"; exit 1; }
|
|
||||||
sudo -u $USER_NAME git clean -dfx
|
|
||||||
|
|
||||||
cd $CURRENT_DIR
|
|
||||||
fi
|
|
||||||
|
|
||||||
# multilib Repo
|
|
||||||
read -p "❓ Soll das multilib-Repository aktiviert werden? (j/n) [n]: " answer
|
|
||||||
answer=${answer,,}
|
|
||||||
answer=${answer:-n}
|
|
||||||
|
|
||||||
if [[ "$answer" == "j" ]] || [[ "$answer" == "y" ]]; then
|
|
||||||
# Multilib-Repository aktivieren
|
|
||||||
echo "🔄 Aktiviere Multilib-Repository..."
|
|
||||||
# Zeilen einkommentieren, falls sie auskommentiert sind
|
|
||||||
if grep -q "^\[multilib\]" /etc/pacman.conf && grep -q "#Include = /etc/pacman.d/mirrorlist" /etc/pacman.conf; then
|
|
||||||
sudo sed -i 's/#\[multilib\]/\[multilib\]/g' /etc/pacman.conf
|
|
||||||
sudo sed -i 's/#Include = \/etc\/pacman.d\/mirrorlist/Include = \/etc\/pacman.d\/mirrorlist/g' /etc/pacman.conf
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Falls der Block nicht vorhanden ist, füge ihn hinzu
|
|
||||||
if ! grep -q "^\[multilib\]" /etc/pacman.conf; then
|
|
||||||
echo "[multilib]" | sudo tee -a /etc/pacman.conf
|
|
||||||
echo "Include = /etc/pacman.d/mirrorlist" | sudo tee -a /etc/pacman.conf
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Paketdatenbank aktualisieren
|
|
||||||
echo "🔄 Aktualisiere die Paketdatenbank..."
|
|
||||||
pacman -Sy --noconfirm
|
|
||||||
fi
|
|
||||||
|
|
||||||
# systemd-boot Config
|
|
||||||
read -p "❓ Soll systemd-boot so angepasst werden, dass es nur im Fehlerfall angezeigt wird? (j/n) [n]: " answer
|
|
||||||
answer=${answer,,}
|
|
||||||
answer=${answer:-n}
|
|
||||||
|
|
||||||
if [[ "$answer" == "j" ]] || [[ "$answer" == "y" ]]; then
|
|
||||||
LOADER_CONF="/boot/loader/loader.conf"
|
|
||||||
|
|
||||||
# Überprüfen, ob die Datei exists
|
|
||||||
if [[ ! -f "$LOADER_CONF" ]]; then
|
|
||||||
echo "❌ Datei $LOADER_CONF nicht gefunden!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Überprüfen, ob die "timeout"-Option bereits auf 0 gesetzt ist
|
|
||||||
if grep -q "^timeout 0" "$LOADER_CONF"; then
|
|
||||||
echo "ℹ️ 'timeout 0' ist bereits gesetzt."
|
|
||||||
else
|
|
||||||
# Timeout auf 0 setzen
|
|
||||||
echo "🔄 Setze 'timeout 0' in der loader.conf..."
|
|
||||||
# Falls bereits eine timeout-Zeile existiert, diese ersetzen
|
|
||||||
sed -i 's/^timeout .*/timeout 0/' "$LOADER_CONF"
|
|
||||||
# Falls keine timeout-Zeile existiert, diese hinzufügen
|
|
||||||
if ! grep -q "^timeout" "$LOADER_CONF"; then
|
|
||||||
echo "timeout 0" >>"$LOADER_CONF"
|
|
||||||
fi
|
|
||||||
echo "✅ 'timeout 0' wurde erfolgreich gesetzt."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Gruppe "sudo" und Sudoers-Regel hinzufügen
|
|
||||||
read -p "❓ Soll die Gruppe 'sudo' erstellt und ihr Sudo-Zugriff gewährt werden? (j/n) [n]: " answer
|
|
||||||
answer=${answer,,}
|
|
||||||
answer=${answer:-n}
|
|
||||||
|
|
||||||
if [[ "$answer" == "j" ]] || [[ "$answer" == "y" ]]; then
|
|
||||||
# Gruppe "sudo" erstellen, falls sie nicht existiert
|
|
||||||
if ! grep -q "^sudo:" /etc/group; then
|
|
||||||
groupadd sudo
|
|
||||||
echo "ℹ️ Gruppe 'sudo' wurde erstellt."
|
|
||||||
else
|
|
||||||
echo "ℹ️ Gruppe 'sudo' existiert bereits."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Sudoers-Regel für die Gruppe "sudo" erstellen
|
|
||||||
SUDOERS_FILE="/etc/sudoers.d/00-sudo-group"
|
|
||||||
if [ ! -f "$SUDOERS_FILE" ]; then
|
|
||||||
echo "%sudo ALL=(ALL:ALL) ALL" >"$SUDOERS_FILE"
|
|
||||||
chmod 440 "$SUDOERS_FILE"
|
|
||||||
echo "ℹ️ Sudo-Regel für die Gruppe 'sudo' wurde erstellt."
|
|
||||||
else
|
|
||||||
echo "ℹ️ Sudo-Regel für die Gruppe 'sudo' existiert bereits."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Abfrage, ob ein Benutzer der Gruppe hinzugefügt werden soll
|
|
||||||
read -p "❓ Welcher Benutzer soll zur Gruppe 'sudo' hinzugefügt werden? (Leer lassen für keinen): " username
|
|
||||||
if [[ -n "$username" ]]; then
|
|
||||||
if id "$username" &>/dev/null; then
|
|
||||||
usermod -aG sudo "$username"
|
|
||||||
echo "ℹ️ Benutzer '$username' wurde zur Gruppe 'sudo' hinzugefügt."
|
|
||||||
else
|
|
||||||
echo "❌ Benutzer '$username' existiert nicht!"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# sbin in PATH aufnehmen
|
|
||||||
read -p "❓ Soll sbin für sudo-Nutzer in den PATH aufgenommen werden? (j/n) [n]: " answer
|
|
||||||
answer=${answer,,}
|
|
||||||
answer=${answer:-n}
|
|
||||||
|
|
||||||
if [[ "$answer" == "j" ]] || [[ "$answer" == "y" ]]; then
|
|
||||||
cat << 'EOF' >/etc/profile.d/sbin_in_path.sh
|
|
||||||
if groups | grep -q "\bsudo\b"; then
|
|
||||||
case ":$PATH:" in
|
|
||||||
*":/sbin:"*) ;;
|
|
||||||
*) export PATH="$PATH:/sbin" ;;
|
|
||||||
esac
|
|
||||||
case ":$PATH:" in
|
|
||||||
*":/usr/sbin:"*) ;;
|
|
||||||
*) export PATH="$PATH:/usr/sbin" ;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo "ℹ️ sbin wurde zum PATH für sudo-Nutzer hinzugefügt."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# sudo-Hinweis
|
|
||||||
read -p "❓ Soll ein sudo-Hinweis hinzugefügt werden? (j/n) [n]: " answer
|
|
||||||
answer=${answer,,}
|
|
||||||
answer=${answer:-n}
|
|
||||||
|
|
||||||
if [[ "$answer" == "j" ]] || [[ "$answer" == "y" ]]; then
|
|
||||||
cat << 'EOF' >/etc/profile.d/sudo_hint.sh
|
|
||||||
if [ ! -e "$HOME/.sudo_as_admin_successful" ] && [ ! -e "$HOME/.hushlogin" ] ; then
|
|
||||||
case " $(groups) " in *\ sudo\ *)
|
|
||||||
if [ -x /usr/bin/sudo ]; then
|
|
||||||
echo 'To run a command as administrator (user "root"), use "sudo <command>".'
|
|
||||||
echo 'See "man sudo_root" for details.'
|
|
||||||
fi
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo "ℹ️ sudo-Hinweis wurde hinzugefügt!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# XDG-Data-Dirs setzen
|
|
||||||
read -p "❓ Sollen die XDG-Data-Dirs gesetzt werden? (j/n) [n]: " answer
|
|
||||||
answer=${answer,,}
|
|
||||||
answer=${answer:-n}
|
|
||||||
|
|
||||||
if [[ "$answer" == "j" ]] || [[ "$answer" == "y" ]]; then
|
|
||||||
cat << 'EOF' >/etc/profile.d/xdg_dirs_desktop_session.sh
|
|
||||||
# /etc/profile.d/desktop_session_xdg_dirs.sh - Prepend a $DESKTOP_SESSION-named directory to $XDG_CONFIG_DIRS and $XDG_DATA_DIRS
|
|
||||||
|
|
||||||
DEFAULT_XDG_CONFIG_DIRS="/etc/xdg"
|
|
||||||
DEFAULT_XDG_DATA_DIRS="/usr/local/share/:/usr/share/"
|
|
||||||
|
|
||||||
if [ -n "$DESKTOP_SESSION" ]; then
|
|
||||||
# readd default if was empty
|
|
||||||
if [ -z "$XDG_CONFIG_DIRS" ]; then
|
|
||||||
XDG_CONFIG_DIRS="$DEFAULT_XDG_CONFIG_DIRS"
|
|
||||||
fi
|
|
||||||
if [ -n "${XDG_CONFIG_DIRS##*$DEFAULT_XDG_CONFIG_DIRS/xdg-$DESKTOP_SESSION*}" ]; then
|
|
||||||
XDG_CONFIG_DIRS="$DEFAULT_XDG_CONFIG_DIRS"/xdg-"$DESKTOP_SESSION":"$XDG_CONFIG_DIRS"
|
|
||||||
fi
|
|
||||||
export XDG_CONFIG_DIRS
|
|
||||||
# gnome is already added if gnome-session installed
|
|
||||||
if [ "$DESKTOP_SESSION" != "gnome" ]; then
|
|
||||||
if [ -z "$XDG_DATA_DIRS" ]; then
|
|
||||||
XDG_DATA_DIRS="$DEFAULT_XDG_DATA_DIRS"
|
|
||||||
fi
|
|
||||||
if [ -n "${XDG_DATA_DIRS##*/usr/share/$DESKTOP_SESSION*}" ]; then
|
|
||||||
XDG_DATA_DIRS=/usr/share/"$DESKTOP_SESSION":"$XDG_DATA_DIRS"
|
|
||||||
fi
|
|
||||||
export XDG_DATA_DIRS
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo "ℹ️ XDG-Data-Dirs wurden gesetzt!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
chmod +x /etc/profile.d/*
|
|
||||||
|
|
||||||
echo "✅ Abgeschlossen. Zum Anwenden der Änderungen bitte neu einloggen!"
|
|
||||||
-75
@@ -1,75 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Root-Rechte prüfen
|
|
||||||
if [ "$(id -u)" -eq 0 ]; then
|
|
||||||
echo "❌ Bitte nicht als root ausführen! Das Skript nutzt sudo, falls nötig."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
yay -Syu
|
|
||||||
|
|
||||||
# Installiere adcli mit yay
|
|
||||||
yay -S --noconfirm adcli || { echo "❌ Fehler beim Installieren von adcli"; exit 1; }
|
|
||||||
|
|
||||||
# Installiere realmd mit yay
|
|
||||||
yay -S --noconfirm realmd || { echo "❌ Fehler beim Installieren von realmd"; exit 1; }
|
|
||||||
|
|
||||||
# Samba-Verzeichnis erstellen
|
|
||||||
sudo mkdir -p /var/lib/samba/private/
|
|
||||||
|
|
||||||
# Installiere notwendige Pakete
|
|
||||||
sudo pacman -Syu --noconfirm sssd curl ntp polkit
|
|
||||||
|
|
||||||
echo "❓ Daten für Domainjoin:"
|
|
||||||
read -p "Domänenname: " DOMAIN
|
|
||||||
read -p "Adminuser: " ADMIN
|
|
||||||
|
|
||||||
# NetBIOS und COMS extrahieren
|
|
||||||
NetBios=$(echo "$DOMAIN" | cut -d '.' -f1)
|
|
||||||
coms=$(echo "$DOMAIN" | cut -d '.' -f2)
|
|
||||||
|
|
||||||
# Realm joinen
|
|
||||||
sudo realm join --verbose --user="$ADMIN" "$DOMAIN" --install=/ || { echo "❌ Fehler beim Domainjoin"; exit 1; }
|
|
||||||
|
|
||||||
# SSSD aktivieren und starten
|
|
||||||
sudo systemctl enable sssd
|
|
||||||
sudo systemctl start sssd
|
|
||||||
|
|
||||||
# PAM-Konfiguration überprüfen und anpassen
|
|
||||||
homedir=$(grep homedir /etc/pam.d/common-session | grep 0077 | cut -d '=' -f3 | head -1)
|
|
||||||
if [ "$homedir" = "0077" ]; then
|
|
||||||
echo "ℹ️ pam_mkhomedir.so konfiguriert"
|
|
||||||
sleep 1
|
|
||||||
else
|
|
||||||
echo "session required pam_mkhomedir.so skel=/etc/skel/ umask=0077" | sudo tee -a /etc/pam.d/common-session
|
|
||||||
fi
|
|
||||||
|
|
||||||
# SSSD-Konfigurationsdatei anpassen
|
|
||||||
sudo sed -i -e 's/fallback_homedir = \/home\/%u@%d/#fallback_homedir = \/home\/%u@%d/g' /etc/sssd/sssd.conf
|
|
||||||
sudo sed -i -e 's/use_fully_qualified_names = True/use_fully_qualified_names = False/g' /etc/sssd/sssd.conf
|
|
||||||
sudo sed -i -e 's/access_provider = ad/access_provider = simple/g' /etc/sssd/sssd.conf
|
|
||||||
sudo sed -i -e 's/sudoers: files sss/sudoers: files/g' /etc/nsswitch.conf
|
|
||||||
echo "override_homedir = /home/%u" | sudo tee -a /etc/sssd/sssd.conf
|
|
||||||
sudo grep -i override /etc/sssd/sssd.conf
|
|
||||||
|
|
||||||
# Weitere Anpassungen an der SSSD-Konfiguration
|
|
||||||
sudo sed -i '/krb5_realm =/a entry_cache_group_timeout = 5400' /etc/sssd/sssd.conf
|
|
||||||
sudo sed -i '/krb5_realm =/a entry_cache_user_timeout = 5400' /etc/sssd/sssd.conf
|
|
||||||
|
|
||||||
# Zusätzliche SSSD-Alternativen
|
|
||||||
sudo echo "#entry_cache_user_timeout = 5400
|
|
||||||
#entry_cache_group_timeout = 5400
|
|
||||||
#cache_credentials = TRUE
|
|
||||||
### Added to help with group mapping
|
|
||||||
###ldap_use_tokengroups = False
|
|
||||||
#ldap_schema = rfc2307bis
|
|
||||||
#ldap_schema = rfc2307
|
|
||||||
#ldap_schema = IPA
|
|
||||||
#ldap_schema = AD
|
|
||||||
#ldap_search_base = DC=$NetBios,DC=$coms
|
|
||||||
#ldap_group_member = uniquemember
|
|
||||||
#ad_enable_gc = False
|
|
||||||
entry_cache_timeout = 600
|
|
||||||
entry_cache_nowait_percentage = 75 " | sudo tee -a /etc/sssd/sssd.alternatives
|
|
||||||
|
|
||||||
echo "✅ Der Computer muss jetzt neugestartet werden. Vor dem Anmelden etwa 3 Minuten warten!"
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Root-Rechte prüfen
|
|
||||||
if [ "$(id -u)" -eq 0 ]; then
|
|
||||||
echo "❌ Bitte nicht als root ausführen! Das Skript nutzt sudo, falls nötig."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
sudo pacman -Syu
|
|
||||||
yay -Syu
|
|
||||||
|
|
||||||
# Temporären Ordner erstellen
|
|
||||||
TEMP_DIR=$(mktemp -d)
|
|
||||||
|
|
||||||
# Skript in den temporären Ordner herunterladen
|
|
||||||
curl -L https://raw.githubusercontent.com/JaKooLit/Arch-Hyprland/main/auto-install.sh -o "$TEMP_DIR/auto-install.sh"
|
|
||||||
|
|
||||||
# Das heruntergeladene Skript mit sh ausführen
|
|
||||||
sh "$TEMP_DIR/auto-install.sh"
|
|
||||||
|
|
||||||
# Temporären Ordner löschen
|
|
||||||
rm -rf "$TEMP_DIR"
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Sicherstellen, dass das Skript mit Root-Rechten ausgeführt wird
|
|
||||||
if [[ $EUID -ne 0 ]]; then
|
|
||||||
echo "❌ Bitte als root ausführen!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Tatsächlichen Benutzer ermitteln (nicht root)
|
|
||||||
if [[ -z "$SUDO_USER" || "$SUDO_USER" == "root" ]]; then
|
|
||||||
echo "❌ Fehler: Das Skript muss mit 'sudo' von einem normalen Benutzer ausgeführt werden."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
USER_NAME="$SUDO_USER"
|
|
||||||
USER_HOME=$(eval echo ~$USER_NAME)
|
|
||||||
|
|
||||||
read -p "❓ Soll die Shell für $USER_NAME auf ZSH gesetzt werden? (j/n) [n]: " answer
|
|
||||||
answer=${answer,,} # In Kleinbuchstaben umwandeln
|
|
||||||
answer=${answer:-n} # Standardwert 'n', falls leer
|
|
||||||
|
|
||||||
if [[ "$answer" == "j" ]] || [[ "$answer" == "y" ]]; then
|
|
||||||
# ZSH-Installation prüfen und ggf. installieren
|
|
||||||
if ! command -v zsh &> /dev/null; then
|
|
||||||
echo "ℹ️ ZSH wird installiert..."
|
|
||||||
sudo pacman -S --noconfirm zsh
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Eintrag aus getent passwd holen
|
|
||||||
USER_ENTRY=$(getent passwd "$USER_NAME")
|
|
||||||
|
|
||||||
# Backup der passwd Datei
|
|
||||||
cp /etc/passwd /etc/passwd.bak
|
|
||||||
|
|
||||||
if grep -q "^$USER_NAME:" /etc/passwd; then
|
|
||||||
# Existierenden Eintrag aktualisieren
|
|
||||||
sed -i "/^$USER_NAME:/s|[^:]*$|/bin/zsh|" /etc/passwd
|
|
||||||
echo "ℹ️ Shell für $USER_NAME wurde auf zsh aktualisiert."
|
|
||||||
else
|
|
||||||
# Neuen Eintrag hinzufügen
|
|
||||||
UPDATED_ENTRY=$(echo "$USER_ENTRY" | awk -F: -v OFS=: '{ $NF="/bin/zsh"; print }')
|
|
||||||
echo "$UPDATED_ENTRY" >>/etc/passwd
|
|
||||||
echo "ℹ️ Eintrag für $USER_NAME wurde in /etc/passwd geschrieben mit zsh als Shell."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Repository klonen
|
|
||||||
REPO_DIR="$USER_HOME/Debian-Hyprland"
|
|
||||||
sudo -u "$USER_NAME" git clone https://github.com/JaKooLit/Arch-Hyprland.git "$REPO_DIR"
|
|
||||||
|
|
||||||
# Ordner nach ~/.oh-my-zsh/themes kopieren
|
|
||||||
THEME_SRC="$REPO_DIR/assets/add_zsh_theme"
|
|
||||||
THEME_DEST="$USER_HOME/.oh-my-zsh/themes"
|
|
||||||
|
|
||||||
if [[ -d "$THEME_SRC" ]]; then
|
|
||||||
sudo -u "$USER_NAME" mkdir -p "$THEME_DEST"
|
|
||||||
sudo -u "$USER_NAME" cp -r "$THEME_SRC"/* "$THEME_DEST/"
|
|
||||||
echo "🎨 ZSH-Theme wurde nach $THEME_DEST kopiert."
|
|
||||||
else
|
|
||||||
echo "⚠️ Fehler: Theme-Ordner wurde nicht gefunden!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Repository löschen
|
|
||||||
rm -rf "$REPO_DIR"
|
|
||||||
|
|
||||||
# /etc/profile in /etc/zsh/zprofile einfügen, falls nicht bereits vorhanden
|
|
||||||
if ! grep -q "source /etc/profile" /etc/zsh/zprofile; then
|
|
||||||
echo "source /etc/profile" >>/etc/zsh/zprofile
|
|
||||||
echo "🔧 'source /etc/profile' wurde in /etc/zsh/zprofile hinzugefügt."
|
|
||||||
else
|
|
||||||
echo "ℹ️ 'source /etc/profile' ist bereits in /etc/zsh/zprofile vorhanden."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "✅ Shell wurde erfolgreich auf ZSH gesetzt! Zum Anwenden, neue Terminal-Sitzung öffnen."
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Sicherstellen, dass das Skript mit Root-Rechten ausgeführt wird
|
|
||||||
if [[ $EUID -ne 0 ]]; then
|
|
||||||
echo "❌ Dieses Skript muss als root ausgeführt werden."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
read -p "❓ Soll SDDM so konfiguriert werden, dass Domänenbenutzer angezeigt werden? (j/n) [n]: " answer
|
|
||||||
answer=${answer,,} # In Kleinbuchstaben umwandeln
|
|
||||||
answer=${answer:-n} # Standardwert 'n', falls leer
|
|
||||||
|
|
||||||
if [[ "$answer" == "j" ]] || [[ "$answer" == "y" ]]; then
|
|
||||||
# SDDM-Konfigurationsdatei in sddm.conf.d erstellen
|
|
||||||
SDDM_CONF_DIR="/etc/sddm.conf.d"
|
|
||||||
SDDM_CUSTOM_CONF="$SDDM_CONF_DIR/ad_login.conf"
|
|
||||||
mkdir -p "$SDDM_CONF_DIR"
|
|
||||||
|
|
||||||
# SDDM-Konfigurationsdatei sichern
|
|
||||||
if [[ -f "$SDDM_CUSTOM_CONF" ]]; then
|
|
||||||
cp "$SDDM_CUSTOM_CONF" "$SDDM_CUSTOM_CONF.bak"
|
|
||||||
echo "ℹ️ SDDM-Konfigurationsdatei gesichert als $SDDM_CUSTOM_CONF.bak"
|
|
||||||
else
|
|
||||||
echo "⚠️ SDDM-Konfigurationsdatei nicht gefunden, keine Sicherung vorgenommen."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Bestehende Konfiguration sichern und anpassen
|
|
||||||
if [[ -f "$SDDM_CUSTOM_CONF" ]]; then
|
|
||||||
sed -i '/MaximumUid/d' "$SDDM_CUSTOM_CONF"
|
|
||||||
sed -i '/MinimumUid/d' "$SDDM_CUSTOM_CONF"
|
|
||||||
sed -i '/HideShells/d' "$SDDM_CUSTOM_CONF"
|
|
||||||
echo "MaximumUid=99999999999999999999" >>"$SDDM_CUSTOM_CONF"
|
|
||||||
echo "MinimumUid=1000" >>"$SDDM_CUSTOM_CONF"
|
|
||||||
echo "HideShells=/sbin/nologin,/bin/false,/usr/sbin/nologin" >>"$SDDM_CUSTOM_CONF"
|
|
||||||
else
|
|
||||||
cat <<EOF >"$SDDM_CUSTOM_CONF"
|
|
||||||
[Users]
|
|
||||||
MaximumUid=999999999999999999
|
|
||||||
MinimumUid=1000
|
|
||||||
HideShells=/sbin/nologin,/bin/false,/usr/sbin/nologin
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
|
|
||||||
# SSSD-Konfiguration anpassen, falls die Datei existiert
|
|
||||||
SSSD_CONF="/etc/sssd/sssd.conf"
|
|
||||||
|
|
||||||
# SSSD-Konfigurationsdatei sichern
|
|
||||||
if [[ -f "$SSSD_CONF" ]]; then
|
|
||||||
cp "$SSSD_CONF" "$SSSD_CONF.bak"
|
|
||||||
echo "ℹ️ SSSD-Konfigurationsdatei gesichert als $SSSD_CONF.bak"
|
|
||||||
else
|
|
||||||
echo "⚠️ SSSD-Konfigurationsdatei nicht gefunden, keine Sicherung vorgenommen."
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -f "$SSSD_CONF" ]]; then
|
|
||||||
if grep -q "^enumerate" "$SSSD_CONF"; then
|
|
||||||
sed -i 's/^enumerate.*/enumerate = false/' "$SSSD_CONF"
|
|
||||||
else
|
|
||||||
echo "enumerate = false" >>"$SSSD_CONF"
|
|
||||||
fi
|
|
||||||
# Berechtigungen für SSSD-Konfigurationsdatei setzen
|
|
||||||
chmod 600 "$SSSD_CONF"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# SSSD und SDDM neu starten
|
|
||||||
systemctl restart sssd
|
|
||||||
systemctl restart sddm
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "✅ Konfiguration von SDDM und sssd abgeschlossen!"
|
|
||||||
-236
@@ -1,236 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e # Skript bricht bei Fehlern ab
|
|
||||||
|
|
||||||
# Root-Rechte prüfen
|
|
||||||
if [ "$(id -u)" -eq 0 ]; then
|
|
||||||
echo "❌ Bitte nicht als root ausführen! Das Skript nutzt sudo, falls nötig."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# System aktualisieren und benötigte Pakete installieren
|
|
||||||
echo "🔄 System wird aktualisiert..."
|
|
||||||
sudo pacman -Syu --noconfirm
|
|
||||||
yay -Syu --noconfirm
|
|
||||||
|
|
||||||
echo "ℹ️ Firewall (ufw) wird installiert..."
|
|
||||||
sudo pacman -S --noconfirm ufw
|
|
||||||
sudo ufw enable
|
|
||||||
|
|
||||||
echo "ℹ️ Virenschutz (clamav) wird installiert..."
|
|
||||||
sudo pacman -S --noconfirm clamav
|
|
||||||
sudo systemctl enable --now clamav-freshclam
|
|
||||||
|
|
||||||
echo "ℹ️ Installiere Flatpak..."
|
|
||||||
sudo pacman -S --noconfirm flatpak
|
|
||||||
sudo flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
|
|
||||||
|
|
||||||
echo "ℹ️ Installiere nützliche Pakete..."
|
|
||||||
sudo pacman -S --noconfirm neovim rfkill xdg-user-dirs rsync bleachbit networkmanager
|
|
||||||
|
|
||||||
# Portmaster installieren
|
|
||||||
read -p "❓ Soll Portmaster installiert werden? (j/n) [n]: " answer
|
|
||||||
answer=${answer,,} # In Kleinbuchstaben umwandeln
|
|
||||||
answer=${answer:-n} # Standardwert 'n', falls leer
|
|
||||||
|
|
||||||
if [[ "$answer" == "j" ]] || [[ "$answer" == "y" ]]; then
|
|
||||||
yay -S portmaster-stub-bin --noconfirm
|
|
||||||
|
|
||||||
echo "✅ Portmaster wurde installiert!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Waterfox-Installation
|
|
||||||
read -p "❓ Soll Firefox durch Waterfox ersetzt werden? (j/n) [n]: " answer
|
|
||||||
answer=${answer,,} # In Kleinbuchstaben umwandeln
|
|
||||||
answer=${answer:-n} # Standardwert 'n', falls leer
|
|
||||||
|
|
||||||
if [[ "$answer" == "j" ]] || [[ "$answer" == "y" ]]; then
|
|
||||||
if pacman -Q firefox &>/dev/null; then
|
|
||||||
sudo pacman -Rns --noconfirm firefox
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "🌍 Waterfox wird installiert..."
|
|
||||||
yay -S --noconfirm waterfox
|
|
||||||
|
|
||||||
echo "✅ Waterfox wurde installiert!"
|
|
||||||
|
|
||||||
# Waterfox als Standardbrowser setzen
|
|
||||||
echo "🌍 Setze Waterfox als Standardbrowser..."
|
|
||||||
if command -v xdg-settings >/dev/null 2>&1; then
|
|
||||||
xdg-settings set default-web-browser waterfox.desktop
|
|
||||||
echo "✅ Waterfox wurde als Standardbrowser gesetzt."
|
|
||||||
|
|
||||||
# Standardanwendungen für spezifische Dateitypen setzen
|
|
||||||
xdg-mime default waterfox.desktop text/html
|
|
||||||
xdg-mime default waterfox.desktop application/xhtml+xml
|
|
||||||
xdg-mime default waterfox.desktop x-scheme-handler/http
|
|
||||||
xdg-mime default waterfox.desktop x-scheme-handler/https
|
|
||||||
else
|
|
||||||
echo "❌ 'xdg-settings' ist nicht verfügbar. Bitte manuell den Standardbrowser setzen."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Hyprland User Bindings
|
|
||||||
read -p "❓ Sollen Anpassungen an den Hyprland-Keybinds vorgenomen werden? (j/n) [n]: " answer
|
|
||||||
answer=${answer,,} # In Kleinbuchstaben umwandeln
|
|
||||||
answer=${answer:-n} # Standardwert 'n', falls leer
|
|
||||||
|
|
||||||
if [[ "$answer" == "j" ]] || [[ "$answer" == "y" ]]; then
|
|
||||||
HYPR_CONFIG_DIR="$HOME/.config/hypr/UserConfigs"
|
|
||||||
mkdir -p "$HYPR_CONFIG_DIR"
|
|
||||||
|
|
||||||
# Keybinds hinzufügen (falls nicht vorhanden)
|
|
||||||
declare -A KEYBINDS=(
|
|
||||||
["bindr = \$mainMod, \$mainMod_L, exec, pkill rofi || rofi -show drun -modi drun,filebrowser,run,window"]="rofi menu"
|
|
||||||
["bindr = \$mainMod, L, exec, \$scriptsDir/LockScreen.sh"]="screen lock"
|
|
||||||
["bindr = \$mainMod, V, exec, \$scriptsDir/ClipManager.sh"]="Clipboard Manager"
|
|
||||||
)
|
|
||||||
|
|
||||||
for BIND in "${!KEYBINDS[@]}"; do
|
|
||||||
FULL_BIND="$BIND # ${KEYBINDS[$BIND]}"
|
|
||||||
if ! grep -Fxq "$FULL_BIND" "$HYPR_CONFIG_DIR/UserKeybinds.conf" 2>/dev/null; then
|
|
||||||
echo "$FULL_BIND" >>"$HYPR_CONFIG_DIR/UserKeybinds.conf"
|
|
||||||
echo "✅ Keybind hinzugefügt: ${KEYBINDS[$BIND]}"
|
|
||||||
else
|
|
||||||
echo "ℹ️ Keybind existiert bereits: ${KEYBINDS[$BIND]}"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Standardvorlagen ablegen
|
|
||||||
echo "📂 Standardvorlagen werden im Vorlagen-Ordner erstellt..."
|
|
||||||
TEMPLATES_DIR=$(xdg-user-dir TEMPLATES 2>/dev/null || echo "$HOME/Vorlagen")
|
|
||||||
mkdir -p "$TEMPLATES_DIR"
|
|
||||||
|
|
||||||
# Vorlagenliste
|
|
||||||
declare -A TEMPLATES=(
|
|
||||||
["Textdokument.txt"]="Dies ist eine Standard-Textdatei."
|
|
||||||
["Markdown-Dokument.md"]="# Markdown-Vorlage\n\nHier beginnt dein Markdown-Dokument."
|
|
||||||
["Bash-Skript.sh"]="#!/bin/bash\n\necho 'Hello, world!'"
|
|
||||||
["Python-Skript.py"]='#!/usr/bin/env python3\n\nprint("Hello, world!")'
|
|
||||||
)
|
|
||||||
|
|
||||||
for FILE in "${!TEMPLATES[@]}"; do
|
|
||||||
TEMPLATE_PATH="$TEMPLATES_DIR/$FILE"
|
|
||||||
if [[ ! -f "$TEMPLATE_PATH" ]]; then
|
|
||||||
echo -e "${TEMPLATES[$FILE]}" >"$TEMPLATE_PATH"
|
|
||||||
chmod +x "$TEMPLATE_PATH" # Falls Skript
|
|
||||||
echo "✅ Vorlage erstellt: $FILE"
|
|
||||||
else
|
|
||||||
echo "ℹ️ Vorlage existiert bereits: $FILE"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Zielverzeichnis und UserKeybinds-Datei definieren
|
|
||||||
SOURCE_DIR="$HOME/.config/hypr/scripts"
|
|
||||||
TARGET_DIR="$HOME/.config/hypr/UserScripts"
|
|
||||||
CONFIG_DIR="$HOME/.config/hypr/UserConfigs"
|
|
||||||
USER_KEYBINDS_FILE="$CONFIG_DIR/UserKeybinds.conf" # Definition an der richtigen Stelle
|
|
||||||
|
|
||||||
read -p "❓ Sollen Anpassungen an den Users-Dirs in den Hyprland-Skripten vorgenommen werden? (j/n) [n]: " answer
|
|
||||||
answer=${answer,,} # In Kleinbuchstaben umwandeln
|
|
||||||
answer=${answer:-n} # Standardwert 'n', falls leer
|
|
||||||
|
|
||||||
if [[ "$answer" == "j" ]] || [[ "$answer" == "y" ]]; then
|
|
||||||
# Bereinige fälschlicherweise kopierte Dateien
|
|
||||||
echo "📂 Überprüfe und bereinige Dateien im Verzeichnis $TARGET_DIR..."
|
|
||||||
if [[ -d "$TARGET_DIR" ]]; then
|
|
||||||
for FILE in "$TARGET_DIR"/*; do
|
|
||||||
if [[ -f "$FILE" ]]; then
|
|
||||||
BASENAME=$(basename "$FILE")
|
|
||||||
SOURCE_FILE="$SOURCE_DIR/$BASENAME"
|
|
||||||
|
|
||||||
# Prüfen, ob die Datei im Quellverzeichnis existiert und angepasst werden musste
|
|
||||||
if [[ -f "$SOURCE_FILE" ]] && ! grep -qE '\$HOME/Desktop|\$HOME/Downloads|\$HOME/Documents|\$HOME/Pictures|\$HOME/Music|\$HOME/Videos' "$SOURCE_FILE"; then
|
|
||||||
echo "🗑️ Entferne fälschlicherweise kopierte Datei: $BASENAME"
|
|
||||||
rm "$FILE"
|
|
||||||
|
|
||||||
# Rückgängig machen der falschen Verweise in UserKeybinds.conf
|
|
||||||
if [[ -f "$USER_KEYBINDS_FILE" ]]; then
|
|
||||||
echo "🔄 Setze falschen Verweis für $BASENAME in $USER_KEYBINDS_FILE zurück..."
|
|
||||||
sed -i "s|\$UserScripts/$BASENAME|\$scriptsDir/$BASENAME|g" "$USER_KEYBINDS_FILE"
|
|
||||||
echo "✅ Verweis für $BASENAME zurückgesetzt."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "✅ Datei $BASENAME ist korrekt und bleibt erhalten."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
else
|
|
||||||
echo "⚠️ Zielverzeichnis $TARGET_DIR existiert nicht. Keine Bereinigung erforderlich."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Kopiere nur relevante Dateien und behalte die Verzeichnisstruktur bei
|
|
||||||
echo "📂 Verarbeite Dateien im Verzeichnis $SOURCE_DIR..."
|
|
||||||
mkdir -p "$TARGET_DIR"
|
|
||||||
COPIED_FILES=() # Array, um die tatsächlich kopierten Dateien zu speichern
|
|
||||||
|
|
||||||
if [[ -d "$SOURCE_DIR" ]]; then
|
|
||||||
find "$SOURCE_DIR" -type f | while read -r FILE; do
|
|
||||||
RELATIVE_PATH="${FILE#$SOURCE_DIR/}" # Relativer Pfad zur Datei
|
|
||||||
TARGET_PATH="$TARGET_DIR/$RELATIVE_PATH"
|
|
||||||
|
|
||||||
# Prüfen, ob die Datei angepasst werden muss
|
|
||||||
if grep -qE '\$HOME/Desktop|\$HOME/Downloads|\$HOME/Documents|\$HOME/Pictures|\$HOME/Music|\$HOME/Videos|/home/[a-zA-Z0-9._-]*/Desktop|/home/[a-zA-Z0-9._-]*/Downloads|/home/[a-zA-Z0-9._-]*/Documents|/home/[a-zA-Z0-9._-]*/Pictures|/home/[a-zA-Z0-9._-]*/Music|/home/[a-zA-Z0-9._-]*/Videos|\$\(xdg-user-dir\)/Desktop|\$\(xdg-user-dir\)/Downloads|\$\(xdg-user-dir\)/Documents|\$\(xdg-user-dir\)/Pictures|\$\(xdg-user-dir\)/Music|\$\(xdg-user-dir\)/Videos' "$FILE"; then
|
|
||||||
echo "🔄 Kopiere Datei zur Anpassung: $RELATIVE_PATH"
|
|
||||||
mkdir -p "$(dirname "$TARGET_PATH")" # Zielverzeichnis erstellen
|
|
||||||
cp "$FILE" "$TARGET_PATH"
|
|
||||||
COPIED_FILES+=("$RELATIVE_PATH") # Relativen Pfad speichern
|
|
||||||
else
|
|
||||||
echo "ℹ️ Datei $RELATIVE_PATH benötigt keine Anpassung. Überspringe Kopieren."
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
else
|
|
||||||
echo "⚠️ Quellverzeichnis $SOURCE_DIR existiert nicht. Überspringe Verarbeitung."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Ersetzungen in allen Dateien im Verzeichnis UserScripts vornehmen
|
|
||||||
echo "🔄 Ersetze absolute Pfade, XDG-Verzeichnisse und Platzhalter in allen Dateien im Verzeichnis $TARGET_DIR..."
|
|
||||||
for FILE in "$TARGET_DIR"/*; do
|
|
||||||
if [[ -f "$FILE" ]]; then
|
|
||||||
echo "Verarbeite Datei: $FILE"
|
|
||||||
|
|
||||||
# Ersetzungen vornehmen
|
|
||||||
sed -i \
|
|
||||||
-e 's|\$HOME/Desktop|$(xdg-user-dir DESKTOP)|g' \
|
|
||||||
-e 's|\$HOME/Downloads|$(xdg-user-dir DOWNLOAD)|g' \
|
|
||||||
-e 's|\$HOME/Documents|$(xdg-user-dir DOCUMENTS)|g' \
|
|
||||||
-e 's|\$HOME/Pictures|$(xdg-user-dir PICTURES)|g' \
|
|
||||||
-e 's|\$HOME/Music|$(xdg-user-dir MUSIC)|g' \
|
|
||||||
-e 's|\$HOME/Videos|$(xdg-user-dir VIDEOS)|g' \
|
|
||||||
-e 's|/home/[a-zA-Z0-9._-]*/Desktop|$(xdg-user-dir DESKTOP)|g' \
|
|
||||||
-e 's|/home/[a-zA-Z0-9._-]*/Downloads|$(xdg-user-dir DOWNLOAD)|g' \
|
|
||||||
-e 's|/home/[a-zA-Z0-9._-]*/Documents|$(xdg-user-dir DOCUMENTS)|g' \
|
|
||||||
-e 's|/home/[a-zA-Z0-9._-]*/Pictures|$(xdg-user-dir PICTURES)|g' \
|
|
||||||
-e 's|/home/[a-zA-Z0-9._-]*/Music|$(xdg-user-dir MUSIC)|g' \
|
|
||||||
-e 's|/home/[a-zA-Z0-9._-]*/Videos|$(xdg-user-dir VIDEOS)|g' \
|
|
||||||
-e 's|$(xdg-user-dir)/Desktop|$(xdg-user-dir DESKTOP)|g' \
|
|
||||||
-e 's|$(xdg-user-dir)/Downloads|$(xdg-user-dir DOWNLOAD)|g' \
|
|
||||||
-e 's|$(xdg-user-dir)/Documents|$(xdg-user-dir DOCUMENTS)|g' \
|
|
||||||
-e 's|$(xdg-user-dir)/Pictures|$(xdg-user-dir PICTURES)|g' \
|
|
||||||
-e 's|$(xdg-user-dir)/Music|$(xdg-user-dir MUSIC)|g' \
|
|
||||||
-e 's|$(xdg-user-dir)/Videos|$(xdg-user-dir VIDEOS)|g' \
|
|
||||||
"$FILE"
|
|
||||||
|
|
||||||
echo "✅ Ersetzungen in $FILE abgeschlossen."
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Verweise in UserKeybinds.conf anpassen
|
|
||||||
if [[ -f "$USER_KEYBINDS_FILE" ]]; then
|
|
||||||
echo "🔄 Passe Verweise in $USER_KEYBINDS_FILE an..."
|
|
||||||
for SCRIPT in "${COPIED_FILES[@]}"; do
|
|
||||||
# Ersetze $scriptsDir/<Dateiname> durch $UserScripts/<Dateiname>
|
|
||||||
sed -i "s|\$scriptsDir/$SCRIPT|\$UserScripts/$SCRIPT|g" "$USER_KEYBINDS_FILE"
|
|
||||||
echo "✅ Verweis für $SCRIPT angepasst."
|
|
||||||
done
|
|
||||||
else
|
|
||||||
echo "⚠️ Datei $USER_KEYBINDS_FILE existiert nicht. Überspringe Anpassung der Verweise."
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "📂 Verschiebe $(xdg-user-dir)/Pictures nach $(xdg-user-dir PICTURES)..."
|
|
||||||
rsync -av --ignore-existing "$(xdg-user-dir)/Pictures/" "$(xdg-user-dir PICTURES)/"
|
|
||||||
rm -r "$(xdg-user-dir)/Pictures/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "✅ Skript erfolgreich ausgeführt!"
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e # Skript bricht bei Fehlern ab
|
|
||||||
|
|
||||||
# Sicherstellen, dass das Skript mit Root-Rechten ausgeführt wird
|
|
||||||
if [[ $EUID -ne 0 ]]; then
|
|
||||||
echo "❌ Dieses Skript muss als root ausgeführt werden."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z "$SUDO_USER" || "$SUDO_USER" == "root" ]]; then
|
|
||||||
echo "❌ Fehler: Das Skript muss mit 'sudo' von einem normalen Benutzer ausgeführt werden."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
USER_NAME="$SUDO_USER"
|
|
||||||
|
|
||||||
echo "🔄 Plymouth und benötigte Pakete installieren..."
|
|
||||||
pacman -Syu --noconfirm plymouth
|
|
||||||
sudo -u $USER_NAME yay -Syu plymouth-theme-archlinux --noconfirm
|
|
||||||
|
|
||||||
# Plymouth in initramfs aktivieren
|
|
||||||
MKINIT_CONF="/etc/mkinitcpio.conf"
|
|
||||||
if [[ -f "$MKINIT_CONF" ]]; then
|
|
||||||
if grep -q "^HOOKS=" "$MKINIT_CONF"; then
|
|
||||||
sed -i 's/^HOOKS=.*/HOOKS=(base udev plymouth autodetect modconf block encrypt lvm2 filesystems keyboard fsck)/' "$MKINIT_CONF"
|
|
||||||
else
|
|
||||||
echo 'HOOKS=(base udev plymouth autodetect modconf block encrypt lvm2 filesystems keyboard fsck)' >>"$MKINIT_CONF"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Initramfs neu erstellen
|
|
||||||
echo "🔄 Initramfs wird aktualisiert..."
|
|
||||||
mkinitcpio -P
|
|
||||||
|
|
||||||
# Arch Plymouth-Theme setzen
|
|
||||||
echo "🎨 Setze Plymouth-Theme auf 'arch'..."
|
|
||||||
plymouth-set-default-theme -R archlinux
|
|
||||||
|
|
||||||
# Korrekte systemd-boot Konfigurationsdatei suchen
|
|
||||||
BOOT_ENTRY_DIR="/boot/loader/entries"
|
|
||||||
BOOT_ENTRY_FILE=$(find "$BOOT_ENTRY_DIR" -name "*.conf" | head -n 1)
|
|
||||||
|
|
||||||
if [[ -z "$BOOT_ENTRY_FILE" ]]; then
|
|
||||||
echo "❌ Keine systemd-boot Eintragsdatei gefunden!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "🔍 Gefundene Konfigurationsdatei: $BOOT_ENTRY_FILE"
|
|
||||||
|
|
||||||
# Backup erstellen
|
|
||||||
BACKUP_FILE="${BOOT_ENTRY_FILE}.backup_$(date +%Y%m%d%H%M%S)"
|
|
||||||
cp "$BOOT_ENTRY_FILE" "$BACKUP_FILE"
|
|
||||||
echo "🛑 Backup gespeichert unter: $BACKUP_FILE"
|
|
||||||
|
|
||||||
NEW_PARAMS="quiet splash vt.global_cursor_default=0 loglevel=3 rd.luks.options=discard plymouth.ignore-serial-consoles"
|
|
||||||
|
|
||||||
# Prüfen, ob bereits eine "options"-Zeile existiert
|
|
||||||
if grep -q "^options " "$BOOT_ENTRY_FILE"; then
|
|
||||||
# Vorhandene Optionen extrahieren
|
|
||||||
EXISTING_PARAMS=$(grep "^options " "$BOOT_ENTRY_FILE" | cut -d' ' -f2-)
|
|
||||||
|
|
||||||
# Fehlende Parameter hinzufügen
|
|
||||||
for param in $NEW_PARAMS; do
|
|
||||||
if ! grep -qw "$param" <<< "$EXISTING_PARAMS"; then
|
|
||||||
EXISTING_PARAMS+=" $param"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Datei mit neuen Optionen aktualisieren
|
|
||||||
sed -i "s|^options .*|options $EXISTING_PARAMS|" "$BOOT_ENTRY_FILE"
|
|
||||||
else
|
|
||||||
# Falls keine options-Zeile existiert, einfach neue hinzufügen
|
|
||||||
echo "options $NEW_PARAMS" >> "$BOOT_ENTRY_FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "✅ Einrichtung abgeschlossen. Bitte starte das System neu, um die Änderungen zu übernehmen."
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Sicherstellen, dass das Skript mit Root-Rechten ausgeführt wird
|
|
||||||
if [[ $EUID -ne 0 ]]; then
|
|
||||||
echo "❌ Dieses Skript muss als root ausgeführt werden."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# System aktualisieren
|
|
||||||
echo "🔄 System wird aktualisiert..."
|
|
||||||
pacman -Syu --noconfirm || { echo "❌ Fehler beim Aktualisieren des Systems."; exit 1; }
|
|
||||||
|
|
||||||
# Steam-Installation
|
|
||||||
read -p "❓ Soll Steam installiert werden? (j/n) [n]: " answer
|
|
||||||
answer=${answer,,} # In Kleinbuchstaben umwandeln
|
|
||||||
answer=${answer:-n} # Standardwert 'n', falls leer
|
|
||||||
|
|
||||||
if [[ "$answer" == "j" ]] || [[ "$answer" == "y" ]]; then
|
|
||||||
echo "🔄 Steam wird installiert..."
|
|
||||||
|
|
||||||
# Steam aus dem offiziellen Repository installieren
|
|
||||||
pacman -S --noconfirm steam || { echo "❌ Fehler beim Installieren von Steam."; exit 1; }
|
|
||||||
echo "✅ Steam wurde erfolgreich installiert."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Lutris-Installation
|
|
||||||
read -p "❓ Soll Lutris installiert werden? (j/n) [n]: " answer
|
|
||||||
answer=${answer,,} # In Kleinbuchstaben umwandeln
|
|
||||||
answer=${answer:-n} # Standardwert 'n', falls leer
|
|
||||||
|
|
||||||
if [[ "$answer" == "j" ]] || [[ "$answer" == "y" ]]; then
|
|
||||||
echo "🔄 Lutris wird installiert..."
|
|
||||||
|
|
||||||
# Lutris aus dem offiziellen Repository installieren
|
|
||||||
pacman -S --noconfirm lutris || { echo "❌ Fehler beim Installieren von Lutris."; exit 1; }
|
|
||||||
echo "✅ Lutris wurde erfolgreich installiert."
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "✅ Skript abgeschlossen!"
|
|
||||||
-302
@@ -1,302 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e # Skript bricht bei Fehlern ab
|
|
||||||
|
|
||||||
# Sicherstellen, dass das Skript **nicht** als root ausgeführt wird
|
|
||||||
if [[ $EUID -eq 0 ]]; then
|
|
||||||
echo "❌ Bitte **nicht** als root oder mit sudo ausführen! Das Skript fordert sudo nur dort an, wo es benötigt wird."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Überprüfen, ob Flatpak installiert ist
|
|
||||||
if ! command -v flatpak &>/dev/null; then
|
|
||||||
echo "❌ Fehler: Flatpak ist nicht installiert. Bitte installiere es und versuche es erneut."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Flatpak Apps installieren (nur falls noch nicht als DEB installiert)
|
|
||||||
declare -A FLATPAK_APPS=(
|
|
||||||
["org.libreoffice.LibreOffice"]="LibreOffice"
|
|
||||||
["dev.vencord.Vesktop"]="Vesktop"
|
|
||||||
["eu.betterbird.Betterbird"]="Betterbird"
|
|
||||||
)
|
|
||||||
|
|
||||||
for APP in "${!FLATPAK_APPS[@]}"; do
|
|
||||||
if pacman -Qs "${FLATPAK_APPS[$APP]}" &>/dev/null; then
|
|
||||||
echo "✅ ${FLATPAK_APPS[$APP]} ist bereits als Paket installiert."
|
|
||||||
elif flatpak list | grep -q "$APP"; then
|
|
||||||
echo "✅ ${FLATPAK_APPS[$APP]} ist bereits als Flatpak installiert."
|
|
||||||
else
|
|
||||||
echo "🔄 Installiere ${FLATPAK_APPS[$APP]} als Flatpak..."
|
|
||||||
flatpak install -y flathub "$APP"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Dynamischen Vorlagen-Ordner ermitteln
|
|
||||||
TEMPLATE_DIR=$(xdg-user-dir TEMPLATES 2>/dev/null || echo "$HOME/Vorlagen")
|
|
||||||
mkdir -p "$TEMPLATE_DIR"
|
|
||||||
|
|
||||||
# Prüfen, ob LibreOffice als Flatpak installiert ist
|
|
||||||
if flatpak list | grep -q "org.libreoffice.LibreOffice"; then
|
|
||||||
LO_CMD="flatpak run org.libreoffice.LibreOffice"
|
|
||||||
elif command -v libreoffice &>/dev/null; then
|
|
||||||
LO_CMD="libreoffice"
|
|
||||||
else
|
|
||||||
echo "❌ Fehler: LibreOffice ist weder als Flatpak noch als Paket installiert."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# LibreOffice-Vorlagen erstellen, falls nicht vorhanden
|
|
||||||
if [[ ! -f "$TEMPLATE_DIR/LibreOffice-Writer.ott" ]]; then
|
|
||||||
echo "🔄 Erstelle LibreOffice Writer-Vorlage..."
|
|
||||||
$LO_CMD --headless --convert-to ott --outdir "$TEMPLATE_DIR" /dev/null
|
|
||||||
else
|
|
||||||
echo "✅ LibreOffice Writer-Vorlage ist bereits vorhanden."
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! -f "$TEMPLATE_DIR/LibreOffice-Calc.ots" ]]; then
|
|
||||||
echo "🔄 Erstelle LibreOffice Calc-Vorlage..."
|
|
||||||
$LO_CMD --headless --convert-to ots --outdir "$TEMPLATE_DIR" /dev/null
|
|
||||||
else
|
|
||||||
echo "✅ LibreOffice Calc-Vorlage ist bereits vorhanden."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Spotify-Installation
|
|
||||||
echo "🎵 Überprüfe Spotify-Installation..."
|
|
||||||
|
|
||||||
if pacman -Qs spotify &>/dev/null; then
|
|
||||||
echo "✅ Spotify ist bereits installiert."
|
|
||||||
else
|
|
||||||
echo "🔄 Installiere Spotify..."
|
|
||||||
|
|
||||||
sudo pacman -S --noconfirm spotify
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Spotify .desktop-Datei erstellen
|
|
||||||
SPOTIFY_DESKTOP_FILE="$HOME/.local/share/applications/spotify.desktop"
|
|
||||||
mkdir -p "$HOME/.local/share/applications"
|
|
||||||
if [[ ! -f "$SPOTIFY_DESKTOP_FILE" ]]; then
|
|
||||||
echo "🔄 Erstelle Spotify .desktop-Datei..."
|
|
||||||
cat <<EOF >"$SPOTIFY_DESKTOP_FILE"
|
|
||||||
[Desktop Entry]
|
|
||||||
Name=Spotify
|
|
||||||
Exec=/usr/bin/spotify
|
|
||||||
Icon=spotify-client
|
|
||||||
Terminal=false
|
|
||||||
Type=Application
|
|
||||||
Categories=Audio;Music;
|
|
||||||
EOF
|
|
||||||
echo "✅ Spotify .desktop-Datei wurde erstellt."
|
|
||||||
else
|
|
||||||
echo "✅ Spotify .desktop-Datei ist bereits vorhanden."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Benötigte Abhängigkeiten prüfen und installieren
|
|
||||||
dependencies=(man fzf ripgrep awk w3m coreutils parallel)
|
|
||||||
for dep in "${dependencies[@]}"; do
|
|
||||||
if ! command -v "$dep" &>/dev/null; then
|
|
||||||
echo "🔄 Installiere $dep..."
|
|
||||||
sudo pacman -S --noconfirm "$dep"
|
|
||||||
else
|
|
||||||
echo "✅ $dep ist bereits installiert."
|
|
||||||
fi
|
|
||||||
|
|
||||||
done
|
|
||||||
|
|
||||||
# Tealdeer (tldr-Client) installieren
|
|
||||||
if ! command -v tldr &>/dev/null; then
|
|
||||||
echo "🔄 Installiere tealdeer..."
|
|
||||||
sudo pacman -S --noconfirm tealdeer
|
|
||||||
else
|
|
||||||
echo "✅ Tealdeer ist bereits installiert."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# tealdeer Autoupdate konfigurieren
|
|
||||||
TEALDEER_CONFIG=~/.config/tealdeer/config.toml
|
|
||||||
mkdir -p ~/.config/tealdeer
|
|
||||||
if ! grep -q "\[updates\]" "$TEALDEER_CONFIG" 2 >/dev/null; then
|
|
||||||
echo -e "\n[updates]" >>"$TEALDEER_CONFIG"
|
|
||||||
fi
|
|
||||||
if grep -q "^auto_update" "$TEALDEER_CONFIG" 2 >/dev/null; then
|
|
||||||
sed -i 's/^auto_update.*/auto_update = true/' "$TEALDEER_CONFIG"
|
|
||||||
else
|
|
||||||
echo "auto_update = true" >>"$TEALDEER_CONFIG"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Wikiman installieren, falls nicht vorhanden
|
|
||||||
if ! command -v wikiman &>/dev/null; then
|
|
||||||
echo "🔄 Installiere Wikiman..."
|
|
||||||
|
|
||||||
# Prüfen, ob `make` installiert ist
|
|
||||||
if ! command -v make &>/dev/null; then
|
|
||||||
echo "🔄 Installiere make..."
|
|
||||||
sudo pacman -S --noconfirm make
|
|
||||||
fi
|
|
||||||
|
|
||||||
git clone 'https://github.com/filiparag/wikiman' ~/wikiman
|
|
||||||
cd ~/wikiman || exit
|
|
||||||
|
|
||||||
# Die neueste stabile Version auschecken
|
|
||||||
git checkout "$(git describe --tags | cut -d'-' -f1)"
|
|
||||||
|
|
||||||
# Kompilieren und installieren
|
|
||||||
make all
|
|
||||||
sudo make install
|
|
||||||
|
|
||||||
# Cleanup
|
|
||||||
cd ..
|
|
||||||
rm -rf ~/wikiman
|
|
||||||
|
|
||||||
echo "✅ Wikiman wurde erfolgreich installiert!"
|
|
||||||
else
|
|
||||||
echo "✅ Wikiman ist bereits installiert."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Arch-Wiki für Wikiman installieren, falls nicht vorhanden
|
|
||||||
if ! wikiman -list | grep -q "arch"; then
|
|
||||||
echo "🔄 Installiere Arch Wiki für Wikiman..."
|
|
||||||
curl -L 'https://raw.githubusercontent.com/filiparag/wikiman/master/Makefile' -o 'wikiman-makefile'
|
|
||||||
make -f ./wikiman-makefile source-arch
|
|
||||||
sudo make -f ./wikiman-makefile source-install
|
|
||||||
sudo make -f ./wikiman-makefile clean
|
|
||||||
rm -f wikiman-makefile
|
|
||||||
else
|
|
||||||
echo "✅ Arch Wiki ist bereits installiert."
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "🔄 Installiere Standard-Programme..."
|
|
||||||
# Bildbetrachter
|
|
||||||
flatpak install flathub org.gnome.Loupe -y
|
|
||||||
xdg-mime default org.gnome.Loupe.desktop image/jpeg
|
|
||||||
xdg-mime default org.gnome.Loupe.desktop image/png
|
|
||||||
|
|
||||||
# Disk-Utility, PDF-Viewer (Okular), Softwarecenter und Flatpak-Plugin, Systemmonitor
|
|
||||||
sudo pacman -S --noconfirm gnome-disk-utility okular gnome-software gnome-system-monitor
|
|
||||||
# Okular als Standard-PDF-Viewer setzen
|
|
||||||
xdg-mime default okularApplication_pdf.desktop application/pdf
|
|
||||||
# Betterbird als Standard-Mail-Programm setzen
|
|
||||||
xdg-mime default org.betterbird.Betterbird.desktop x-scheme-handler/mailto
|
|
||||||
|
|
||||||
# Simple Scan isntallieren
|
|
||||||
flatpak install flathub org.gnome.SimpleScan -y
|
|
||||||
# Festplattenbelegungsanalyse installieren
|
|
||||||
flatpak install flathub org.gnome.baobab -y
|
|
||||||
# Camera installieren
|
|
||||||
flatpak install flathub org.gnome.Snapshot -y
|
|
||||||
# Amberol (Musik-Player) installieren
|
|
||||||
flatpak install flathub io.bassi.Amberol -y
|
|
||||||
xdg-mime default io.bassi.Amberol.desktop audio/mpeg
|
|
||||||
xdg-mime default io.bassi.Amberol.desktop audio/x-wav
|
|
||||||
xdg-mime default io.bassi.Amberol.desktop audio/flac
|
|
||||||
xdg-mime default io.bassi.Amberol.desktop audio/ogg
|
|
||||||
|
|
||||||
# VLC-Media-Player installieren
|
|
||||||
flatpak install flathub org.videolan.VLC -y
|
|
||||||
# Protokolle installieren
|
|
||||||
flatpak install flathub org.gnome.Logs -y
|
|
||||||
# Schriftarten-Viewer
|
|
||||||
flatpak install flathub org.gnome.font-viewer -y
|
|
||||||
# Taschenrechner
|
|
||||||
flatpak install flathub org.gnome.Calculator -y
|
|
||||||
# Texteditor
|
|
||||||
flatpak install flathub org.gnome.TextEditor -y
|
|
||||||
xdg-mime default org.gnome.TextEditor.desktop text/plain
|
|
||||||
xdg-mime default org.gnome.TextEditor.desktop text/x-log
|
|
||||||
xdg-mime default org.gnome.TextEditor.desktop text/markdown
|
|
||||||
|
|
||||||
# Ente Authenticator
|
|
||||||
flatpak install flathub io.ente.auth -y
|
|
||||||
# Cartridges (Gaming)
|
|
||||||
flatpak install flathub page.kramo.Cartridges -y
|
|
||||||
# Decoder (QR-Code)
|
|
||||||
flatpak install flathub com.belmoussaoui.Decoder -y
|
|
||||||
# Fragments (Torrent)
|
|
||||||
flatpak install flathub de.haeckerfelix.Fragments -y
|
|
||||||
# Impressions (Bootsticks)
|
|
||||||
flatpak install flathub io.gitlab.adhami3310.Impression -y
|
|
||||||
# Ressources (Task-Manager)
|
|
||||||
flatpak install flathub net.nokyan.Resources -y
|
|
||||||
# Secrets
|
|
||||||
flatpak install flathub org.gnome.World.Secrets -y
|
|
||||||
# Archivverwaltung
|
|
||||||
flatpak install flathub org.gnome.FileRoller -y
|
|
||||||
# Obfuscate
|
|
||||||
flatpak install flathub com.belmoussaoui.Obfuscate -y
|
|
||||||
|
|
||||||
# Flatseal
|
|
||||||
flatpak install com.github.tchx84.Flatseal -y
|
|
||||||
|
|
||||||
# VeraCrypt installieren
|
|
||||||
read -p "❓ Soll VeraCrypt installiert werden? (j/n) [n]: " answer
|
|
||||||
answer=${answer,,} # In Kleinbuchstaben umwandeln
|
|
||||||
answer=${answer:-n} # Standardwert 'n', falls leer
|
|
||||||
|
|
||||||
if [[ "$answer" == "j" ]] || [[ "$answer" == "y" ]]; then
|
|
||||||
echo "🔄 Installiere VeraCrypt..."
|
|
||||||
|
|
||||||
# VeraCrypt aus den Arch-Repositories installieren
|
|
||||||
if ! command -v veracrypt &>/dev/null; then
|
|
||||||
sudo pacman -S --noconfirm veracrypt
|
|
||||||
else
|
|
||||||
echo "✅ VeraCrypt ist bereits installiert."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Webmin installieren
|
|
||||||
read -p "❓ Soll Webmin installiert werden? (j/n) [n]: " answer
|
|
||||||
answer=${answer,,} # In Kleinbuchstaben umwandeln
|
|
||||||
answer=${answer:-n} # Standardwert 'n', falls leer
|
|
||||||
|
|
||||||
if [[ "$answer" == "j" ]] || [[ "$answer" == "y" ]]; then
|
|
||||||
echo "🔄 Installiere Webmin..."
|
|
||||||
|
|
||||||
# Webmin aus dem AUR installieren (falls yay vorhanden ist)
|
|
||||||
if command -v yay &>/dev/null; then
|
|
||||||
yay -S --noconfirm webmin
|
|
||||||
else
|
|
||||||
echo "❌ Bitte installiere yay oder einen anderen AUR-Helper, um Webmin zu installieren."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Waydroid installieren
|
|
||||||
read -p "❓ Soll Waydroid installiert werden? (j/n) [n]: " answer
|
|
||||||
answer=${answer,,} # In Kleinbuchstaben umwandeln
|
|
||||||
answer=${answer:-n} # Standardwert 'n', falls leer
|
|
||||||
|
|
||||||
if [[ "$answer" == "j" ]] || [[ "$answer" == "y" ]]; then
|
|
||||||
echo "🔄 Installiere Waydroid über AUR..."
|
|
||||||
|
|
||||||
# Überprüfen, ob yay installiert ist
|
|
||||||
if ! command -v yay &>/dev/null; then
|
|
||||||
echo "❌ Yay ist nicht installiert. Bitte installiere yay und versuche es erneut."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Waydroid über AUR installieren
|
|
||||||
yay -S --noconfirm waydroid
|
|
||||||
|
|
||||||
# UFW konfigurieren (falls installiert)
|
|
||||||
if command -v ufw &>/dev/null; then
|
|
||||||
sudo ufw allow 53
|
|
||||||
sudo ufw allow 67
|
|
||||||
sudo ufw default allow FORWARD
|
|
||||||
else
|
|
||||||
echo "❌ UFW ist nicht installiert. Bitte installiere es manuell, falls benötigt."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Waydroid-Container aktivieren
|
|
||||||
sudo systemctl enable --now waydroid-container
|
|
||||||
|
|
||||||
# Skript zum Verstecken der Waydroid-Apps erstellen
|
|
||||||
sudo cat << 'EOF' | sudo tee /etc/profile.d/hide_waydroid_apps.sh > /dev/null
|
|
||||||
for app in ~/.local/share/applications/waydroid.*.desktop; do
|
|
||||||
grep -q NoDisplay $app || sed '/^Icon=/a NoDisplay=true' -i $app
|
|
||||||
done
|
|
||||||
EOF
|
|
||||||
|
|
||||||
sudo chmod +x /etc/profile.d/*
|
|
||||||
|
|
||||||
echo "⚠️ Beachte, dass in Portmaster unter 'Network Noise' die Ports 53 und 67 komplett freigegeben sind!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "✅ Alle Programme wurden erfolgreich installiert!"
|
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
# AGENTS.md — Richtlinien und Architektur-Leitfaden für KI-Agenten
|
||||||
|
|
||||||
|
Dieses Dokument enthält Richtlinien, Architekturstandards, Konventionen und Arbeitsanweisungen für KI-Agenten und Entwickler, die an diesem Repository arbeiten oder es warten.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Projektübersicht
|
||||||
|
|
||||||
|
Dieses Repository ist ein modulares, automatisiertes Setup- und Konfigurations-Framework für **Debian GNU/Linux Unstable (Sid)** mit:
|
||||||
|
- **Hyprland** (Wayland-Compositor) und Waybar-Desktopumgebung
|
||||||
|
- **XanMod-Kernel** (main/edge) mit CPU P-State-Tuning (AMD/Intel) und `auto-cpufreq`
|
||||||
|
- **Bootloader & Splash**: GRUB-Konfiguration mit Vimix-Theme und Plymouth Solar-Splash
|
||||||
|
- **Gaming & Multimedia**: MangoHud, Gamescope, Lutris, Steam, Wine, Pro-Audio via PipeWire
|
||||||
|
- **Virtualisierung & Container**: QEMU/KVM, Libvirt, Docker, Waydroid
|
||||||
|
- **Paket- und Anwendungsmanagement**: Deb822-Quellen, APT-Pinning, Flatpaks (Flathub), isolierte Schriften-Installation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Projekt- und Verzeichnisstruktur
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── install.sh # Web-Installer & Bootstrap-Skript (curl | bash, klont nach /tmp)
|
||||||
|
├── setup.sh # Zentraler Orchestrator (CLI Flags & whiptail TUI)
|
||||||
|
├── lib/ # Wiederverwendbare Funktionsbibliotheken
|
||||||
|
│ ├── utils.sh # Logging, Error-Traps, Benutzer- und Pfadermittlung, Backups
|
||||||
|
│ ├── apt.sh # Deb822-Quellen, Keyrings (/etc/apt/keyrings), APT-Pinning, Paketinstallation
|
||||||
|
│ ├── btrfs.sh # Btrfs Root- & Subvolume-Erkennung, Migration (@, @home), fstab-Verwaltung
|
||||||
|
│ ├── network.sh # Loopback- & LAN-Schnittstellenkonfiguration (Debian Installer Standard)
|
||||||
|
│ └── tui.sh # Whiptail-TUI-Menüs und Dialoge
|
||||||
|
├── config/ # Konfigurationsdateien & Paketlisten
|
||||||
|
│ ├── setup.conf # Globale Umgebungsvariablen und Standardwerte
|
||||||
|
│ └── packages/ # Thematisch gegliederte Paketlisten (*.list)
|
||||||
|
│ ├── 01-base.list # Basiswerkzeuge, Firmware, Microcode
|
||||||
|
│ ├── 02-desktop.list # Hyprland, Waybar, SDDM, XDG-Portale, PipeWire
|
||||||
|
│ ├── 03-gaming.list # Steam, Heroic, Lutris, Gamescope, MangoHud, Wine
|
||||||
|
│ ├── 04-virtualization.list # QEMU, KVM, Libvirt, Bridge-Tools
|
||||||
|
│ ├── 05-multimedia.list # VLC, Amberol, GIMP, Loupe, LibreOffice
|
||||||
|
│ └── 06-flatpaks.list # Flathub App-IDs
|
||||||
|
├── stages/ # Sequenziell und modular ausführbare Phasen (00 bis 11)
|
||||||
|
│ ├── 00-preflight.sh # Systemvoraussetzungen, Root/User-Erkennung, Internetverbindung
|
||||||
|
│ ├── 01-apt-unstable.sh # Deb822-Quellen, Keyrings, Pinning, apt-listbugs, dist-upgrade
|
||||||
|
│ ├── 02-kernel-hardware.sh # XanMod-Kernel, CPU P-State, Microcode, auto-cpufreq, Shader-Cache-Pfad
|
||||||
|
│ ├── 03-bootloader.sh # GRUB Kernel-Parameter, Vimix-Theme, Plymouth Solar
|
||||||
|
│ ├── 04-packages.sh # Installation der thematischen Paketlisten
|
||||||
|
│ ├── 05-fonts.sh # Download & Installation von Coding- & Nerd-Fonts
|
||||||
|
│ ├── 06-services.sh # Aktivierung von AppArmor, Docker, Libvirt, zram, Ollama, GameMode/CoreCtrl, systemd-oomd, scx-Scheduler
|
||||||
|
│ ├── 07-skel-and-user.sh # /etc/skel Vorlagen, ZSH-Shell-Setzen, Dotfiles-Sync
|
||||||
|
│ ├── 08-flatpaks.sh # Flathub Setup, Flatpak-Paketlisten-Installation
|
||||||
|
│ ├── 09-apps.sh # Spotify, Waydroid, OpenDeck, Desktop-Starter, MIME-Types
|
||||||
|
│ ├── 10-hyprland.sh # LinuxBeginnings Debian-Hyprland Auto-Installer & Desktop-Setup
|
||||||
|
│ └── 11-linutil.sh # Linutil (Chris Titus Tech System Toolbox), letzter Install-Schritt
|
||||||
|
├── data/ # Statische Konfigurationsdateien & Vorlagen
|
||||||
|
│ ├── apt/
|
||||||
|
│ │ ├── sources.list.d/ # Deb822-Quellen (debian-unstable.sources, xanmod.sources)
|
||||||
|
│ │ └── preferences.d/ # Pinning-Konfigurationen
|
||||||
|
│ ├── etc/
|
||||||
|
│ │ ├── environment.d/ # Globale Umgebungsvariablen (Raytracing, Upscaling)
|
||||||
|
│ │ ├── gamemode.ini # GameMode Performance-Daemon Defaults (Governor, Renice, GPU)
|
||||||
|
│ │ ├── polkit-1/rules.d/ # 90-corectrl.rules (passwortlose CoreCtrl-Freigabe für sudo-Gruppe)
|
||||||
|
│ │ └── profile.d/ # Globale Profil-Skripte (z. B. XDG User Dirs Update)
|
||||||
|
│ ├── skel/ # Vorlagen für /etc/skel (.zshrc, .config/MangoHud, .config/vkBasalt, Dokumentvorlagen)
|
||||||
|
│ └── usr/share/applications/ # Desktop-Starter-Dateien
|
||||||
|
├── README.md # Projektdokumentation für Endanwender
|
||||||
|
└── LICENSE # MIT-Lizenz
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Architekturprinzipien und Entwicklungsrichtlinien
|
||||||
|
|
||||||
|
### 3.1 Idempotenz (Mehrfache Ausführbarkeit)
|
||||||
|
- Jedes Skript und jede Phase muss **vollständig idempotent** sein: Die wiederholte Ausführung darf keine Fehler erzeugen und keine doppelten Konfigurationseinträge hinterlassen.
|
||||||
|
- Verwende vor dem Hinzufügen von Zeilen zu Konfigurationsdateien Prüfungen (z. B. `grep -q`) oder nutze saubere Konfigurationsverzeichnisse (`.d/`-Verzeichnisse).
|
||||||
|
|
||||||
|
### 3.2 Strikte Trennung von Root- und Benutzerrechten
|
||||||
|
- Die Skripte laufen grundsätzlich mit Root-Rechten (`sudo ./setup.sh`).
|
||||||
|
- **Niemals** Benutzerkonfigurationen unter `/root/` ablegen, wenn sie für den Desktop-Benutzer gedacht sind.
|
||||||
|
- Der Zielbenutzer wird in `$TARGET_USER` gespeichert (ermittelt über `prompt_target_user` in `lib/utils.sh`).
|
||||||
|
- Befehle im Kontext des regulären Benutzers **müssen** über `run_as_user "<befehl>"` ausgeführt werden.
|
||||||
|
- Das Home-Verzeichnis des Zielbenutzers muss über `get_target_home "$TARGET_USER"` ermittelt werden.
|
||||||
|
|
||||||
|
### 3.3 Fehlerbehandlung und Shell-Standards
|
||||||
|
- Alle Bash-Skripte **müssen** mit folgenden Flags starten:
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
```
|
||||||
|
- Binde `lib/utils.sh` ein und rufe `setup_err_trap` am Anfang jedes Skripts auf:
|
||||||
|
```bash
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
# shellcheck source=lib/utils.sh
|
||||||
|
source "$SCRIPT_DIR/../lib/utils.sh"
|
||||||
|
setup_err_trap
|
||||||
|
```
|
||||||
|
- `setup_err_trap` aktiviert `errtrace`/`functrace`, damit Fehler auch innerhalb von `lib/*.sh`-Funktionen abgefangen werden, und registriert `error_handler` als ERR-Trap. Schlägt ein Befehl fehl, wird der Benutzer interaktiv gefragt, ob der Befehl **wiederholt**, **ignoriert** oder die **Installation abgebrochen** werden soll (`ask_retry_ignore_abort`). In nicht-interaktiven Läufen (`CI=1`, `DEBIAN_FRONTEND=noninteractive`, `AUTO_CONFIRM=1`, keine TTY) wird immer automatisch abgebrochen, wie zuvor.
|
||||||
|
- Verwende für temporäre Verzeichnisse immer `mktemp -d` und stelle sicher, dass am Skriptende oder bei Fehlern aufgeräumt wird:
|
||||||
|
```bash
|
||||||
|
TMP_DIR="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$TMP_DIR"' EXIT
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.4 Dateisicherungen (Backups)
|
||||||
|
- Modifiziere bestehende Konfigurationsdateien (z. B. in `/etc/default/grub` oder `/etc/apt/`) nie ohne vorheriges Backup.
|
||||||
|
- Nutze die Bibliotheksfunktion `backup_file "$DATEIPFAD"`. Diese erstellt Backups im Format `<datei>.backup_YYYYMMDD_HHMMSS`.
|
||||||
|
|
||||||
|
### 3.5 Debian-Standards & APT-Hygiene
|
||||||
|
- **Deb822-Format**: Neue Paketquellen ausschließlich im Deb822-Format (`.sources`) unter `/etc/apt/sources.list.d/` ablegen.
|
||||||
|
- **Keyrings**: GPG-Schlüsselbunde gehören nach `/etc/apt/keyrings/` mit Rechten `644`. Das veraltete `apt-key` darf **nicht** verwendet werden.
|
||||||
|
- **Pinning**: APT-Prioritäten in `/etc/apt/preferences.d/` verwalten.
|
||||||
|
- **Nicht-interaktive Paketinstallation**: APT-Befehle müssen immer `DEBIAN_FRONTEND=noninteractive` und `-y` bzw. `--no-install-recommends` (sofern passend) verwenden. Verwende bevorzugt die Wrapper in `lib/apt.sh` (`safe_apt_update`, `install_packages_from_file`).
|
||||||
|
|
||||||
|
### 3.6 Keine gefährlichen Login-Hooks & Shell-Verwaltung
|
||||||
|
- **Keine Live-Downloads** (`curl | sh` / `wget | bash`) in `/etc/profile.d/` oder Login-Skripten.
|
||||||
|
- Dotfiles und Vorlagen gehören nach `/etc/skel/` und werden bei Bedarf einmalig für bestehende Nutzer synchronisiert (`stages/07-skel-and-user.sh`).
|
||||||
|
- Ändere Login-Shells niemals durch direkte Bearbeitung von `/etc/passwd` via `sed` oder `awk`, sondern ausschließlich über `chsh -s "$(command -v zsh)" "$TARGET_USER"`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Hilfsbibliotheken (`lib/`)
|
||||||
|
|
||||||
|
### `lib/utils.sh`
|
||||||
|
- `log_info "Nachricht"`: Blaue Info-Meldung.
|
||||||
|
- `log_success "Nachricht"`: Grüne Erfolgsmeldung (`[✓]`).
|
||||||
|
- `log_warn "Nachricht"`: Gelbe Warnung (`[WARN]`).
|
||||||
|
- `log_error "Nachricht"`: Rote Fehlermeldung (`[ERROR]`).
|
||||||
|
- `log_step "Titel"`: Hervorgehobener Abschnitts-Header.
|
||||||
|
- `log_substep "Titel"`: Eingerückter Unterabschnitt.
|
||||||
|
- `setup_err_trap`: Aktiviert `errtrace`/`functrace` und registriert `error_handler` als ERR-Trap für das gesamte Skript (inkl. `lib/*.sh`-Funktionen).
|
||||||
|
- `error_handler` / `ask_retry_ignore_abort` / `resume_after_error`: Zentrale Fehlerbehandlung. Bei einem fehlgeschlagenen Befehl wird interaktiv nach Wiederholen/Ignorieren/Abbrechen gefragt; nicht-interaktiv wird immer abgebrochen.
|
||||||
|
- `require_root`: Bricht ab, wenn das Skript nicht mit Root-Rechten ausgeführt wird.
|
||||||
|
- `command_exists "tool"`: Prüft, ob ein Befehl im Pfad verfügbar ist.
|
||||||
|
- `prompt_target_user`: Erkennt `$SUDO_USER` oder fragt interaktiv nach dem Zielbenutzer.
|
||||||
|
- `prompt_ollama_models_path`: Fragt interaktiv nach dem Speicherort für Ollama KI-Modelle oder nutzt Standardpfad.
|
||||||
|
- `get_target_home [username]`: Gibt den absoluten Pfad des Home-Verzeichnisses des Nutzers zurück.
|
||||||
|
- `run_as_user "command"`: Führt einen Befehl als `$TARGET_USER` via `su - "$TARGET_USER" -c ...` aus.
|
||||||
|
- `backup_file "filepath"`: Erstellt ein Backup einer Datei mit Zeitstempel.
|
||||||
|
|
||||||
|
### `lib/apt.sh`
|
||||||
|
- `safe_apt_update`: Führt ein abgesichertes `apt-get update` durch.
|
||||||
|
- `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`.
|
||||||
|
|
||||||
|
### `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/network.sh`
|
||||||
|
- `get_lan_interfaces`: Erkennt alle physischen LAN/Ethernet-Schnittstellen dynamisch auf jedem System (unter Ausschluss von Loopback, Wi-Fi, Bridges und virtuellen Containern).
|
||||||
|
- `is_interface_configured "iface"`: Prüft, ob eine Schnittstelle bereits in `/etc/network/interfaces` oder `/etc/network/interfaces.d/` konfiguriert ist.
|
||||||
|
- `configure_loopback_interface`: Richtet das Loopback-Interface `lo` in `/etc/network/interfaces` (`auto lo`, `iface lo inet loopback`) sowie `/etc/hosts` ein und aktiviert es.
|
||||||
|
- `configure_lan_interfaces`: Richtet alle erkannten physischen LAN-Schnittstellen im Debian-Installer-Standard (`allow-hotplug <iface>`, `iface <iface> inet dhcp`, `iface <iface> inet6 auto`) ein und aktiviert diese.
|
||||||
|
- `configure_network_manager`: Konfiguriert `/etc/NetworkManager/NetworkManager.conf` mit `[ifupdown] managed=true` für nahtlose Desktop- und Waybar-Integration.
|
||||||
|
- `enable_network_services`: Aktiviert und startet `networking.service` und `NetworkManager.service`.
|
||||||
|
- `configure_network_all`: Zentraler Orchestrator zur vollständigen Netzwerkeinrichtung.
|
||||||
|
|
||||||
|
### `lib/tui.sh`
|
||||||
|
- `tui_check_whiptail`: Stellt sicher, dass `whiptail` installiert ist.
|
||||||
|
- `tui_welcome`: Zeigt den Begrüßungsdialog.
|
||||||
|
- `tui_select_stages`: Zeigt ein `whiptail` Checklist-Menü zur interaktiven Auswahl der auszuführenden Phasen.
|
||||||
|
- `tui_prompt_target_user [default_user]`: Bestätigt oder erfragt den Desktop-Zielbenutzer per Dialog und exportiert `TARGET_USER`.
|
||||||
|
- `tui_confirm_execution "stages" "user"`: Bestätigungsdialog vor dem Start der Installation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Arbeitsanweisungen für Modifikationen
|
||||||
|
|
||||||
|
### Neue Phase (Stage) hinzufügen
|
||||||
|
1. Erstelle eine neue Datei `stages/XX-<name>.sh` (nummeriert nach Reihenfolge, mit `chmod +x`).
|
||||||
|
2. Nutze das Standard-Template:
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# stages/XX-<name>.sh - Kurzbeschreibung der Phase
|
||||||
|
# ==============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
# shellcheck source=lib/utils.sh
|
||||||
|
source "$SCRIPT_DIR/../lib/utils.sh"
|
||||||
|
|
||||||
|
setup_err_trap
|
||||||
|
require_root
|
||||||
|
|
||||||
|
log_step "Stage XX: <Name der Phase>"
|
||||||
|
|
||||||
|
TARGET_USER="${TARGET_USER:-$(prompt_target_user)}"
|
||||||
|
TARGET_HOME="$(get_target_home "$TARGET_USER")"
|
||||||
|
|
||||||
|
# Implementierung (idempotent) ...
|
||||||
|
|
||||||
|
log_success "Stage XX abgeschlossen."
|
||||||
|
```
|
||||||
|
3. Registriere die neue Phase im Orchestrator `setup.sh` (Hilfetext `show_help`, Validierung in `run_stages`).
|
||||||
|
4. Ergänze die Phase im TUI-Auswahlmenü in `lib/tui.sh`.
|
||||||
|
5. Aktualisiere `README.md`.
|
||||||
|
|
||||||
|
### Paketlisten anpassen
|
||||||
|
- Paketdateien liegen unter `config/packages/<kategorie>.list`.
|
||||||
|
- Jede Zeile enthält genau einen Paketnamen.
|
||||||
|
- Kommentare beginnen mit `#`. Leerzeilen sind erlaubt.
|
||||||
|
|
||||||
|
### Vorlagen und Skel-Dateien hinzufügen
|
||||||
|
- Konfigurationsvorlagen für neue Benutzer gehören in `data/skel/`.
|
||||||
|
- Dateivorlagen für den Dateimanager (XDG Templates) gehören nach `data/skel/Templates/`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Verifikation & Qualitätssicherung
|
||||||
|
|
||||||
|
Bevor Änderungen eingecheckt oder abgeschlossen werden:
|
||||||
|
|
||||||
|
1. **ShellCheck ausführen**:
|
||||||
|
Alle Bash-Skripte auf statische Fehler und Warnungen prüfen:
|
||||||
|
```bash
|
||||||
|
shellcheck setup.sh lib/*.sh stages/*.sh
|
||||||
|
```
|
||||||
|
2. **Dry-Run Validierung**:
|
||||||
|
Sicherstellen, dass der Orchestrator im Simulationsmodus funktioniert:
|
||||||
|
```bash
|
||||||
|
./setup.sh --dry-run --stages 00,01,02
|
||||||
|
```
|
||||||
|
3. **Syntaxprüfung**:
|
||||||
|
```bash
|
||||||
|
bash -n setup.sh
|
||||||
|
for f in lib/*.sh stages/*.sh; do bash -n "$f"; done
|
||||||
|
```
|
||||||
|
4. **Pfad- und Berechtigungskonsistenz**:
|
||||||
|
Prüfen, dass keine absoluten Pfade auf Benutzerverzeichnisse (`/home/...`) hartcodiert sind.
|
||||||
@@ -1,34 +1,189 @@
|
|||||||
# Setup
|
# Debian Unstable (Sid) & Hyprland Setup Framework
|
||||||
|
|
||||||
## Übersicht
|
Ein modulares, sicheres und erweiterbares Framework zur automatisierten Einrichtung von **Debian GNU/Linux Unstable (Sid)** mit Hyprland Desktop, XanMod-Kernel, Hardware-Tuning und thematisch gegliederten Anwendungen.
|
||||||
|
|
||||||
Dieses Repository enthält Skripte um ein Linux System mit Hyprland und diversen Programmen aufzusetzen.
|
---
|
||||||
Die entsprechenden Skripte befinden sich in den Branches zur jeweiligen Distribution.
|
|
||||||
|
|
||||||
**Mit diesen Skripten kann *ARCH Linux* mit *Hyprland* und diversen Programmen aufgesetzt werden.**
|
## 🚀 Schnellstart
|
||||||
|
|
||||||
## Anforderungen
|
### 1. Web-Installer (Curl One-Liner)
|
||||||
|
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:
|
||||||
|
|
||||||
Um die Skripte in diesem Repository auszuführen, stelle sicher, dass die folgenden Voraussetzungen erfüllt sind:
|
```bash
|
||||||
|
# Interaktiver Modus (whiptail Menü)
|
||||||
|
curl -fsSL https://gitea.creative-dragonslayer.de/Scripts/Setup/raw/branch/main/install.sh | bash
|
||||||
|
|
||||||
- Die Skripte sind als bash-Skripte konzipiert.
|
# Vollautomatischer Modus (alle Stages 00 bis 11)
|
||||||
- Für die Skripte muss der richtige Branch der jewiligen Distribution ausgewählt werden.
|
curl -fsSL https://gitea.creative-dragonslayer.de/Scripts/Setup/raw/branch/main/install.sh | bash -s -- --all
|
||||||
- Daten werden vor der Ausführung nicht gesichert. Datenverlust möglich!
|
|
||||||
|
|
||||||
## Nutzung
|
# 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
|
||||||
|
```
|
||||||
|
|
||||||
1. Klone das Repository:
|
### 2. Manuelles Klonen & Starten
|
||||||
```bash
|
Alternativ kann das Repository manuell geklont und ausgeführt werden:
|
||||||
git clone -b arch https://gitea.creative-dragonslayer.de/DragonSlayer_14/Setup.git
|
```bash
|
||||||
```
|
git clone https://gitea.creative-dragonslayer.de/Scripts/Setup.git && cd Setup && ./setup.sh
|
||||||
2. Wechsle in den richtigen Branch.
|
```
|
||||||
3. Navigiere in das Verzeichnis `Setup`, um die ausführbaren Dateien und Ressourcen zu finden.
|
Oder direkt vollautomatisch alle Stages (`00` bis `10`) ausführen:
|
||||||
4. Folge den spezifischen Anweisungen in einzelnen Dateien oder Skripten für die korrekte Ausführung.
|
```bash
|
||||||
|
git clone https://gitea.creative-dragonslayer.de/Scripts/Setup.git && cd Setup && ./setup.sh --all
|
||||||
|
```
|
||||||
|
|
||||||
## Lizenz
|
> `setup.sh` fragt zuerst alle nötigen Eingaben (Modulauswahl, Zielbenutzer, ...) direkt am Terminal ab und eskaliert erst danach selbstständig per `sudo`. Ein manuelles `sudo` davor ist nicht nötig, funktioniert aber ebenfalls.
|
||||||
|
|
||||||
Dieses Projekt steht unter einer Open-Source-Lizenz. Weitere Informationen findest du in der Datei `LICENSE`.
|
### 3. Interaktiver TUI-Modus
|
||||||
|
Starte das grafische Terminal-Menü (`whiptail`) zur flexiblen Auswahl einzelner Module:
|
||||||
|
```bash
|
||||||
|
./setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
## Autor
|
### 4. Vollautomatischer CLI-Modus
|
||||||
|
Führe alle Phasen (`00` bis `10`) ohne Interaktion aus:
|
||||||
|
```bash
|
||||||
|
./setup.sh --all
|
||||||
|
```
|
||||||
|
|
||||||
Erstellt und gepflegt von DragonSlayer_14.
|
### 5. Gezielte Modulauswahl
|
||||||
|
Installiere nur bestimmte Phasen (z. B. nur Paketlisten und Schriften):
|
||||||
|
```bash
|
||||||
|
./setup.sh --stages 04,05
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Simulationslauf (Dry-Run)
|
||||||
|
Überprüfe geplante Aktionen, ohne Änderungen am System vorzunehmen:
|
||||||
|
```bash
|
||||||
|
./setup.sh --dry-run --stages 01,02,04
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ CLI-Optionen
|
||||||
|
|
||||||
|
| Option | Argument | Beschreibung |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `-a`, `--all` | – | Führt alle Phasen (`00` bis `10`) sequenziell aus |
|
||||||
|
| `-s`, `--stages` | `<01,02,...>` | Kommagetrennte Liste der auszuführenden Phasen |
|
||||||
|
| `-u`, `--user` | `<username>` | Zielbenutzer für Userland/Dotfiles (Standard: `$SUDO_USER` mit Bestätigungsabfrage) |
|
||||||
|
| `-d`, `--dry-run` | – | Simuliert die Ausführung ohne Systemänderungen |
|
||||||
|
| `-h`, `--help` | – | Zeigt die Hilfe und alle verfügbaren Stages an |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 Projektstruktur
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── install.sh # Web-Installer & Bootstrap-Skript (curl | bash, klont nach /tmp)
|
||||||
|
├── setup.sh # Zentraler Orchestrator (CLI Flags & whiptail TUI)
|
||||||
|
├── lib/ # Wiederverwendbare Hilfsbibliotheken
|
||||||
|
│ ├── utils.sh # Logging, Error-Traps (set -euo pipefail), Root/User-Erkennung & Abfrage
|
||||||
|
│ ├── apt.sh # APT-Keyring-Management (/etc/apt/keyrings/), Deb822, Pinning
|
||||||
|
│ ├── btrfs.sh # Btrfs Root- & Subvolume-Erkennung, Migration (@, @home), Timeshift-Snapshots
|
||||||
|
│ ├── network.sh # Loopback- & LAN-Schnittstellenkonfiguration (Debian Installer Standard)
|
||||||
|
│ └── tui.sh # Whiptail-Menüs und Dialoge
|
||||||
|
├── config/ # Konfigurationsdateien & Paketlisten
|
||||||
|
│ ├── setup.conf # Globale Variablen (Kernel, GRUB-Theme, Plymouth)
|
||||||
|
│ └── packages/ # Thematische Paketlisten
|
||||||
|
│ ├── 01-base.list # Grundlegende Systemwerkzeuge, Firmware, Microcode
|
||||||
|
│ ├── 02-desktop.list # Hyprland, Waybar, SDDM, Portale, Pipewire
|
||||||
|
│ ├── 03-gaming.list # Steam, Heroic, Lutris, Gamescope, MangoHud, Wine
|
||||||
|
│ ├── 04-virtualization.list # QEMU, KVM, Libvirt, Bridge-Tools
|
||||||
|
│ ├── 05-multimedia.list # VLC, Amberol, GIMP, Loupe, LibreOffice
|
||||||
|
│ └── 06-flatpaks.list # Flatpak App-IDs & Flathub
|
||||||
|
├── stages/ # Sequenzielle, modular ausführbare Phasen
|
||||||
|
│ ├── 00-preflight.sh # Prüfung von Rechten, Ziel-User ($SUDO_USER) und Internet
|
||||||
|
│ ├── 01-apt-unstable.sh # Deb822-Quellen, Keyrings, Pinning, apt-listbugs, dist-upgrade
|
||||||
|
│ ├── 02-kernel-hardware.sh # XanMod-Kernel (main/edge), CPU P-State, Microcode, auto-cpufreq (Source)
|
||||||
|
│ ├── 03-bootloader.sh # GRUB Kernel-Parameter, Vimix-Theme, Plymouth Solar
|
||||||
|
│ ├── 04-packages.sh # Installation der ausgewählten thematischen Paketlisten
|
||||||
|
│ ├── 05-fonts.sh # Download & Installation von JetBrainsMono, Fantasque, VictorMono
|
||||||
|
│ ├── 06-services.sh # Aktivierung von AppArmor, Docker, Libvirt, zram, Ollama
|
||||||
|
│ ├── 07-skel-and-user.sh # /etc/skel Vorlagen, ZSH-Shell-Setzen, Dotfiles-Sync
|
||||||
|
│ ├── 08-flatpaks.sh # Flathub Setup, Flatpak-Paketlisten-Installation
|
||||||
|
│ ├── 09-apps.sh # Spotify, Waydroid, OpenDeck, Desktop-Starter, MIME-Types
|
||||||
|
│ ├── 10-hyprland.sh # LinuxBeginnings Debian-Hyprland Auto-Installer & Dotfiles
|
||||||
|
│ └── 11-linutil.sh # Linutil (Chris Titus Tech System Toolbox), letzter Install-Schritt
|
||||||
|
└── data/ # Statische Konfigurationsdateien & Vorlagen
|
||||||
|
├── apt/
|
||||||
|
│ ├── sources.list.d/ # debian-unstable.sources, xanmod.sources
|
||||||
|
│ └── preferences.d/ # Pinning-Konfigurationen (unstable, xanmod)
|
||||||
|
├── etc/
|
||||||
|
│ ├── environment.d/ # 99-raytracing.conf, 99-upscaling.conf (99-shader-cache.conf wird optional zur Laufzeit generiert)
|
||||||
|
│ ├── gamemode.ini # GameMode Performance-Daemon Defaults
|
||||||
|
│ ├── polkit-1/rules.d/ # 90-corectrl.rules (passwortlose CoreCtrl-Freigabe für sudo-Gruppe)
|
||||||
|
│ └── profile.d/ # Bereinigte System-Profile-Skripte
|
||||||
|
├── skel/ # .zshrc, .config/hypr, .config/MangoHud, .config/vkBasalt, Templates für Dokumente & Skripte
|
||||||
|
└── usr/share/applications/ # Desktop-Dateien (z. B. Spotify)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 Sicherheits- & Stabilitätsmerkmale
|
||||||
|
|
||||||
|
1. **Rechte- und Nutzertrennung**:
|
||||||
|
- Automatische Erkennung des regulären Desktop-Benutzers via `$SUDO_USER` / interactivem Fallback.
|
||||||
|
- Benutzerkonfigurationen und Gruppenrechte (`sudo`, `docker`, `libvirt`) werden sauber für den Zielbenutzer gesetzt.
|
||||||
|
2. **Keine gefährlichen Login-Hooks**:
|
||||||
|
- Vollständige Beseitigung unkontrollierter Live-Downloads (`curl | sh`) beim Login.
|
||||||
|
- Standardkonforme Bereitstellung über `/etc/skel` für neue und bestehende Benutzer.
|
||||||
|
3. **Idempotenz, Backups & Timeshift Btrfs-Snapshots**:
|
||||||
|
- 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.
|
||||||
|
- 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**:
|
||||||
|
- 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.
|
||||||
|
5. **Sichere Shell-Verwaltung**:
|
||||||
|
- Standardkonforme Änderung der Login-Shell via `chsh -s` statt direkter `/etc/passwd`-Modifikation.
|
||||||
|
6. **Netzwerk- & Schnittstellenkonfiguration (Debian Installer Standard)**:
|
||||||
|
- Automatische Erkennung und Konfiguration aller physischen LAN-Netzwerkschnittstellen via DHCP und IPv6 SLAAC (`allow-hotplug <iface>`, `iface <iface> inet dhcp`, `iface <iface> inet6 auto`).
|
||||||
|
- Standardkonforme Einrichtung des Loopback-Interfaces (`auto lo`, `iface lo inet loopback`) in `/etc/network/interfaces` und `/etc/hosts`.
|
||||||
|
- Nahtlose NetworkManager-Integration (`managed=true`) und Berechtigung des Zielbenutzers via `netdev`-Gruppe.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎮 Gaming & Performance-Optimierungen
|
||||||
|
|
||||||
|
### GameMode, MangoHud & CoreCtrl
|
||||||
|
- **GameMode**: `stages/06-services.sh` rollt eine getunte `/etc/gamemode.ini` aus (`desiredgov=performance`, Renice, I/O-Priorität, AMD-GPU-Performance-Level `high`) und fügt den Zielbenutzer automatisch den Gruppen `gamemode` (Renice/Core-Parking) und `render` (Vulkan/GPU-Compute) hinzu.
|
||||||
|
- **MangoHud**: `data/skel/.config/MangoHud/MangoHud.conf` liefert ein vorkonfiguriertes Overlay (FPS, Frametime, CPU/GPU-Temperatur & -Takt, VRAM/RAM, GameMode-Indikator), das über `stages/07-skel-and-user.sh` automatisch für neue und bestehende Benutzer bereitgestellt wird. Umschalten mit `Shift+F12` (Position mit `Shift+F11`).
|
||||||
|
- **CoreCtrl**: Für Fankurven, Power-States und Undervolting von AMD-CPU/GPU. Eine PolicyKit-Regel (`90-corectrl.rules`) erlaubt Mitgliedern der `sudo`-Gruppe den Start ohne wiederholte Passwortabfrage.
|
||||||
|
|
||||||
|
### systemd-oomd (Out-of-Memory-Schutz)
|
||||||
|
- Wird aktiviert, um Einfrieren des Systems bei gleichzeitiger Speicher-/Swap-Auslastung (Spiel + Discord + Browser) zu verhindern – ergänzt das bestehende zram-Swap.
|
||||||
|
|
||||||
|
### Sched-ext Gaming-Scheduler
|
||||||
|
- **sched-ext (`scx-scheds`)**: `stages/06-services.sh` installiert das Paket aus dem bereits konfigurierten XanMod-Repository und aktiviert den Scheduler `scx_lavd` (Latency-criticality Aware Virtual Deadline – speziell für Gaming/interaktive Desktop-Workloads) im `--autopilot`-Modus über `/etc/default/scx` und `scx.service`. Benötigt einen Kernel mit `CONFIG_SCHED_CLASS_EXT` (bei XanMod bereits aktiv); nach einer frischen Kernel-Installation ist ggf. ein Neustart nötig.
|
||||||
|
|
||||||
|
### vkBasalt (Vulkan-Postprocessing)
|
||||||
|
- ReShade-ähnliche Vulkan-Postprocessing-Shader (Contrast Adaptive Sharpening als Standard) mit vorkonfigurierter `~/.config/vkBasalt/vkBasalt.conf`. Aktivierung pro Spiel über die Steam-Launch-Option `ENABLE_VKBASALT=1 %command%`; Status wird von MangoHud mit angezeigt.
|
||||||
|
|
||||||
|
### Persistenter Shader-Cache (optional)
|
||||||
|
- Standardmäßig bleiben Mesa (RADV/OpenGL) und DXVK (Wine/Proton) bei ihren eigenen Cache-Pfaden (`~/.cache/mesa_shader_cache`, `~/.cache/dxvk`). Wird `SHADER_CACHE_DIR` in `config/setup.conf` gesetzt (z. B. auf eine separate/schnelle Festplatte), verlegt `stages/02-kernel-hardware.sh` beide Caches dorthin, um Nachcompilierungs-Ruckler nach Neustarts zu vermeiden.
|
||||||
|
|
||||||
|
### CPU P-State & Kernel-Parameter
|
||||||
|
In `stages/03-bootloader.sh` wird der CPU-Hersteller automatisch ermittelt und in `/etc/default/grub` eingetragen:
|
||||||
|
- **AMD CPUs**: `amd_pstate=active`
|
||||||
|
- **Intel CPUs**: `intel_pstate=active`
|
||||||
|
- **Latenz-Optimierung**: `rcutree.rcu_idle_gp_delay=1 threadirqs`
|
||||||
|
|
||||||
|
### Game Launch-Parameter (Steam / Lutris)
|
||||||
|
```bash
|
||||||
|
# Standard Gaming Launch-Optionen mit MangoHud & Gamescope
|
||||||
|
gamemoderun gamescope -W 2560 -H 1440 --force-grab-cursor --hdr-enabled --adaptive-sync -f -o 240 -r 240 -- mangohud %command%
|
||||||
|
|
||||||
|
# Raytracing für DX12 Titel (RADV / Mesa)
|
||||||
|
RADV_PERFTEST=gpl,rt,ngg_streamout VKD3D_CONFIG=dxr,dxr11 DXVK_ASYNC=1 AMD_VULKAN_ICD=RADV gamemoderun gamescope -W 2560 -H 1440 --force-grab-cursor --hdr-enabled --adaptive-sync -f -o 240 -r 240 -- mangohud %command%
|
||||||
|
|
||||||
|
# Mit vkBasalt-Postprocessing (Schärfung via Contrast Adaptive Sharpening)
|
||||||
|
ENABLE_VKBASALT=1 gamemoderun gamescope -W 2560 -H 1440 --force-grab-cursor --hdr-enabled --adaptive-sync -f -o 240 -r 240 -- mangohud %command%
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📜 Lizenz & Autor
|
||||||
|
|
||||||
|
- **Lizenz**: MIT License (siehe `LICENSE`)
|
||||||
|
- **Autor**: DragonSlayer_14
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
# ==============================================================================
|
||||||
|
# config/packages/01-base.list - Base system utilities, firmware, shell & diagnostics
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
7zip
|
||||||
|
adb
|
||||||
|
apparmor
|
||||||
|
apparmor-notify
|
||||||
|
apparmor-profiles
|
||||||
|
apparmor-profiles-extra
|
||||||
|
apparmor-utils
|
||||||
|
apt-listbugs
|
||||||
|
apt-listchanges
|
||||||
|
aptitude
|
||||||
|
baobab
|
||||||
|
bat
|
||||||
|
bleachbit
|
||||||
|
bluetooth
|
||||||
|
blueman
|
||||||
|
bluez
|
||||||
|
bruno
|
||||||
|
btop
|
||||||
|
ca-certificates
|
||||||
|
clamav
|
||||||
|
clamav-daemon
|
||||||
|
clamav-docs
|
||||||
|
clamav-freshclam
|
||||||
|
command-not-found
|
||||||
|
cpupower-gui
|
||||||
|
cryptomator
|
||||||
|
curl
|
||||||
|
dconf-editor
|
||||||
|
debian-goodies
|
||||||
|
ethtool
|
||||||
|
fastfetch
|
||||||
|
file-roller
|
||||||
|
firmware-linux
|
||||||
|
firmware-linux-nonfree
|
||||||
|
firmware-misc-nonfree
|
||||||
|
flatpak
|
||||||
|
flatseal
|
||||||
|
fzf
|
||||||
|
git
|
||||||
|
glances
|
||||||
|
gnome-disk-utility
|
||||||
|
gnome-firmware
|
||||||
|
gnome-logs
|
||||||
|
gnome-system-monitor
|
||||||
|
gparted
|
||||||
|
htop
|
||||||
|
hunspell
|
||||||
|
hunspell-de-de-frami
|
||||||
|
ifupdown
|
||||||
|
inotify-tools
|
||||||
|
iproute2
|
||||||
|
irqbalance
|
||||||
|
isc-dhcp-client
|
||||||
|
keepassxc
|
||||||
|
lsd
|
||||||
|
man
|
||||||
|
net-tools
|
||||||
|
network-manager
|
||||||
|
network-manager-gnome
|
||||||
|
plymouth
|
||||||
|
plymouth-themes
|
||||||
|
resources
|
||||||
|
rfkill
|
||||||
|
ripgrep
|
||||||
|
rpi-imager
|
||||||
|
shellcheck
|
||||||
|
sl
|
||||||
|
systemd-oomd
|
||||||
|
systemd-timesyncd
|
||||||
|
tar
|
||||||
|
tealdeer
|
||||||
|
timeshift
|
||||||
|
topgrade
|
||||||
|
tree
|
||||||
|
unrar
|
||||||
|
unzip
|
||||||
|
wireless-regdb
|
||||||
|
wpasupplicant
|
||||||
|
zram-tools
|
||||||
|
zsh
|
||||||
|
zsh-autosuggestions
|
||||||
|
zsh-doc
|
||||||
|
zsh-syntax-highlighting
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# ==============================================================================
|
||||||
|
# config/packages/02-desktop.list - Wayland/Hyprland desktop, display manager & tools
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
cava
|
||||||
|
codium
|
||||||
|
dconf-editor
|
||||||
|
flameshot
|
||||||
|
floorp
|
||||||
|
fragments
|
||||||
|
gnome-calculator
|
||||||
|
gnome-decoder
|
||||||
|
gnome-font-viewer
|
||||||
|
gnome-keyring
|
||||||
|
gnome-snapshot
|
||||||
|
gnome-text-editor
|
||||||
|
grim
|
||||||
|
impression
|
||||||
|
input-remapper
|
||||||
|
kitty
|
||||||
|
localsend
|
||||||
|
nodejs
|
||||||
|
nwg-displays
|
||||||
|
nwg-look
|
||||||
|
obfuscate
|
||||||
|
pipewire
|
||||||
|
pipewire-pulse
|
||||||
|
remmina
|
||||||
|
sddm
|
||||||
|
slurp
|
||||||
|
speech-dispatcher
|
||||||
|
swappy
|
||||||
|
swaybg
|
||||||
|
thunar
|
||||||
|
vesktop
|
||||||
|
wayland-utils
|
||||||
|
wireplumber
|
||||||
|
wl-clipboard
|
||||||
|
xdg-user-dirs
|
||||||
|
yazi
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# ==============================================================================
|
||||||
|
# config/packages/03-gaming.list - Gaming tools, Vulkan drivers, Wine & Emulation
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
corectrl
|
||||||
|
gameconqueror
|
||||||
|
gamemode
|
||||||
|
gamescope
|
||||||
|
heroic
|
||||||
|
lutris
|
||||||
|
mangohud
|
||||||
|
mangoapp
|
||||||
|
mesa-utils
|
||||||
|
mesa-vulkan-drivers
|
||||||
|
openrgb
|
||||||
|
prismlauncher
|
||||||
|
protontricks
|
||||||
|
radeontop
|
||||||
|
steam-installer
|
||||||
|
steam-devices
|
||||||
|
vkbasalt
|
||||||
|
vkbasalt:i386
|
||||||
|
vulkan-tools
|
||||||
|
wine
|
||||||
|
winetricks
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# ==============================================================================
|
||||||
|
# config/packages/04-virtualization.list - QEMU, KVM & Libvirt packages
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
bridge-utils
|
||||||
|
libvirt-clients
|
||||||
|
libvirt-daemon-system
|
||||||
|
qemu-system-x86
|
||||||
|
qemu-utils
|
||||||
|
virt-manager
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# ==============================================================================
|
||||||
|
# config/packages/05-multimedia.list - Audio, Video, Graphics & Productivity
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
amberol
|
||||||
|
easyeffects
|
||||||
|
ffmpeg
|
||||||
|
gimp
|
||||||
|
libreoffice
|
||||||
|
libreoffice-gtk4
|
||||||
|
libreoffice-l10n-de
|
||||||
|
loupe
|
||||||
|
simple-scan
|
||||||
|
vlc
|
||||||
|
vlc-data
|
||||||
|
vlc-l10n
|
||||||
|
vlc-plugin-access-extra
|
||||||
|
vlc-plugin-base
|
||||||
|
vlc-plugin-notify
|
||||||
|
vlc-plugin-pipewire
|
||||||
|
vlc-plugin-qt
|
||||||
|
vlc-plugin-video-output
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# ==============================================================================
|
||||||
|
# config/packages/06-flatpaks.list - Flathub Flatpak Application IDs
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# Communication & Productivity
|
||||||
|
eu.betterbird.Betterbird
|
||||||
|
im.fluffychat.Fluffychat
|
||||||
|
org.pvermeer.WebAppHub
|
||||||
|
io.github.giantpinkrobots.flatsweep
|
||||||
|
|
||||||
|
# Creativity & AI
|
||||||
|
org.musescore.MuseScore
|
||||||
|
com.jeffser.Alpaca
|
||||||
|
|
||||||
|
# Gaming & Compatibility
|
||||||
|
net.davidotek.pupgui2
|
||||||
|
io.github.rfrench3.scopebuddy-gui
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# ==============================================================================
|
||||||
|
# config/setup.conf - Configuration parameters for Debian Unstable Setup
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# Kernel & Hardware
|
||||||
|
# XanMod branches: "main" (stable & recommended), "edge" (bleeding-edge), "lts" (long-term support), "rt" (real-time)
|
||||||
|
XANMOD_BRANCH="main"
|
||||||
|
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
|
||||||
|
GRUB_THEME="vimix"
|
||||||
|
PLYMOUTH_THEME="solar"
|
||||||
|
GRUB_TIMEOUT=5
|
||||||
|
GRUB_TIMEOUT_STYLE="menu"
|
||||||
|
|
||||||
|
# Shell & Userland
|
||||||
|
DEFAULT_SHELL="zsh"
|
||||||
|
|
||||||
|
# Gaming: Persistent Shader Cache (Mesa & DXVK)
|
||||||
|
# Optional relocation of the shader/pipeline cache onto separate/bulk storage.
|
||||||
|
# Leave empty to keep Mesa's and DXVK's own default cache locations
|
||||||
|
# (~/.cache/mesa_shader_cache, ~/.cache/dxvk) - recommended unless you have a
|
||||||
|
# specific reason (small root disk, dedicated fast scratch drive, ...).
|
||||||
|
# Example: SHADER_CACHE_DIR="/mnt/Data/Software/mesa_shader_cache"
|
||||||
|
SHADER_CACHE_DIR=""
|
||||||
|
|
||||||
|
# Ollama AI Configuration
|
||||||
|
# Suggested custom models path, pre-filled only if the user declines
|
||||||
|
# Ollama's own default storage location (/usr/share/ollama/.ollama/models).
|
||||||
|
OLLAMA_MODELS_DEFAULT="/mnt/Data/Software/ollama/models"
|
||||||
|
OLLAMA_FLASH_ATTENTION="1"
|
||||||
|
OLLAMA_KV_CACHE_TYPE="q8_0"
|
||||||
|
OLLAMA_KEEP_ALIVE="30m"
|
||||||
|
|
||||||
|
# Default Stages (if --all is passed or in default batch mode)
|
||||||
|
ALL_STAGES="00 01 02 03 04 05 06 07 08 09 10"
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
Package: *
|
||||||
|
Pin: release a=unstable
|
||||||
|
Pin-Priority: 900
|
||||||
|
|
||||||
|
Package: *
|
||||||
|
Pin: release a=experimental
|
||||||
|
Pin-Priority: 100
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
Package: *
|
||||||
|
Pin: origin deb.xanmod.org
|
||||||
|
Pin-Priority: 900
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
Types: deb deb-src
|
||||||
|
URIs: http://deb.debian.org/debian/
|
||||||
|
Suites: unstable
|
||||||
|
Components: main non-free-firmware non-free contrib
|
||||||
|
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Types: deb
|
||||||
|
URIs: https://gitea.creative-dragonslayer.de/api/packages/Linuxapps/debian/
|
||||||
|
Suites: stable
|
||||||
|
Components: main
|
||||||
|
Signed-By: /etc/apt/keyrings/gitea-Linuxapps.asc
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
Types: deb
|
||||||
|
URIs: https://gitea.creative-dragonslayer.de/api/packages/Mirror/debian/
|
||||||
|
Suites: stable
|
||||||
|
Components: main
|
||||||
|
Architectures: amd64
|
||||||
|
Signed-By: /etc/apt/keyrings/gitea-Mirror.asc
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Types: deb
|
||||||
|
URIs: http://deb.xanmod.org
|
||||||
|
Suites: releases
|
||||||
|
Components: main
|
||||||
|
Signed-By: /etc/apt/keyrings/xanmod-archive-keyring.gpg
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Raytracing support environment flags for RADV / Mesa
|
||||||
|
VKD3D_CONFIG=dxr11,dxr
|
||||||
|
RADV_PERFTEST=rt
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Upscaling and gaming enhancement variables
|
||||||
|
WINE_FULLSCREEN_FSR=1
|
||||||
|
WINE_FULLSCREEN_FSR_STRENGTH=2
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# ==============================================================================
|
||||||
|
# data/etc/gamemode.ini - GameMode performance daemon defaults
|
||||||
|
# ==============================================================================
|
||||||
|
# Deployed to /etc/gamemode.ini by stages/06-services.sh
|
||||||
|
# Full reference: /usr/share/gamemode/gamemode.ini
|
||||||
|
|
||||||
|
[general]
|
||||||
|
; Use the "performance" CPU governor while a game is running instead of
|
||||||
|
; "powersave"/"ondemand", restoring the previous governor once it exits.
|
||||||
|
desiredgov=performance
|
||||||
|
|
||||||
|
; Renice game processes for smoother frame pacing (0-20, applied as a negated
|
||||||
|
; nice value). Requires the target user to be a member of the "gamemode"
|
||||||
|
; group, which the setup framework adds automatically.
|
||||||
|
renice=5
|
||||||
|
|
||||||
|
; Lower I/O priority class while gaming (0 = highest Best-Effort priority).
|
||||||
|
ioprio=0
|
||||||
|
|
||||||
|
; Keep the screen from blanking/locking while a game has focus.
|
||||||
|
inhibit_screensaver=1
|
||||||
|
|
||||||
|
[gpu]
|
||||||
|
; Apply AMD/NVIDIA GPU performance-level optimisations while gaming.
|
||||||
|
apply_gpu_optimisations=accept-responsibility
|
||||||
|
gpu_device=0
|
||||||
|
|
||||||
|
; AMD: force the "high" DPM performance level during GameMode sessions
|
||||||
|
; (requires an up-to-date amdgpu kernel module).
|
||||||
|
amd_performance_level=high
|
||||||
|
|
||||||
|
; NVIDIA-specific PowerMizer tuning (nv_powermizer_mode, nv_core_clock_mhz_offset,
|
||||||
|
; nv_mem_clock_mhz_offset) requires the "coolbits" Xorg option and is not
|
||||||
|
; configured by default, since this setup targets AMD/Intel GPUs.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// Allow members of the "sudo" administrative group to start/stop CoreCtrl's
|
||||||
|
// privileged helper without an interactive PolicyKit password prompt.
|
||||||
|
// Deployed by stages/06-services.sh.
|
||||||
|
polkit.addRule(function(action, subject) {
|
||||||
|
if ((action.id == "org.corectrl.helper.init" ||
|
||||||
|
action.id == "org.corectrl.helperkiller.init") &&
|
||||||
|
subject.isInGroup("sudo")) {
|
||||||
|
return polkit.Result.YES;
|
||||||
|
}
|
||||||
|
});
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Update XDG User Dirs if tool is present
|
||||||
|
if command -v xdg-user-dirs-update >/dev/null 2>&1; then
|
||||||
|
xdg-user-dirs-update
|
||||||
|
fi
|
||||||
Executable
+10
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Hide Waydroid desktop applications from standard desktop menus
|
||||||
|
if [ -d "$HOME/.local/share/applications" ]; then
|
||||||
|
for app in "$HOME"/.local/share/applications/waydroid.*.desktop; do
|
||||||
|
[ -f "$app" ] || continue
|
||||||
|
if ! grep -q "NoDisplay" "$app"; then
|
||||||
|
sed -i '/^Icon=/a NoDisplay=true' "$app" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
Executable
+12
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Add sbin and /usr/sbin to PATH for sudoers if not already present
|
||||||
|
if groups 2>/dev/null | grep -q "\bsudo\b"; then
|
||||||
|
case ":$PATH:" in
|
||||||
|
*":/sbin:"*) ;;
|
||||||
|
*) export PATH="$PATH:/sbin" ;;
|
||||||
|
esac
|
||||||
|
case ":$PATH:" in
|
||||||
|
*":/usr/sbin:"*) ;;
|
||||||
|
*) export PATH="$PATH:/usr/sbin" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
Executable
+23
@@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Display sudo reminder for administrative users once after initial setup
|
||||||
|
SUDO_FILE="$HOME/.sudo_as_admin_successful"
|
||||||
|
HUSH_FILE="$HOME/.hushlogin"
|
||||||
|
|
||||||
|
if [ -f "$SUDO_FILE" ]; then
|
||||||
|
FILE_MOD_TIME=$(stat -c %Y "$SUDO_FILE" 2>/dev/null || echo 0)
|
||||||
|
ONE_WEEK_AGO=$(date -d '7 days ago' +%s 2>/dev/null || echo 0)
|
||||||
|
if [ "$FILE_MOD_TIME" -gt 0 ] && [ "$ONE_WEEK_AGO" -gt 0 ] && [ "$FILE_MOD_TIME" -lt "$ONE_WEEK_AGO" ]; then
|
||||||
|
rm -f "$SUDO_FILE"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -e "$SUDO_FILE" ] && [ ! -e "$HUSH_FILE" ]; then
|
||||||
|
case " $(groups 2>/dev/null) " in
|
||||||
|
*\ admin\ *|*\ sudo\ *)
|
||||||
|
if [ -x /usr/bin/sudo ]; then
|
||||||
|
echo 'To run a command as administrator (user "root"), use "sudo <command>".'
|
||||||
|
echo 'See "man sudo_root" for details.'
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# /etc/profile.d/desktop_session_xdg_dirs.sh - Prepend a $DESKTOP_SESSION-named directory to $XDG_CONFIG_DIRS and $XDG_DATA_DIRS
|
||||||
|
|
||||||
|
DEFAULT_XDG_CONFIG_DIRS="/etc/xdg"
|
||||||
|
DEFAULT_XDG_DATA_DIRS="/usr/local/share/:/usr/share/"
|
||||||
|
|
||||||
|
if [ -n "$DESKTOP_SESSION" ]; then
|
||||||
|
# readd default if was empty
|
||||||
|
if [ -z "$XDG_CONFIG_DIRS" ]; then
|
||||||
|
XDG_CONFIG_DIRS="$DEFAULT_XDG_CONFIG_DIRS"
|
||||||
|
fi
|
||||||
|
if [ -n "${XDG_CONFIG_DIRS##*"$DEFAULT_XDG_CONFIG_DIRS"/xdg-"$DESKTOP_SESSION"*}" ]; then
|
||||||
|
XDG_CONFIG_DIRS="$DEFAULT_XDG_CONFIG_DIRS"/xdg-"$DESKTOP_SESSION":"$XDG_CONFIG_DIRS"
|
||||||
|
fi
|
||||||
|
export XDG_CONFIG_DIRS
|
||||||
|
# gnome is already added if gnome-session installed
|
||||||
|
if [ "$DESKTOP_SESSION" != "gnome" ]; then
|
||||||
|
if [ -z "$XDG_DATA_DIRS" ]; then
|
||||||
|
XDG_DATA_DIRS="$DEFAULT_XDG_DATA_DIRS"
|
||||||
|
fi
|
||||||
|
if [ -n "${XDG_DATA_DIRS##*/usr/share/"$DESKTOP_SESSION"*}" ]; then
|
||||||
|
XDG_DATA_DIRS=/usr/share/"$DESKTOP_SESSION":"$XDG_DATA_DIRS"
|
||||||
|
fi
|
||||||
|
export XDG_DATA_DIRS
|
||||||
|
fi
|
||||||
|
fi
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Ersteinrichtung: Dotfiles, Oh-My-Zsh, JetBrains Junie, Claude Code & Skills
|
||||||
|
# Systemweit installiert unter /etc/systemd/user/ und global aktiviert
|
||||||
|
# (siehe stages/10-hyprland.sh) - gilt fuer alle Benutzer, ohne dass etwas
|
||||||
|
# in einzelne Home-Verzeichnisse kopiert werden muss. Bewusst NICHT an
|
||||||
|
# Hyprland gebunden - laeuft ueber den systemd --user Manager, unabhaengig
|
||||||
|
# davon, welcher Compositor/welche Desktop-Sitzung gestartet wird.
|
||||||
|
After=default.target
|
||||||
|
StartLimitIntervalSec=300
|
||||||
|
StartLimitBurst=60
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/local/lib/first-login-setup/first-login-setup.sh
|
||||||
|
# first-login-setup.sh beendet sich mit Exit-Code 1, solange die grafische
|
||||||
|
# Sitzung (Wayland-Socket) noch nicht bereit ist - in diesem Fall einfach
|
||||||
|
# erneut versuchen, bis sie verfuegbar ist. Das Skript selbst entscheidet
|
||||||
|
# anhand von ~/.local/state/first-login-setup/completed (pro Benutzer), ob
|
||||||
|
# es fuer den aktuellen Benutzer ueberhaupt noch etwas zu tun gibt.
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=3
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
### MangoHud default overlay configuration
|
||||||
|
### Deployed to ~/.config/MangoHud/MangoHud.conf via stages/07-skel-and-user.sh
|
||||||
|
### Full reference: https://github.com/flightlessmango/MangoHud/blob/master/data/MangoHud.conf
|
||||||
|
|
||||||
|
# Toggle the overlay on/off with Shift+F12, move it with Shift+F11
|
||||||
|
toggle_hud=Shift_L+F12
|
||||||
|
toggle_hud_position=Shift_L+F11
|
||||||
|
|
||||||
|
# Performance metrics
|
||||||
|
fps
|
||||||
|
frametime
|
||||||
|
frame_timing
|
||||||
|
cpu_stats
|
||||||
|
cpu_temp
|
||||||
|
cpu_power
|
||||||
|
gpu_stats
|
||||||
|
gpu_temp
|
||||||
|
gpu_power
|
||||||
|
gpu_core_clock
|
||||||
|
gpu_mem_clock
|
||||||
|
vram
|
||||||
|
ram
|
||||||
|
io_read
|
||||||
|
io_write
|
||||||
|
|
||||||
|
# GameMode / vkBasalt indicators
|
||||||
|
gamemode
|
||||||
|
vkbasalt
|
||||||
|
|
||||||
|
# Presentation
|
||||||
|
position=top-left
|
||||||
|
background_alpha=0.4
|
||||||
|
font_size=20
|
||||||
|
round_corners=6
|
||||||
|
table_columns=3
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# ==============================================================================
|
||||||
|
# Rudimentäre Hyprland-Konfiguration (Standard für neue Benutzer)
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# Monitor Setup (Standard: Automatische Auflösung & Positionierung)
|
||||||
|
monitor = , preferred, auto, 1
|
||||||
|
|
||||||
|
# Modifikator-Taste: SUPER (Windows-Taste)
|
||||||
|
$mainMod = SUPER
|
||||||
|
|
||||||
|
# Standard-Terminal
|
||||||
|
$terminal = kitty
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Tastenbelegungen (Keybindings)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# SUPER + ENTER: Kitty Terminal öffnen
|
||||||
|
bind = $mainMod, RETURN, exec, $terminal
|
||||||
|
bind = $mainMod, Return, exec, $terminal
|
||||||
|
|
||||||
|
# SUPER + Q: Aktives Fenster schließen
|
||||||
|
bind = $mainMod, Q, killactive,
|
||||||
|
|
||||||
|
# Grundlegende Navigation & Fensterverwaltung
|
||||||
|
bind = $mainMod, M, exit,
|
||||||
|
bind = $mainMod, E, exec, thunar
|
||||||
|
bind = $mainMod, V, togglefloating,
|
||||||
|
bind = $mainMod, F, fullscreen,
|
||||||
|
|
||||||
|
# Fokus mit Pfeiltasten wechseln
|
||||||
|
bind = $mainMod, left, movefocus, l
|
||||||
|
bind = $mainMod, right, movefocus, r
|
||||||
|
bind = $mainMod, up, movefocus, u
|
||||||
|
bind = $mainMod, down, movefocus, d
|
||||||
|
|
||||||
|
# Workspaces 1-10 wechseln
|
||||||
|
bind = $mainMod, 1, workspace, 1
|
||||||
|
bind = $mainMod, 2, workspace, 2
|
||||||
|
bind = $mainMod, 3, workspace, 3
|
||||||
|
bind = $mainMod, 4, workspace, 4
|
||||||
|
bind = $mainMod, 5, workspace, 5
|
||||||
|
bind = $mainMod, 6, workspace, 6
|
||||||
|
bind = $mainMod, 7, workspace, 7
|
||||||
|
bind = $mainMod, 8, workspace, 8
|
||||||
|
bind = $mainMod, 9, workspace, 9
|
||||||
|
bind = $mainMod, 0, workspace, 10
|
||||||
|
|
||||||
|
# Aktives Fenster auf Workspace 1-10 verschieben
|
||||||
|
bind = $mainMod SHIFT, 1, movetoworkspace, 1
|
||||||
|
bind = $mainMod SHIFT, 2, movetoworkspace, 2
|
||||||
|
bind = $mainMod SHIFT, 3, movetoworkspace, 3
|
||||||
|
bind = $mainMod SHIFT, 4, movetoworkspace, 4
|
||||||
|
bind = $mainMod SHIFT, 5, movetoworkspace, 5
|
||||||
|
bind = $mainMod SHIFT, 6, movetoworkspace, 6
|
||||||
|
bind = $mainMod SHIFT, 7, movetoworkspace, 7
|
||||||
|
bind = $mainMod SHIFT, 8, movetoworkspace, 8
|
||||||
|
bind = $mainMod SHIFT, 9, movetoworkspace, 9
|
||||||
|
bind = $mainMod SHIFT, 0, movetoworkspace, 10
|
||||||
|
|
||||||
|
# Fenster mit Maus bewegen und vergrößern/verkleinern
|
||||||
|
bindm = $mainMod, mouse:272, movewindow
|
||||||
|
bindm = $mainMod, mouse:273, resizewindow
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Erscheinungsbild & Verhalten
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
general {
|
||||||
|
gaps_in = 4
|
||||||
|
gaps_out = 8
|
||||||
|
border_size = 2
|
||||||
|
col.active_border = rgba(33ccffee) rgba(00ff99ee) 45deg
|
||||||
|
col.inactive_border = rgba(595959aa)
|
||||||
|
layout = dwindle
|
||||||
|
}
|
||||||
|
|
||||||
|
decoration {
|
||||||
|
rounding = 8
|
||||||
|
}
|
||||||
|
|
||||||
|
animations {
|
||||||
|
enabled = yes
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
kb_layout = de
|
||||||
|
follow_mouse = 1
|
||||||
|
touchpad {
|
||||||
|
natural_scroll = yes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Ersteinrichtung
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Dotfiles-Auswahl, Oh-My-Zsh, JetBrains Junie CLI & AwesomeJunieSkills starten
|
||||||
|
# automatisch beim Login über den systemweiten systemd --user Service
|
||||||
|
# 'first-login-setup.service' (/etc/systemd/user/) - bewusst NICHT hier per
|
||||||
|
# exec-once, damit die Ersteinrichtung unabhängig vom gewählten Compositor
|
||||||
|
# funktioniert. Manueller Aufruf jederzeit möglich mit:
|
||||||
|
# /usr/local/lib/first-login-setup/first-login-setup.sh --force
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
### vkBasalt default post-processing configuration
|
||||||
|
### Deployed to ~/.config/vkBasalt/vkBasalt.conf via stages/07-skel-and-user.sh
|
||||||
|
### Full reference: https://github.com/DadSchoorse/vkBasalt
|
||||||
|
###
|
||||||
|
### Only active for games launched with ENABLE_VKBASALT=1, e.g. as a Steam
|
||||||
|
### launch option: ENABLE_VKBASALT=1 %command%
|
||||||
|
|
||||||
|
# Effect chain to apply, in order
|
||||||
|
effects = cas
|
||||||
|
|
||||||
|
# Contrast Adaptive Sharpening strength (0.0 - 1.0)
|
||||||
|
casSharpness = 0.4
|
||||||
|
|
||||||
|
# Apply the effect chain immediately without needing to press toggleKey first
|
||||||
|
enableOnLaunch = True
|
||||||
|
|
||||||
|
# Key to toggle all effects on/off at runtime
|
||||||
|
toggleKey = Home
|
||||||
Executable
+75
@@ -0,0 +1,75 @@
|
|||||||
|
# If you come from bash you might have to change your $PATH.
|
||||||
|
# export PATH=$HOME/bin:/usr/local/bin:$PATH
|
||||||
|
|
||||||
|
export ZSH="$HOME/.oh-my-zsh"
|
||||||
|
|
||||||
|
# Automatic Oh-My-Zsh installation on login / shell startup if not yet installed
|
||||||
|
if [[ ! -d "$ZSH" ]]; then
|
||||||
|
if command -v git >/dev/null 2>&1; then
|
||||||
|
echo "Oh-My-Zsh ist nicht eingerichtet. Installiere Oh-My-Zsh für $USER..."
|
||||||
|
if git clone --depth=1 https://github.com/ohmyzsh/ohmyzsh.git "$ZSH" 2>/dev/null; then
|
||||||
|
mkdir -p "${ZSH_CUSTOM:-$ZSH/custom}/plugins"
|
||||||
|
git clone --depth=1 https://github.com/zsh-users/zsh-autosuggestions "${ZSH_CUSTOM:-$ZSH/custom}/plugins/zsh-autosuggestions" 2>/dev/null || true
|
||||||
|
git clone --depth=1 https://github.com/zsh-users/zsh-syntax-highlighting "${ZSH_CUSTOM:-$ZSH/custom}/plugins/zsh-syntax-highlighting" 2>/dev/null || true
|
||||||
|
echo "Oh-My-Zsh erfolgreich eingerichtet."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Plugins sicherstellen, falls Oh-My-Zsh existiert aber Custom-Plugins fehlen
|
||||||
|
if [[ ! -d "${ZSH_CUSTOM:-$ZSH/custom}/plugins/zsh-autosuggestions" ]] && command -v git >/dev/null 2>&1; then
|
||||||
|
git clone --depth=1 https://github.com/zsh-users/zsh-autosuggestions "${ZSH_CUSTOM:-$ZSH/custom}/plugins/zsh-autosuggestions" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if [[ ! -d "${ZSH_CUSTOM:-$ZSH/custom}/plugins/zsh-syntax-highlighting" ]] && command -v git >/dev/null 2>&1; then
|
||||||
|
git clone --depth=1 https://github.com/zsh-users/zsh-syntax-highlighting "${ZSH_CUSTOM:-$ZSH/custom}/plugins/zsh-syntax-highlighting" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
apt_pref='apt'
|
||||||
|
apt_upgr='upgrade'
|
||||||
|
|
||||||
|
ZSH_THEME="agnosterzak"
|
||||||
|
|
||||||
|
plugins=(
|
||||||
|
alias-finder
|
||||||
|
aliases
|
||||||
|
colored-man-pages
|
||||||
|
command-not-found
|
||||||
|
common-aliases
|
||||||
|
composer
|
||||||
|
cp
|
||||||
|
debian
|
||||||
|
docker
|
||||||
|
git
|
||||||
|
jsontools
|
||||||
|
kitty
|
||||||
|
python
|
||||||
|
ssh
|
||||||
|
symfony
|
||||||
|
themes
|
||||||
|
vscode
|
||||||
|
zsh-autosuggestions
|
||||||
|
zsh-syntax-highlighting
|
||||||
|
)
|
||||||
|
|
||||||
|
# Oh-My-Zsh configuration
|
||||||
|
if [[ -d "$ZSH" && -f "$ZSH/oh-my-zsh.sh" ]]; then
|
||||||
|
source "$ZSH/oh-my-zsh.sh"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fastfetch system info banner
|
||||||
|
if command -v fastfetch >/dev/null 2>&1; then
|
||||||
|
if [[ -f "$HOME/.config/fastfetch/config-compact.jsonc" ]]; then
|
||||||
|
fastfetch -c "$HOME/.config/fastfetch/config-compact.jsonc"
|
||||||
|
else
|
||||||
|
fastfetch
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Aliases using modern tools with fallback
|
||||||
|
if command -v lsd >/dev/null 2>&1; then
|
||||||
|
alias ls='lsd'
|
||||||
|
alias l='ls -l'
|
||||||
|
alias la='ls -a'
|
||||||
|
alias lla='ls -la'
|
||||||
|
alias lt='ls --tree'
|
||||||
|
fi
|
||||||
Executable
+2
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
Executable
BIN
Binary file not shown.
Executable
+1
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
Executable
+2
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
Executable
+1
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
Executable
BIN
Binary file not shown.
Executable
+39
@@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# /usr/local/lib/first-login-setup/_lib.sh
|
||||||
|
# Gemeinsame Farbcodes & Ausgabe-Helfer für die first-login-setup.sh Skripte.
|
||||||
|
# Nicht zur direkten Ausführung gedacht - wird von den anderen Skripten source't.
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
CLR_RESET='\033[0m'
|
||||||
|
CLR_BOLD='\033[1m'
|
||||||
|
CLR_CYAN='\033[0;36m'
|
||||||
|
CLR_GREEN='\033[0;32m'
|
||||||
|
CLR_YELLOW='\033[0;33m'
|
||||||
|
CLR_RED='\033[0;31m'
|
||||||
|
CLR_BLUE='\033[0;34m'
|
||||||
|
CLR_MAGENTA='\033[0;35m'
|
||||||
|
|
||||||
|
print_header() {
|
||||||
|
printf "\n"
|
||||||
|
printf "%b\n" "${CLR_BOLD}${CLR_CYAN}==============================================================================${CLR_RESET}"
|
||||||
|
printf "%b\n" "${CLR_BOLD}${CLR_CYAN}$1${CLR_RESET}"
|
||||||
|
printf "%b\n" "${CLR_BOLD}${CLR_CYAN}==============================================================================${CLR_RESET}"
|
||||||
|
printf "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_info() {
|
||||||
|
printf "%b\n" "${CLR_CYAN}[INFO] $*${CLR_RESET}"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_success() {
|
||||||
|
printf "%b\n" "${CLR_GREEN}[✓] $*${CLR_RESET}"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_warn() {
|
||||||
|
printf "%b\n" "${CLR_YELLOW}[WARNUNG] $*${CLR_RESET}"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_error() {
|
||||||
|
printf "%b\n" "${CLR_RED}[FEHLER] $*${CLR_RESET}"
|
||||||
|
}
|
||||||
+134
@@ -0,0 +1,134 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# /usr/local/lib/first-login-setup/first-login-setup.sh
|
||||||
|
# Ersteinrichtung nach der ersten Anmeldung. Führt der Reihe nach aus:
|
||||||
|
# 1) setup-dotfiles.sh - Desktop-Konfiguration (LinuxBeginnings / eigenes Repo)
|
||||||
|
# 2) setup-shell.sh - Oh-My-Zsh & Plugins
|
||||||
|
# 3) setup-junie-cli.sh - JetBrains Junie CLI
|
||||||
|
# 4) setup-junie-skills.sh - AwesomeJunieSkills
|
||||||
|
# 5) setup-claude-cli.sh - Claude Code (Anthropic)
|
||||||
|
# 6) setup-claude-skills.sh - Awesome LLM Skills
|
||||||
|
#
|
||||||
|
# Wird automatisch beim Login über den systemweiten systemd --user Service
|
||||||
|
# 'first-login-setup.service' (/etc/systemd/user/) gestartet - bewusst NICHT
|
||||||
|
# über hyprland.conf, damit die Ersteinrichtung unabhängig vom gewählten
|
||||||
|
# Compositor funktioniert. Kann jederzeit auch manuell aufgerufen werden.
|
||||||
|
# Jedes der obigen Skripte kann auch einzeln & unabhängig ausgeführt werden.
|
||||||
|
#
|
||||||
|
# Liegt system- statt benutzerweit unter /usr/local/lib (eine gemeinsame
|
||||||
|
# Kopie für alle Benutzer, statt in jedes Home dupliziert zu werden), und
|
||||||
|
# NICHT unter ~/.config: setup-dotfiles.sh kann ~/.config komplett durch ein
|
||||||
|
# eigenes Dotfiles-Repository ersetzen - das darf weder dieses Skript noch
|
||||||
|
# den Fortschritt (siehe MARKER_FILE) mit sich reißen.
|
||||||
|
#
|
||||||
|
# Läuft wirklich nur bei der allerersten Anmeldung eines Benutzers, oder
|
||||||
|
# erneut, wenn zuvor explizit "Später" gewählt wurde (siehe unten) - danach
|
||||||
|
# nie wieder, sobald MARKER_FILE gesetzt ist.
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
# shellcheck source=_lib.sh
|
||||||
|
source "$SCRIPT_DIR/_lib.sh"
|
||||||
|
|
||||||
|
MARKER_FILE="$HOME/.local/state/first-login-setup/completed"
|
||||||
|
FORCE=0
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
-f|--force)
|
||||||
|
FORCE=1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Wenn bereits abgeschlossen und kein --force übergeben wurde, beenden.
|
||||||
|
# (Ein zuvor gewähltes "Später" setzt MARKER_FILE bewusst NICHT, siehe unten -
|
||||||
|
# das Skript fragt in diesem Fall beim nächsten Login erneut.)
|
||||||
|
if [[ "$FORCE" -eq 0 && -f "$MARKER_FILE" ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Wenn nicht in einem interaktiven Terminal ausgeführt (z. B. via systemd
|
||||||
|
# --user Service oder Keybind ohne Terminal), starte das Skript in einem
|
||||||
|
# Kitty-Fenster:
|
||||||
|
if [[ ! -t 0 || ! -t 1 ]]; then
|
||||||
|
# Beim Start über systemd --user ist WAYLAND_DISPLAY/DISPLAY meist nicht
|
||||||
|
# gesetzt, da der Dienst unabhängig vom Compositor läuft. Das
|
||||||
|
# Wayland-Socket direkt suchen, statt uns auf eine importierte Umgebung
|
||||||
|
# zu verlassen.
|
||||||
|
if [[ -z "${WAYLAND_DISPLAY:-}" && -z "${DISPLAY:-}" && -n "${XDG_RUNTIME_DIR:-}" ]]; then
|
||||||
|
wl_socket="$(find "$XDG_RUNTIME_DIR" -maxdepth 1 -name 'wayland-*' ! -name '*.lock' 2>/dev/null | head -n1)"
|
||||||
|
if [[ -n "$wl_socket" ]]; then
|
||||||
|
export WAYLAND_DISPLAY
|
||||||
|
WAYLAND_DISPLAY="$(basename "$wl_socket")"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${WAYLAND_DISPLAY:-}" && -z "${DISPLAY:-}" ]]; then
|
||||||
|
# Grafische Sitzung ist noch nicht bereit (z. B. Compositor startet
|
||||||
|
# gerade erst). Mit Exit-Code 1 beenden, damit systemd den Dienst
|
||||||
|
# gemäß 'Restart=on-failure' erneut versucht.
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v kitty >/dev/null 2>&1; then
|
||||||
|
exec kitty --title "System-Ersteinrichtung & Dotfiles" bash -c "$0 --in-terminal; echo; read -r -p 'Drücke Enter zum Schließen...' || true"
|
||||||
|
else
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
mark_as_prompted() {
|
||||||
|
mkdir -p "$(dirname "$MARKER_FILE")" 2>/dev/null || true
|
||||||
|
touch "$MARKER_FILE" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
clear 2>/dev/null || true
|
||||||
|
print_header " Hyprland Desktop - Ersteinrichtung "
|
||||||
|
printf "%b\n" "Willkommen auf Ihrem neuen Debian Unstable Hyprland-System!"
|
||||||
|
printf "\n"
|
||||||
|
printf "%b\n" "Aktuell ist eine rudimentäre Basiskonfiguration aktiv:"
|
||||||
|
printf "%b\n" " - ${CLR_BOLD}SUPER + ENTER${CLR_RESET} : Terminal (Kitty) öffnen"
|
||||||
|
printf "%b\n" " - ${CLR_BOLD}SUPER + Q${CLR_RESET} : Aktives Fenster schließen"
|
||||||
|
printf "\n"
|
||||||
|
printf "%b\n" "Folgende Schritte können nacheinander ausgeführt werden:"
|
||||||
|
printf "%b\n" " 1) Desktop-Konfiguration (Dotfiles)"
|
||||||
|
printf "%b\n" " 2) Oh-My-Zsh & Shell-Plugins"
|
||||||
|
printf "%b\n" " 3) JetBrains Junie CLI"
|
||||||
|
printf "%b\n" " 4) AwesomeJunieSkills"
|
||||||
|
printf "%b\n" " 5) Claude Code"
|
||||||
|
printf "%b\n" " 6) Awesome LLM Skills"
|
||||||
|
printf "\n"
|
||||||
|
printf "%b\n" "${CLR_BOLD}${CLR_CYAN}------------------------------------------------------------------------------${CLR_RESET}"
|
||||||
|
|
||||||
|
# Explizite Ja/Später-Abfrage: Nur bei "Jetzt" wird MARKER_FILE gesetzt.
|
||||||
|
# Bei "Später" bricht das Skript ohne Marker ab, sodass es beim nächsten
|
||||||
|
# Login erneut über first-login-setup.service gestartet wird und wieder fragt.
|
||||||
|
answer=""
|
||||||
|
read -r -p "Ersteinrichtung jetzt durchführen? [J]etzt / [S]päter (beim nächsten Login erneut fragen): " answer || answer="j"
|
||||||
|
answer="$(echo "$answer" | xargs)"
|
||||||
|
|
||||||
|
if [[ "$answer" =~ ^[Ss] ]]; then
|
||||||
|
printf "\n"
|
||||||
|
print_warn "Ersteinrichtung verschoben. Sie werden beim nächsten Login erneut gefragt."
|
||||||
|
printf "%b\n" "Manueller Aufruf jederzeit möglich mit: ${CLR_BOLD}/usr/local/lib/first-login-setup/first-login-setup.sh${CLR_RESET}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
bash "$SCRIPT_DIR/setup-dotfiles.sh"
|
||||||
|
bash "$SCRIPT_DIR/setup-shell.sh"
|
||||||
|
bash "$SCRIPT_DIR/setup-junie-cli.sh"
|
||||||
|
bash "$SCRIPT_DIR/setup-junie-skills.sh"
|
||||||
|
bash "$SCRIPT_DIR/setup-claude-cli.sh"
|
||||||
|
bash "$SCRIPT_DIR/setup-claude-skills.sh"
|
||||||
|
|
||||||
|
mark_as_prompted
|
||||||
|
|
||||||
|
printf "\n"
|
||||||
|
print_success "Ersteinrichtung vollständig abgeschlossen!"
|
||||||
|
printf "%b\n" "Sie können dieses Menü jederzeit erneut aufrufen mit:"
|
||||||
|
printf "%b\n" " ${CLR_BOLD}/usr/local/lib/first-login-setup/first-login-setup.sh --force${CLR_RESET}"
|
||||||
|
printf "%b\n" "Oder einzelne Schritte direkt erneut ausführen, z. B.:"
|
||||||
|
printf "%b\n" " ${CLR_BOLD}/usr/local/lib/first-login-setup/setup-junie-skills.sh${CLR_RESET}"
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# /usr/local/lib/first-login-setup/setup-claude-cli.sh
|
||||||
|
# Installiert Claude Code (Anthropic) über den offiziellen nativen Installer.
|
||||||
|
#
|
||||||
|
# Kann eigenständig ausgeführt werden, unabhängig von first-login-setup.sh.
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
# shellcheck source=_lib.sh
|
||||||
|
source "$SCRIPT_DIR/_lib.sh"
|
||||||
|
|
||||||
|
install_claude_cli() {
|
||||||
|
print_header " Claude Code Installation "
|
||||||
|
|
||||||
|
if command -v curl >/dev/null 2>&1; then
|
||||||
|
if curl -fsSL https://claude.ai/install.sh | bash; then
|
||||||
|
print_success "Claude Code erfolgreich installiert."
|
||||||
|
else
|
||||||
|
print_warn "Installation von Claude Code fehlgeschlagen oder unterbrochen."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_error "'curl' ist nicht installiert. Claude Code konnte nicht geladen werden."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Nur ausführen, wenn das Skript direkt gestartet wird (nicht bei source)
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
install_claude_cli
|
||||||
|
fi
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# /usr/local/lib/first-login-setup/setup-claude-skills.sh
|
||||||
|
# Klont/aktualisiert Awesome LLM Skills (https://github.com/Prat011/awesome-llm-skills)
|
||||||
|
# in ~/.claude/skills und verknüpft alle gefundenen Skills, damit Claude Code
|
||||||
|
# sie automatisch erkennt.
|
||||||
|
#
|
||||||
|
# Kann eigenständig ausgeführt werden, unabhängig von first-login-setup.sh.
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
# shellcheck source=_lib.sh
|
||||||
|
source "$SCRIPT_DIR/_lib.sh"
|
||||||
|
|
||||||
|
install_claude_skills() {
|
||||||
|
print_header " Awesome LLM Skills Installation "
|
||||||
|
|
||||||
|
local skills_dir="$HOME/.claude/skills"
|
||||||
|
local repo_dir="$skills_dir/awesome-llm-skills"
|
||||||
|
mkdir -p "$skills_dir"
|
||||||
|
|
||||||
|
if ! command -v git >/dev/null 2>&1; then
|
||||||
|
print_error "'git' ist nicht installiert. Awesome LLM Skills konnte nicht eingerichtet werden."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -d "$repo_dir/.git" ]]; then
|
||||||
|
print_info "Repository existiert bereits in $repo_dir. Führe 'git pull' aus..."
|
||||||
|
if git -C "$repo_dir" pull --ff-only 2>/dev/null; then
|
||||||
|
print_success "Awesome LLM Skills erfolgreich aktualisiert."
|
||||||
|
else
|
||||||
|
print_warn "'git pull' in $repo_dir fehlgeschlagen."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_info "Klone Awesome LLM Skills nach $repo_dir..."
|
||||||
|
rm -rf "$repo_dir" 2>/dev/null || true
|
||||||
|
if git clone https://github.com/Prat011/awesome-llm-skills.git "$repo_dir"; then
|
||||||
|
print_success "Awesome LLM Skills erfolgreich nach $repo_dir geklont."
|
||||||
|
else
|
||||||
|
print_warn "Klonen von Awesome LLM Skills fehlgeschlagen."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Symlinks für alle Skills im Skill-Hauptordner erstellen
|
||||||
|
if [[ -d "$repo_dir" ]]; then
|
||||||
|
local count=0
|
||||||
|
for skill_path in "$repo_dir"/*; do
|
||||||
|
if [[ -d "$skill_path" && -f "$skill_path/SKILL.md" ]]; then
|
||||||
|
local s_name
|
||||||
|
s_name="$(basename "$skill_path")"
|
||||||
|
ln -sfn "$skill_path" "$skills_dir/$s_name"
|
||||||
|
((count++)) || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
print_success "$count Skills verknüpft in '$skills_dir' (kann via 'git pull' in '$repo_dir' aktualisiert werden)."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Nur ausführen, wenn das Skript direkt gestartet wird (nicht bei source)
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
install_claude_skills
|
||||||
|
fi
|
||||||
+185
@@ -0,0 +1,185 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# /usr/local/lib/first-login-setup/setup-dotfiles.sh
|
||||||
|
# Interaktive Auswahl & Einrichtung der Desktop-Konfiguration (Dotfiles):
|
||||||
|
# LinuxBeginnings Hyprland-Dots, eigenes Git-Repository, oder überspringen.
|
||||||
|
#
|
||||||
|
# Kann eigenständig ausgeführt werden, unabhängig von first-login-setup.sh.
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
# shellcheck source=_lib.sh
|
||||||
|
source "$SCRIPT_DIR/_lib.sh"
|
||||||
|
|
||||||
|
install_linuxbeginnings() {
|
||||||
|
printf "\n"
|
||||||
|
printf "%b\n" "${CLR_GREEN}${CLR_BOLD}[INFO] Starte Installation der LinuxBeginnings Hyprland-Dotfiles...${CLR_RESET}"
|
||||||
|
printf "\n"
|
||||||
|
if command -v curl >/dev/null 2>&1; then
|
||||||
|
sh <(curl -fsSL https://raw.githubusercontent.com/LinuxBeginnings/Hyprland-Dots/main/Distro-Hyprland.sh)
|
||||||
|
else
|
||||||
|
print_error "'curl' ist nicht installiert. Bitte installieren Sie curl und wiederholen Sie den Vorgang."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
clone_custom_repo() {
|
||||||
|
if ! command -v git >/dev/null 2>&1; then
|
||||||
|
printf "\n"
|
||||||
|
print_error "'git' ist nicht installiert. Bitte installieren Sie git zuerst."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
printf "\n"
|
||||||
|
printf "%b\n" "${CLR_BOLD}${CLR_CYAN}--- Eigenes Git-Repository für ~/.config klonen ---${CLR_RESET}"
|
||||||
|
printf "%b\n" "Geben Sie die Git-URL Ihres Dotfiles-/Config-Repositories an."
|
||||||
|
printf "%b\n" "Beispiele:"
|
||||||
|
printf "%b\n" " HTTPS : ${CLR_BLUE}https://github.com/Benutzername/mein-config-repo.git${CLR_RESET}"
|
||||||
|
printf "%b\n" " SSH : ${CLR_BLUE}git@github.com:Benutzername/mein-config-repo.git${CLR_RESET}"
|
||||||
|
printf "\n"
|
||||||
|
printf "%b\n" "${CLR_YELLOW}Hinweise zur Authentifizierung:${CLR_RESET}"
|
||||||
|
printf "%b\n" " - ${CLR_BOLD}HTTPS:${CLR_RESET} Bei privaten Repositories fragt Git nach Benutzername & Personal Access Token (PAT)."
|
||||||
|
printf "%b\n" " - ${CLR_BOLD}SSH:${CLR_RESET} Erfordert einen hinterlegten SSH-Schlüssel (z. B. in ~/.ssh/ oder via ssh-agent)."
|
||||||
|
printf "\n"
|
||||||
|
|
||||||
|
read -r -p "Git-Repository-URL (oder 'q' zum Abbrechen): " repo_url
|
||||||
|
repo_url="$(echo "$repo_url" | xargs)"
|
||||||
|
|
||||||
|
if [[ "$repo_url" == "q" || "$repo_url" == "Q" || -z "$repo_url" ]]; then
|
||||||
|
print_warn "Vorgang abgebrochen. Kehre zum Hauptmenü zurück."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# SSH-Prüfung und Hilfestellung
|
||||||
|
if [[ "$repo_url" =~ ^git@ || "$repo_url" =~ ^ssh:// ]]; then
|
||||||
|
local ssh_keys_found=0
|
||||||
|
if [[ -d "$HOME/.ssh" ]]; then
|
||||||
|
for k in "$HOME/.ssh"/id_*; do
|
||||||
|
if [[ -f "$k" && ! "$k" =~ \.pub$ ]]; then
|
||||||
|
ssh_keys_found=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$ssh_keys_found" -eq 0 ]]; then
|
||||||
|
printf "\n"
|
||||||
|
print_warn "Es wurde kein lokaler SSH-Privatschlüssel in ~/.ssh/ gefunden."
|
||||||
|
read -r -p "Möchten Sie vor dem Klonen ein neues SSH-Schlüsselpaar (ed25519) generieren? [j/N]: " gen_ssh || gen_ssh="n"
|
||||||
|
if [[ "$gen_ssh" =~ ^[JjYy] ]]; then
|
||||||
|
mkdir -p "$HOME/.ssh"
|
||||||
|
chmod 700 "$HOME/.ssh"
|
||||||
|
ssh-keygen -t ed25519 -f "$HOME/.ssh/id_ed25519" -C "$USER@$(hostname)"
|
||||||
|
printf "\n%b\n" "${CLR_GREEN}Neuer öffentlicher SSH-Schlüssel (~/.ssh/id_ed25519.pub):${CLR_RESET}"
|
||||||
|
cat "$HOME/.ssh/id_ed25519.pub"
|
||||||
|
printf "\n%b\n" "Bitte fügen Sie diesen Schlüssel bei GitHub/GitLab zu Ihrem Account hinzu."
|
||||||
|
if command -v wl-copy >/dev/null 2>&1; then
|
||||||
|
if wl-copy < "$HOME/.ssh/id_ed25519.pub" 2>/dev/null; then
|
||||||
|
print_success "(In die Zwischenablage kopiert!)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
read -r -p "Drücken Sie Enter, sobald der SSH-Key hinterlegt wurde..."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Backup und Klon-Vorgang vorbereiten
|
||||||
|
local timestamp
|
||||||
|
timestamp="$(date +%Y%m%d_%H%M%S)"
|
||||||
|
local backup_dir="$HOME/.config.backup_$timestamp"
|
||||||
|
|
||||||
|
printf "\n"
|
||||||
|
printf "%b\n" "${CLR_CYAN}[1/3] Sichere bestehendes ~/.config-Verzeichnis nach ${backup_dir}...${CLR_RESET}"
|
||||||
|
|
||||||
|
if [[ -d "$HOME/.config" ]]; then
|
||||||
|
mv "$HOME/.config" "$backup_dir"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "%b\n" "${CLR_CYAN}[2/3] Klone Repository direkt nach ~/.config...${CLR_RESET}"
|
||||||
|
|
||||||
|
# Git ausführen mit aktivierter Terminal-Eingabeaufforderung für Authentifizierung
|
||||||
|
if env GIT_TERMINAL_PROMPT=1 git clone --recurse-submodules "$repo_url" "$HOME/.config"; then
|
||||||
|
printf "\n"
|
||||||
|
printf "%b\n" "${CLR_GREEN}${CLR_BOLD}[ERFOLG] Repository wurde erfolgreich nach ~/.config geklont!${CLR_RESET}"
|
||||||
|
printf "%b\n" " - Ihr gesamter ${CLR_BOLD}~/.config${CLR_RESET}-Ordner ist nun das Git-Repository."
|
||||||
|
printf "%b\n" " - Vorherige Konfigurationen wurden gesichert in: ${CLR_BOLD}${backup_dir}${CLR_RESET}"
|
||||||
|
|
||||||
|
# Hyprland-Konfiguration neu laden falls aktiv
|
||||||
|
if command -v hyprctl >/dev/null 2>&1; then
|
||||||
|
printf "\n"
|
||||||
|
printf "%b\n" "${CLR_CYAN}[3/3] Lade Hyprland-Konfiguration neu...${CLR_RESET}"
|
||||||
|
hyprctl reload 2>/dev/null || true
|
||||||
|
print_success "Hyprland erfolgreich aktualisiert."
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "\n"
|
||||||
|
print_success "Dotfiles-Einrichtung abgeschlossen!"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
printf "\n"
|
||||||
|
print_error "Git Clone ist fehlgeschlagen (Authentifizierungsfehler, falsche URL oder Netzwerkproblem)."
|
||||||
|
print_warn "[ROLLBACK] Stelle vorheriges ~/.config-Verzeichnis aus dem Backup wieder her..."
|
||||||
|
rm -rf "$HOME/.config" 2>/dev/null || true
|
||||||
|
if [[ -d "$backup_dir" ]]; then
|
||||||
|
mv "$backup_dir" "$HOME/.config"
|
||||||
|
print_success "Ursprüngliches ~/.config-Verzeichnis wurde vollständig wiederhergestellt."
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "\n"
|
||||||
|
read -r -p "Möchten Sie es erneut versuchen? [J/n]: " retry || retry="n"
|
||||||
|
if [[ ! "$retry" =~ ^[JjYy] && -n "$retry" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
run_dotfiles_menu() {
|
||||||
|
print_header " Hyprland Desktop - Dotfiles Setup & Konfiguration "
|
||||||
|
printf "%b\n" "Wie möchten Sie Ihre Desktop-Konfiguration (Dotfiles) einrichten?"
|
||||||
|
printf "\n"
|
||||||
|
printf "%b\n" " ${CLR_BOLD}[1]${CLR_RESET} ${CLR_GREEN}LinuxBeginnings Hyprland-Dotfiles installieren${CLR_RESET}"
|
||||||
|
printf "%b\n" " Vollständig vorkonfiguriertes Setup (Waybar, Rofi, Animationen, Themes)"
|
||||||
|
printf "%b\n" " URL: https://github.com/LinuxBeginnings/Hyprland-Dots"
|
||||||
|
printf "\n"
|
||||||
|
printf "%b\n" " ${CLR_BOLD}[2]${CLR_RESET} ${CLR_MAGENTA}Eigenes Git-Repository als ~/.config klonen${CLR_RESET}"
|
||||||
|
printf "%b\n" " Klont Ihr eigenes Git-Repository direkt als vollständigen ~/.config-Ordner."
|
||||||
|
printf "%b\n" " (Unterstützt HTTPS & SSH inkl. Authentifizierung)"
|
||||||
|
printf "\n"
|
||||||
|
printf "%b\n" " ${CLR_BOLD}[3]${CLR_RESET} ${CLR_YELLOW}Überspringen / Basiskonfiguration beibehalten${CLR_RESET}"
|
||||||
|
printf "%b\n" " Keine Änderungen vornehmen (kann später manuell eingerichtet werden)."
|
||||||
|
printf "\n"
|
||||||
|
printf "%b\n" "${CLR_BOLD}${CLR_CYAN}------------------------------------------------------------------------------${CLR_RESET}"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
read -r -p "Bitte wählen Sie eine Option [1-3] (Standard: 1): " choice || choice="1"
|
||||||
|
choice="$(echo "$choice" | xargs)"
|
||||||
|
[[ -z "$choice" ]] && choice="1"
|
||||||
|
|
||||||
|
case "$choice" in
|
||||||
|
1)
|
||||||
|
install_linuxbeginnings
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
clone_custom_repo
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
printf "\n"
|
||||||
|
print_warn "Dotfiles-Einrichtung übersprungen. Die Basiskonfiguration bleibt aktiv."
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
print_error "Ungültige Eingabe. Bitte 1, 2 oder 3 wählen."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Nur ausführen, wenn das Skript direkt gestartet wird (nicht bei source)
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
run_dotfiles_menu
|
||||||
|
fi
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# /usr/local/lib/first-login-setup/setup-junie-cli.sh
|
||||||
|
# Installiert die JetBrains Junie CLI.
|
||||||
|
#
|
||||||
|
# Kann eigenständig ausgeführt werden, unabhängig von first-login-setup.sh.
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
# shellcheck source=_lib.sh
|
||||||
|
source "$SCRIPT_DIR/_lib.sh"
|
||||||
|
|
||||||
|
install_junie_cli() {
|
||||||
|
print_header " JetBrains Junie CLI Installation "
|
||||||
|
|
||||||
|
if command -v curl >/dev/null 2>&1; then
|
||||||
|
if curl -fsSL https://junie.jetbrains.com/install.sh | bash; then
|
||||||
|
print_success "JetBrains Junie CLI erfolgreich installiert."
|
||||||
|
else
|
||||||
|
print_warn "Installation von JetBrains Junie CLI fehlgeschlagen oder unterbrochen."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_error "'curl' ist nicht installiert. Junie CLI konnte nicht geladen werden."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Nur ausführen, wenn das Skript direkt gestartet wird (nicht bei source)
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
install_junie_cli
|
||||||
|
fi
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# /usr/local/lib/first-login-setup/setup-junie-skills.sh
|
||||||
|
# Klont/aktualisiert AwesomeJunieSkills in ~/.junie/skills, verknüpft alle
|
||||||
|
# gefundenen Skills und trägt das Repository in ~/.junie/config.json ein.
|
||||||
|
#
|
||||||
|
# Kann eigenständig ausgeführt werden, unabhängig von first-login-setup.sh.
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
# shellcheck source=_lib.sh
|
||||||
|
source "$SCRIPT_DIR/_lib.sh"
|
||||||
|
|
||||||
|
install_junie_skills() {
|
||||||
|
print_header " AwesomeJunieSkills Installation "
|
||||||
|
|
||||||
|
local skills_dir="$HOME/.junie/skills"
|
||||||
|
local repo_dir="$skills_dir/awesome-junie-skills"
|
||||||
|
mkdir -p "$skills_dir"
|
||||||
|
|
||||||
|
if ! command -v git >/dev/null 2>&1; then
|
||||||
|
print_error "'git' ist nicht installiert. AwesomeJunieSkills konnte nicht eingerichtet werden."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -d "$repo_dir/.git" ]]; then
|
||||||
|
print_info "Repository existiert bereits in $repo_dir. Führe 'git pull' aus..."
|
||||||
|
if git -C "$repo_dir" pull --ff-only 2>/dev/null; then
|
||||||
|
print_success "AwesomeJunieSkills erfolgreich aktualisiert."
|
||||||
|
else
|
||||||
|
print_warn "'git pull' in $repo_dir fehlgeschlagen."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_info "Klone AwesomeJunieSkills nach $repo_dir..."
|
||||||
|
rm -rf "$repo_dir" 2>/dev/null || true
|
||||||
|
if git clone https://github.com/alebaffa/awesome-junie-skills.git "$repo_dir"; then
|
||||||
|
print_success "AwesomeJunieSkills erfolgreich nach $repo_dir geklont."
|
||||||
|
else
|
||||||
|
print_warn "Klonen von AwesomeJunieSkills fehlgeschlagen."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Symlinks für alle Skills im Skill-Hauptordner erstellen
|
||||||
|
if [[ -d "$repo_dir" ]]; then
|
||||||
|
local count=0
|
||||||
|
for skill_path in "$repo_dir"/*; do
|
||||||
|
if [[ -d "$skill_path" && -f "$skill_path/SKILL.md" ]]; then
|
||||||
|
local s_name
|
||||||
|
s_name="$(basename "$skill_path")"
|
||||||
|
ln -sfn "$skill_path" "$skills_dir/$s_name"
|
||||||
|
((count++)) || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
print_success "$count Skills verknüpft in '$skills_dir' (kann via 'git pull' in '$repo_dir' aktualisiert werden)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Junie config.json konfigurieren (skill-locations)
|
||||||
|
mkdir -p "$HOME/.junie"
|
||||||
|
local config_file="$HOME/.junie/config.json"
|
||||||
|
if command -v python3 >/dev/null 2>&1; then
|
||||||
|
python3 -c "
|
||||||
|
import json, os
|
||||||
|
cfg_path = os.path.expanduser('$config_file')
|
||||||
|
repo_path = os.path.expanduser('$repo_dir')
|
||||||
|
data = {}
|
||||||
|
if os.path.exists(cfg_path):
|
||||||
|
try:
|
||||||
|
with open(cfg_path, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
except Exception:
|
||||||
|
data = {}
|
||||||
|
locs = data.get('skill-locations', [])
|
||||||
|
if repo_path not in locs:
|
||||||
|
locs.append(repo_path)
|
||||||
|
data['skill-locations'] = locs
|
||||||
|
with open(cfg_path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(data, f, indent=2)
|
||||||
|
" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Nur ausführen, wenn das Skript direkt gestartet wird (nicht bei source)
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
install_junie_skills
|
||||||
|
fi
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# /usr/local/lib/first-login-setup/setup-shell.sh
|
||||||
|
# Richtet Oh-My-Zsh samt nützlicher Plugins (autosuggestions, syntax-highlighting)
|
||||||
|
# für den aktuellen Benutzer ein.
|
||||||
|
#
|
||||||
|
# Kann eigenständig ausgeführt werden, unabhängig von first-login-setup.sh.
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
# shellcheck source=_lib.sh
|
||||||
|
source "$SCRIPT_DIR/_lib.sh"
|
||||||
|
|
||||||
|
install_oh_my_zsh() {
|
||||||
|
local zsh_dir="$HOME/.oh-my-zsh"
|
||||||
|
print_header " Oh-My-Zsh & Shell-Konfiguration "
|
||||||
|
|
||||||
|
if [[ ! -d "$zsh_dir" ]]; then
|
||||||
|
print_info "Richte Oh-My-Zsh für $USER ein..."
|
||||||
|
if command -v git >/dev/null 2>&1; then
|
||||||
|
if git clone --depth=1 https://github.com/ohmyzsh/ohmyzsh.git "$zsh_dir"; then
|
||||||
|
mkdir -p "$zsh_dir/custom/plugins"
|
||||||
|
git clone --depth=1 https://github.com/zsh-users/zsh-autosuggestions "$zsh_dir/custom/plugins/zsh-autosuggestions" 2>/dev/null || true
|
||||||
|
git clone --depth=1 https://github.com/zsh-users/zsh-syntax-highlighting "$zsh_dir/custom/plugins/zsh-syntax-highlighting" 2>/dev/null || true
|
||||||
|
print_success "Oh-My-Zsh erfolgreich eingerichtet."
|
||||||
|
else
|
||||||
|
print_warn "Oh-My-Zsh konnte nicht geklont werden."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_error "'git' ist nicht installiert. Oh-My-Zsh konnte nicht eingerichtet werden."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Plugins sicherstellen
|
||||||
|
if [[ ! -d "$zsh_dir/custom/plugins/zsh-autosuggestions" ]] && command -v git >/dev/null 2>&1; then
|
||||||
|
git clone --depth=1 https://github.com/zsh-users/zsh-autosuggestions "$zsh_dir/custom/plugins/zsh-autosuggestions" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if [[ ! -d "$zsh_dir/custom/plugins/zsh-syntax-highlighting" ]] && command -v git >/dev/null 2>&1; then
|
||||||
|
git clone --depth=1 https://github.com/zsh-users/zsh-syntax-highlighting "$zsh_dir/custom/plugins/zsh-syntax-highlighting" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
print_success "Oh-My-Zsh ist bereits vorhanden und konfiguriert."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Nur ausführen, wenn das Skript direkt gestartet wird (nicht bei source)
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
install_oh_my_zsh
|
||||||
|
fi
|
||||||
Executable
+7
@@ -0,0 +1,7 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Name=Spotify
|
||||||
|
Exec=/usr/bin/spotify
|
||||||
|
Icon=spotify-client
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
|
Categories=Audio;Music;
|
||||||
Executable
+141
@@ -0,0 +1,141 @@
|
|||||||
|
#!/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" "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
TMP_DIR=""
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
if [[ -n "${TMP_DIR:-}" && -d "$TMP_DIR" ]]; then
|
||||||
|
log_info "Cleaning up temporary installation files..."
|
||||||
|
rm -rf "$TMP_DIR"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
|
main() {
|
||||||
|
local repo_url="${REPO_URL:-https://gitea.creative-dragonslayer.de/Scripts/Setup.git}"
|
||||||
|
local repo_branch="${REPO_BRANCH:-dev}"
|
||||||
|
|
||||||
|
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)"
|
||||||
|
|
||||||
|
log_info "Cloning repository into temporary directory: $TMP_DIR..."
|
||||||
|
local 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..."
|
||||||
|
|
||||||
|
# setup.sh itself elevates to root via sudo once it needs it - after all
|
||||||
|
# interactive TUI/prompts have already run as the current user. Escalating
|
||||||
|
# here instead would wrap the whiptail dialogs in sudo, which (when sudo is
|
||||||
|
# invoked from this non-interactive piped shell) leaves them unable to
|
||||||
|
# receive keystrokes. See setup.sh for details.
|
||||||
|
local -a setup_cmd=(./setup.sh "$@")
|
||||||
|
|
||||||
|
# Reconnect stdin/stdout to /dev/tty before executing setup.sh if piped
|
||||||
|
# (e.g. curl ... | bash), otherwise the interactive TUI receives no keystrokes
|
||||||
|
if [[ -c /dev/tty ]] && ( : < /dev/tty ) 2>/dev/null; then
|
||||||
|
if [[ ! -t 0 ]]; then
|
||||||
|
exec < /dev/tty
|
||||||
|
fi
|
||||||
|
if [[ ! -t 1 ]]; then
|
||||||
|
exec > /dev/tty
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure a terminal type whiptail/newt is able to render
|
||||||
|
case "${TERM:-}" in
|
||||||
|
""|dumb|unknown)
|
||||||
|
export TERM="xterm-256color"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
"${setup_cmd[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
Executable
+151
@@ -0,0 +1,151 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# lib/apt.sh - APT, Deb822, GPG Keyring and Package installation utilities
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
APT_KEYRINGS_DIR="/etc/apt/keyrings"
|
||||||
|
APT_SOURCES_DIR="/etc/apt/sources.list.d"
|
||||||
|
APT_PREFERENCES_DIR="/etc/apt/preferences.d"
|
||||||
|
|
||||||
|
# Ensure the standard keyring directory exists
|
||||||
|
apt_ensure_keyrings_dir() {
|
||||||
|
if [[ ! -d "$APT_KEYRINGS_DIR" ]]; then
|
||||||
|
log_info "Creating keyrings directory: $APT_KEYRINGS_DIR"
|
||||||
|
mkdir -p "$APT_KEYRINGS_DIR"
|
||||||
|
chmod 0755 "$APT_KEYRINGS_DIR"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Enable i386 (32-bit) multiarch architecture
|
||||||
|
apt_enable_i386() {
|
||||||
|
if ! dpkg --print-foreign-architectures | grep -q "^i386$"; then
|
||||||
|
log_info "Enabling 32-bit (i386) multiarch architecture..."
|
||||||
|
dpkg --add-architecture i386
|
||||||
|
log_success "32-bit (i386) architecture enabled."
|
||||||
|
else
|
||||||
|
log_info "32-bit (i386) architecture is already enabled."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Download and install a GPG key into /etc/apt/keyrings/
|
||||||
|
# Usage: apt_add_keyring_from_url "https://example.com/key.gpg" "example.gpg"
|
||||||
|
apt_add_keyring_from_url() {
|
||||||
|
local url="$1"
|
||||||
|
local keyring_filename="$2"
|
||||||
|
local dest_path="$APT_KEYRINGS_DIR/$keyring_filename"
|
||||||
|
|
||||||
|
apt_ensure_keyrings_dir
|
||||||
|
|
||||||
|
log_substep "Fetching GPG keyring from: $url -> $dest_path"
|
||||||
|
|
||||||
|
local tmp_key
|
||||||
|
tmp_key="$(mktemp /tmp/apt-key.XXXXXX)"
|
||||||
|
|
||||||
|
if curl -fsSL "$url" -o "$tmp_key"; then
|
||||||
|
# Check if the key is ASCII armored or binary
|
||||||
|
if gpg --dry-run --quiet --import "$tmp_key" >/dev/null 2>&1; then
|
||||||
|
if grep -q "BEGIN PGP PUBLIC KEY BLOCK" "$tmp_key"; then
|
||||||
|
gpg --dearmor --yes --batch -o "$dest_path" < "$tmp_key" 2>/dev/null || cp "$tmp_key" "$dest_path"
|
||||||
|
else
|
||||||
|
cp "$tmp_key" "$dest_path"
|
||||||
|
fi
|
||||||
|
chmod 0644 "$dest_path"
|
||||||
|
log_success "Keyring installed: $dest_path"
|
||||||
|
else
|
||||||
|
# If gpg check fails, fallback to direct copy if non-empty
|
||||||
|
if [[ -s "$tmp_key" ]]; then
|
||||||
|
cp "$tmp_key" "$dest_path"
|
||||||
|
chmod 0644 "$dest_path"
|
||||||
|
log_success "Keyring installed (raw): $dest_path"
|
||||||
|
else
|
||||||
|
log_error "Downloaded key from $url is empty or invalid."
|
||||||
|
rm -f "$tmp_key"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
rm -f "$tmp_key"
|
||||||
|
else
|
||||||
|
rm -f "$tmp_key"
|
||||||
|
log_error "Failed to download GPG key from $url"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install Deb822 sources file
|
||||||
|
# Usage: apt_install_sources_file "/path/to/source.sources" "debian-unstable.sources"
|
||||||
|
apt_install_sources_file() {
|
||||||
|
local src_path="$1"
|
||||||
|
local dest_name="$2"
|
||||||
|
local dest_path="$APT_SOURCES_DIR/$dest_name"
|
||||||
|
|
||||||
|
if [[ ! -f "$src_path" ]]; then
|
||||||
|
log_error "Source file does not exist: $src_path"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$APT_SOURCES_DIR"
|
||||||
|
cp "$src_path" "$dest_path"
|
||||||
|
chmod 0644 "$dest_path"
|
||||||
|
log_success "Installed APT Deb822 source: $dest_path"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install APT preferences file
|
||||||
|
# Usage: apt_install_preference_file "/path/to/pinning.pref" "99-custom.pref"
|
||||||
|
apt_install_preference_file() {
|
||||||
|
local src_path="$1"
|
||||||
|
local dest_name="$2"
|
||||||
|
local dest_path="$APT_PREFERENCES_DIR/$dest_name"
|
||||||
|
|
||||||
|
if [[ ! -f "$src_path" ]]; then
|
||||||
|
log_error "Preference file does not exist: $src_path"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$APT_PREFERENCES_DIR"
|
||||||
|
cp "$src_path" "$dest_path"
|
||||||
|
chmod 0644 "$dest_path"
|
||||||
|
log_success "Installed APT preference: $dest_path"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Update APT package indexes
|
||||||
|
apt_update() {
|
||||||
|
log_step "Updating APT package indexes..."
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get update -y
|
||||||
|
}
|
||||||
|
|
||||||
|
# Dist-upgrade system
|
||||||
|
apt_dist_upgrade() {
|
||||||
|
log_step "Upgrading system packages (dist-upgrade)..."
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get dist-upgrade -y
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install packages from a .list file
|
||||||
|
# Usage: apt_install_package_list_file "/path/to/packages.list"
|
||||||
|
apt_install_package_list_file() {
|
||||||
|
local list_file="$1"
|
||||||
|
|
||||||
|
if [[ ! -f "$list_file" ]]; then
|
||||||
|
log_error "Package list file not found: $list_file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local -a pkgs=()
|
||||||
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||||
|
# Trim leading/trailing whitespace
|
||||||
|
line="$(echo "$line" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
|
||||||
|
# Skip empty lines and comment lines
|
||||||
|
[[ -z "$line" || "$line" =~ ^# ]] && continue
|
||||||
|
pkgs+=("$line")
|
||||||
|
done < "$list_file"
|
||||||
|
|
||||||
|
if [[ ${#pkgs[@]} -eq 0 ]]; then
|
||||||
|
log_info "No packages to install from $list_file"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Installing ${#pkgs[@]} packages from $(basename "$list_file")..."
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y "${pkgs[@]}"
|
||||||
|
log_success "Installed packages from $(basename "$list_file")"
|
||||||
|
}
|
||||||
+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
|
||||||
|
}
|
||||||
+273
@@ -0,0 +1,273 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# lib/network.sh - Network interface configuration (Debian Installer style & Loopback)
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
# shellcheck source=lib/utils.sh
|
||||||
|
source "$SCRIPT_DIR/lib/utils.sh"
|
||||||
|
|
||||||
|
# Get a list of all physical LAN / Ethernet network interfaces on the system
|
||||||
|
get_lan_interfaces() {
|
||||||
|
local -a lan_ifaces=()
|
||||||
|
|
||||||
|
if [[ -d /sys/class/net ]]; then
|
||||||
|
for iface_path in /sys/class/net/*; do
|
||||||
|
[[ -e "$iface_path" ]] || continue
|
||||||
|
local iface
|
||||||
|
iface="$(basename "$iface_path")"
|
||||||
|
|
||||||
|
# Skip loopback interface
|
||||||
|
[[ "$iface" == "lo" ]] && continue
|
||||||
|
|
||||||
|
# Skip known virtual, bridge, container, tunnel and VPN interfaces
|
||||||
|
if [[ "$iface" =~ ^(docker[0-9]*|veth.*|virbr[0-9]*|br.*|bridge.*|tun[0-9]*|tap[0-9]*|wg[0-9]*|dummy[0-9]*|waydroid.*|tailscale.*|zt.*|bond.*|sit[0-9]*|ip6tnl.*|gre.*|erspan.*|vboxnet.*|vmnet.*)$ ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Skip wireless / Wi-Fi interfaces
|
||||||
|
if [[ -e "$iface_path/wireless" || -e "$iface_path/phy80211" || "$iface" =~ ^(wl.*|wlan.*|wifi.*|ath.*)$ ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if it is a physical device (has device link) or standard Ethernet type (type == 1)
|
||||||
|
local is_lan=0
|
||||||
|
local dev_type=""
|
||||||
|
if [[ -f "$iface_path/type" ]]; then
|
||||||
|
dev_type="$(cat "$iface_path/type" 2>/dev/null || echo "")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -e "$iface_path/device" && "$dev_type" == "1" ]]; then
|
||||||
|
is_lan=1
|
||||||
|
elif [[ "$iface" =~ ^(eth[0-9]+|en[a-zA-Z0-9]+)$ ]] && [[ "$dev_type" == "1" || -z "$dev_type" ]]; then
|
||||||
|
is_lan=1
|
||||||
|
elif [[ -e "$iface_path/device" ]]; then
|
||||||
|
is_lan=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$is_lan" -eq 1 ]]; then
|
||||||
|
lan_ifaces+=("$iface")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
elif command_exists ip; then
|
||||||
|
# Fallback to ip link parsing if /sys/class/net is not directly readable
|
||||||
|
while IFS= read -r line; do
|
||||||
|
local iface
|
||||||
|
iface="$(echo "$line" | awk -F': ' '{print $2}' | cut -d'@' -f1)"
|
||||||
|
[[ -z "$iface" || "$iface" == "lo" ]] && continue
|
||||||
|
if [[ "$iface" =~ ^(eth[0-9]+|en[a-zA-Z0-9]+)$ ]]; then
|
||||||
|
lan_ifaces+=("$iface")
|
||||||
|
fi
|
||||||
|
done < <(ip -o link show 2>/dev/null || true)
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "${lan_ifaces[@]:-}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if a specific interface is already configured in /etc/network/interfaces or interfaces.d
|
||||||
|
is_interface_configured() {
|
||||||
|
local iface="$1"
|
||||||
|
local interfaces_file="/etc/network/interfaces"
|
||||||
|
local interfaces_dir="/etc/network/interfaces.d"
|
||||||
|
|
||||||
|
if [[ -f "$interfaces_file" ]]; then
|
||||||
|
if grep -E -q "^[[:space:]]*(iface|auto|allow-hotplug)[[:space:]]+${iface}([[:space:]]|$)" "$interfaces_file" 2>/dev/null; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -d "$interfaces_dir" ]]; then
|
||||||
|
if grep -E -q -r "^[[:space:]]*(iface|auto|allow-hotplug)[[:space:]]+${iface}([[:space:]]|$)" "$interfaces_dir" 2>/dev/null; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Configure loopback interface in /etc/network/interfaces and /etc/hosts
|
||||||
|
configure_loopback_interface() {
|
||||||
|
local interfaces_file="/etc/network/interfaces"
|
||||||
|
local interfaces_dir="/etc/network/interfaces.d"
|
||||||
|
|
||||||
|
mkdir -p "/etc/network" "$interfaces_dir"
|
||||||
|
|
||||||
|
log_substep "Configuring loopback network interface (lo)..."
|
||||||
|
|
||||||
|
if [[ ! -f "$interfaces_file" ]]; then
|
||||||
|
cat <<EOF > "$interfaces_file"
|
||||||
|
# This file describes the network interfaces available on your system
|
||||||
|
# and how to activate them. For more information, see interfaces(5).
|
||||||
|
|
||||||
|
source /etc/network/interfaces.d/*
|
||||||
|
|
||||||
|
# The loopback network interface
|
||||||
|
auto lo
|
||||||
|
iface lo inet loopback
|
||||||
|
EOF
|
||||||
|
chmod 0644 "$interfaces_file"
|
||||||
|
log_success "Created $interfaces_file with loopback configuration."
|
||||||
|
else
|
||||||
|
backup_file_or_dir "$interfaces_file"
|
||||||
|
|
||||||
|
# Ensure source /etc/network/interfaces.d/* is present
|
||||||
|
if ! grep -E -q "^[[:space:]]*source[[:space:]]+/etc/network/interfaces\.d/\*" "$interfaces_file"; then
|
||||||
|
sed -i '1i # The loopback network interface\nsource /etc/network/interfaces.d/*\n' "$interfaces_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure auto lo and iface lo inet loopback are configured
|
||||||
|
if ! grep -E -q "^[[:space:]]*iface[[:space:]]+lo[[:space:]]+inet[[:space:]]+loopback" "$interfaces_file"; then
|
||||||
|
cat <<EOF >> "$interfaces_file"
|
||||||
|
|
||||||
|
# The loopback network interface
|
||||||
|
auto lo
|
||||||
|
iface lo inet loopback
|
||||||
|
EOF
|
||||||
|
log_success "Added loopback configuration to $interfaces_file."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure /etc/hosts has standard loopback mappings
|
||||||
|
if [[ -f /etc/hosts ]]; then
|
||||||
|
local host_name
|
||||||
|
host_name="$(hostname 2>/dev/null || cat /etc/hostname 2>/dev/null || echo "debian")"
|
||||||
|
if ! grep -q "127.0.0.1[[:space:]]\+localhost" /etc/hosts; then
|
||||||
|
echo "127.0.0.1 localhost" >> /etc/hosts
|
||||||
|
fi
|
||||||
|
if ! grep -q "::1[[:space:]]\+localhost" /etc/hosts; then
|
||||||
|
echo "::1 localhost ip6-localhost ip6-loopback" >> /etc/hosts
|
||||||
|
fi
|
||||||
|
if [[ -n "$host_name" ]] && ! grep -E -q "(127\.0\.1\.1|127\.0\.0\.1)[[:space:]]+.*$host_name" /etc/hosts; then
|
||||||
|
echo "127.0.1.1 $host_name" >> /etc/hosts
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Bring up loopback interface immediately
|
||||||
|
if command_exists ip; then
|
||||||
|
ip link set lo up 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if command_exists ifup; then
|
||||||
|
ifup lo 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
log_success "Loopback interface (lo) configured and activated."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Configure all detected physical LAN interfaces (Debian installer standard)
|
||||||
|
configure_lan_interfaces() {
|
||||||
|
local interfaces_file="/etc/network/interfaces"
|
||||||
|
local interfaces_dir="/etc/network/interfaces.d"
|
||||||
|
|
||||||
|
mkdir -p "/etc/network" "$interfaces_dir"
|
||||||
|
|
||||||
|
local -a lan_ifaces
|
||||||
|
read -r -a lan_ifaces <<< "$(get_lan_interfaces)"
|
||||||
|
|
||||||
|
if [[ ${#lan_ifaces[@]} -eq 0 ]]; then
|
||||||
|
log_info "No physical LAN interfaces detected at this time."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_substep "Found physical LAN interface(s): ${lan_ifaces[*]}"
|
||||||
|
|
||||||
|
# Backup interfaces file before modifications
|
||||||
|
if [[ -f "$interfaces_file" ]]; then
|
||||||
|
backup_file_or_dir "$interfaces_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for iface in "${lan_ifaces[@]}"; do
|
||||||
|
[[ -z "$iface" ]] && continue
|
||||||
|
|
||||||
|
if is_interface_configured "$iface"; then
|
||||||
|
log_info "LAN interface '$iface' is already configured."
|
||||||
|
else
|
||||||
|
log_info "Configuring LAN interface '$iface' (Debian Installer style: DHCP & IPv6 Auto)..."
|
||||||
|
cat <<EOF >> "$interfaces_file"
|
||||||
|
|
||||||
|
# LAN network interface: $iface
|
||||||
|
allow-hotplug $iface
|
||||||
|
iface $iface inet dhcp
|
||||||
|
iface $iface inet6 auto
|
||||||
|
EOF
|
||||||
|
log_success "Added LAN interface '$iface' to $interfaces_file."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure the interface is brought UP and usable
|
||||||
|
if command_exists ip; then
|
||||||
|
ip link set "$iface" up 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if command_exists ifup; then
|
||||||
|
ifup "$iface" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log_success "All LAN network interfaces configured and activated."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Configure NetworkManager to manage interfaces and work seamlessly with ifupdown
|
||||||
|
configure_network_manager() {
|
||||||
|
local nm_conf="/etc/NetworkManager/NetworkManager.conf"
|
||||||
|
local nm_dir="/etc/NetworkManager"
|
||||||
|
|
||||||
|
if [[ -d "$nm_dir" ]] || command_exists NetworkManager || command_exists nmcli; then
|
||||||
|
log_substep "Configuring NetworkManager integration (managed=true)..."
|
||||||
|
mkdir -p "$nm_dir"
|
||||||
|
|
||||||
|
if [[ ! -f "$nm_conf" ]]; then
|
||||||
|
cat <<EOF > "$nm_conf"
|
||||||
|
[main]
|
||||||
|
plugins=ifupdown,keyfile
|
||||||
|
|
||||||
|
[ifupdown]
|
||||||
|
managed=true
|
||||||
|
EOF
|
||||||
|
chmod 0644 "$nm_conf"
|
||||||
|
log_success "Created $nm_conf with managed=true."
|
||||||
|
else
|
||||||
|
backup_file_or_dir "$nm_conf"
|
||||||
|
|
||||||
|
# Set managed=true in [ifupdown] section
|
||||||
|
if grep -q "\[ifupdown\]" "$nm_conf"; then
|
||||||
|
sed -i '/\[ifupdown\]/,/^\[/{s/managed=false/managed=true/}' "$nm_conf"
|
||||||
|
if ! grep -A2 "\[ifupdown\]" "$nm_conf" | grep -q "managed="; then
|
||||||
|
sed -i '/\[ifupdown\]/a managed=true' "$nm_conf"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
cat <<EOF >> "$nm_conf"
|
||||||
|
|
||||||
|
[ifupdown]
|
||||||
|
managed=true
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
log_success "Updated $nm_conf to managed=true."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Enable networking and NetworkManager services
|
||||||
|
enable_network_services() {
|
||||||
|
if command_exists systemctl; then
|
||||||
|
log_substep "Enabling and activating network services..."
|
||||||
|
|
||||||
|
# Enable networking service (ifupdown)
|
||||||
|
systemctl enable networking.service 2>/dev/null || true
|
||||||
|
|
||||||
|
# Enable & start NetworkManager if available
|
||||||
|
if systemctl list-unit-files NetworkManager.service >/dev/null 2>&1 || [[ -f /lib/systemd/system/NetworkManager.service ]]; then
|
||||||
|
systemctl enable NetworkManager.service 2>/dev/null || true
|
||||||
|
systemctl start NetworkManager.service 2>/dev/null || true
|
||||||
|
log_success "NetworkManager service enabled and started."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Central orchestration function for network configuration
|
||||||
|
configure_network_all() {
|
||||||
|
log_substep "Configuring complete network subsystem (Loopback, all LAN interfaces, NetworkManager)..."
|
||||||
|
configure_loopback_interface
|
||||||
|
configure_lan_interfaces
|
||||||
|
configure_network_manager
|
||||||
|
enable_network_services
|
||||||
|
log_success "Network configuration completed successfully."
|
||||||
|
}
|
||||||
Executable
+189
@@ -0,0 +1,189 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# lib/tui.sh - Interactive Whiptail TUI dialogs and checklists
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# All whiptail dialogs are executed via 'run_whiptail' (lib/utils.sh), which
|
||||||
|
# binds stdin and stdout to the controlling terminal (/dev/tty). Without this,
|
||||||
|
# keyboard input (ENTER, SPACE, arrow keys) is consumed by the shell instead of
|
||||||
|
# the dialog whenever the setup is started through a pipe (curl ... | bash).
|
||||||
|
|
||||||
|
# Check if whiptail is available
|
||||||
|
tui_check_whiptail() {
|
||||||
|
if ! command_exists whiptail; then
|
||||||
|
log_warn "whiptail is not installed. Attempting to install whiptail..."
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get update -y && DEBIAN_FRONTEND=noninteractive apt-get install -y whiptail
|
||||||
|
fi
|
||||||
|
ensure_term
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine whether an interactive TUI can be displayed at all
|
||||||
|
tui_is_available() {
|
||||||
|
command_exists whiptail || return 1
|
||||||
|
has_tty || return 1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Compute dialog geometry into TUI_HEIGHT / TUI_WIDTH / TUI_LIST_HEIGHT
|
||||||
|
# Usage: tui_calc_geometry <desired_height> <desired_width> [desired_list_height]
|
||||||
|
tui_calc_geometry() {
|
||||||
|
local want_height="$1"
|
||||||
|
local want_width="$2"
|
||||||
|
local want_list="${3:-0}"
|
||||||
|
|
||||||
|
local term_size term_lines term_cols
|
||||||
|
term_size="$(get_term_size)"
|
||||||
|
term_lines="${term_size%% *}"
|
||||||
|
term_cols="${term_size##* }"
|
||||||
|
|
||||||
|
local height="$want_height"
|
||||||
|
local width="$want_width"
|
||||||
|
|
||||||
|
if (( term_lines - 2 < height )); then
|
||||||
|
height=$(( term_lines - 2 ))
|
||||||
|
fi
|
||||||
|
if (( height < 8 )); then
|
||||||
|
height=8
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( term_cols - 4 < width )); then
|
||||||
|
width=$(( term_cols - 4 ))
|
||||||
|
fi
|
||||||
|
if (( width < 40 )); then
|
||||||
|
width=40
|
||||||
|
fi
|
||||||
|
|
||||||
|
local list_height="$want_list"
|
||||||
|
if (( want_list > 0 )); then
|
||||||
|
if (( height - 8 < list_height )); then
|
||||||
|
list_height=$(( height - 8 ))
|
||||||
|
fi
|
||||||
|
if (( list_height < 3 )); then
|
||||||
|
list_height=3
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
TUI_HEIGHT="$height"
|
||||||
|
TUI_WIDTH="$width"
|
||||||
|
TUI_LIST_HEIGHT="$list_height"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Display welcome message
|
||||||
|
tui_welcome() {
|
||||||
|
tui_calc_geometry 14 70
|
||||||
|
|
||||||
|
run_whiptail --title "Debian Unstable Setup & Hyprland Installer" \
|
||||||
|
--msgbox "Willkommen beim Debian Unstable & Desktop Setup!\n\nDieses Skript führt dich durch die modulare Einrichtung deines Debian Sid Systems mit XanMod-Kernel, Hyprland und Optimierungen.\n\nDrücke ENTER um zur Modulauswahl zu gelangen." \
|
||||||
|
"$TUI_HEIGHT" "$TUI_WIDTH" || true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Present stage checklist to user
|
||||||
|
# Sets TUI_SELECTION and returns 0 on success, 1 on cancel
|
||||||
|
tui_select_stages() {
|
||||||
|
local tmp_file
|
||||||
|
tmp_file="$(mktemp)"
|
||||||
|
|
||||||
|
tui_calc_geometry 20 74 10
|
||||||
|
|
||||||
|
if run_whiptail --output-fd 3 --title "Modulauswahl / Stages" \
|
||||||
|
--checklist "Wähle die gewünschten Installations-Phasen mit der LEERTASTE aus (ENTER = OK):" \
|
||||||
|
"$TUI_HEIGHT" "$TUI_WIDTH" "$TUI_LIST_HEIGHT" \
|
||||||
|
"01" "Debian Unstable (Deb822, Pinning, Upgrade)" ON \
|
||||||
|
"02" "Kernel & Hardware (XanMod, P-State, Microcode)" ON \
|
||||||
|
"03" "Bootloader & Theme (GRUB, Vimix, Plymouth)" ON \
|
||||||
|
"04" "Pakete (Base, Desktop, Gaming, Virtualisierung)" ON \
|
||||||
|
"05" "Coding & Nerd Fonts (JetBrainsMono, VictorMono)" ON \
|
||||||
|
"06" "System-Dienste (AppArmor, Docker, Libvirt, zram, Ollama)" ON \
|
||||||
|
"07" "Userland & Skel (Zsh, Skel-Templates, Dotfiles)" ON \
|
||||||
|
"08" "Flatpaks (Flathub Repository & Flatpaks)" ON \
|
||||||
|
"09" "Zusatz-Apps & MIME (Spotify, Waydroid, OpenDeck, MIME)" ON \
|
||||||
|
"10" "Hyprland Desktop (LinuxBeginnings Debian-Hyprland Installer)" ON \
|
||||||
|
"11" "Linutil (Chris Titus Tech System Toolbox)" ON \
|
||||||
|
3> "$tmp_file"; then
|
||||||
|
|
||||||
|
TUI_SELECTION="$(tr -d '"' < "$tmp_file")"
|
||||||
|
export TUI_SELECTION
|
||||||
|
rm -f "$tmp_file"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
rm -f "$tmp_file"
|
||||||
|
TUI_SELECTION=""
|
||||||
|
export TUI_SELECTION
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prompt user for target user via Whiptail dialog
|
||||||
|
tui_prompt_target_user() {
|
||||||
|
local default_user="${1:-}"
|
||||||
|
local chosen=""
|
||||||
|
|
||||||
|
# Detect candidate user if not provided or root
|
||||||
|
if [[ -z "$default_user" || "$default_user" == "root" ]]; then
|
||||||
|
if [[ -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then
|
||||||
|
default_user="$SUDO_USER"
|
||||||
|
elif [[ -n "${PKEXEC_UID:-}" ]]; then
|
||||||
|
default_user="$(id -nu "$PKEXEC_UID" 2>/dev/null || true)"
|
||||||
|
elif [[ "$(id -u)" -ne 0 ]]; then
|
||||||
|
default_user="$(id -un)"
|
||||||
|
else
|
||||||
|
default_user="$(awk -F: '$3 >= 1000 && $3 < 60000 && $1 != "nobody" {print $1; exit}' /etc/passwd 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$default_user" && "$default_user" != "root" ]]; then
|
||||||
|
tui_calc_geometry 10 65
|
||||||
|
if run_whiptail --title "Ziel-Benutzer bestätigen" \
|
||||||
|
--yes-button "Verwenden" \
|
||||||
|
--no-button "Anderer Benutzer" \
|
||||||
|
--yesno "Erkannter Ziel-Benutzer: $default_user\n\nMöchtest du diesen Benutzer für die Konfiguration (Dotfiles, Gruppen, Hyprland) verwenden?" \
|
||||||
|
"$TUI_HEIGHT" "$TUI_WIDTH"; then
|
||||||
|
chosen="$default_user"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
local tmp_file
|
||||||
|
tmp_file="$(mktemp)"
|
||||||
|
|
||||||
|
while [[ -z "$chosen" ]]; do
|
||||||
|
tui_calc_geometry 10 60
|
||||||
|
if run_whiptail --output-fd 3 --title "Ziel-Benutzer eingeben" \
|
||||||
|
--inputbox "Bitte gib den Benutzernamen des Desktop-Benutzers ein:" \
|
||||||
|
"$TUI_HEIGHT" "$TUI_WIDTH" "$default_user" \
|
||||||
|
3> "$tmp_file"; then
|
||||||
|
|
||||||
|
local input_user
|
||||||
|
input_user="$(tr -d '[:space:]' < "$tmp_file")"
|
||||||
|
if [[ -z "$input_user" || "$input_user" == "root" ]]; then
|
||||||
|
run_whiptail --title "Ungültiger Benutzer" --msgbox "Ungültiger Benutzername (darf nicht leer oder root sein)." 8 55 || true
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if ! id "$input_user" >/dev/null 2>&1; then
|
||||||
|
run_whiptail --title "Benutzer nicht gefunden" --msgbox "Benutzer '$input_user' existiert nicht im System." 8 55 || true
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
chosen="$input_user"
|
||||||
|
else
|
||||||
|
rm -f "$tmp_file"
|
||||||
|
log_info "Setup durch Benutzer abgebrochen."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
rm -f "$tmp_file"
|
||||||
|
export TARGET_USER="$chosen"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Confirmation dialog before running
|
||||||
|
tui_confirm_execution() {
|
||||||
|
local selected_stages="$1"
|
||||||
|
local target_user="$2"
|
||||||
|
|
||||||
|
tui_calc_geometry 14 70
|
||||||
|
|
||||||
|
run_whiptail --title "Installation starten" \
|
||||||
|
--yesno "Folgende Konfiguration wird ausgeführt:\n\n- Ziel-Benutzer: $target_user\n- Ausgewählte Stages: $selected_stages\n\nMöchtest du die Installation jetzt starten?" \
|
||||||
|
"$TUI_HEIGHT" "$TUI_WIDTH"
|
||||||
|
}
|
||||||
Executable
+460
@@ -0,0 +1,460 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# lib/utils.sh - Common utilities, logging, error handling & user detection
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ANSI Color Codes
|
||||||
|
export CLR_RESET='\033[0m'
|
||||||
|
export CLR_BOLD='\033[1m'
|
||||||
|
export CLR_DIM='\033[2m'
|
||||||
|
export CLR_RED='\033[0;31m'
|
||||||
|
export CLR_GREEN='\033[0;32m'
|
||||||
|
export CLR_YELLOW='\033[0;33m'
|
||||||
|
export CLR_BLUE='\033[0;34m'
|
||||||
|
export CLR_MAGENTA='\033[0;35m'
|
||||||
|
export CLR_CYAN='\033[0;36m'
|
||||||
|
export CLR_WHITE='\033[0;37m'
|
||||||
|
|
||||||
|
# Disable colors if not running in a terminal
|
||||||
|
if [[ ! -t 1 ]]; then
|
||||||
|
CLR_RESET=""
|
||||||
|
CLR_BOLD=""
|
||||||
|
CLR_DIM=""
|
||||||
|
CLR_RED=""
|
||||||
|
CLR_GREEN=""
|
||||||
|
CLR_YELLOW=""
|
||||||
|
CLR_BLUE=""
|
||||||
|
CLR_MAGENTA=""
|
||||||
|
CLR_CYAN=""
|
||||||
|
CLR_WHITE=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Logging functions
|
||||||
|
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" "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_substep() {
|
||||||
|
printf " ${CLR_CYAN}->${CLR_RESET} %s\n" "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Error trap handler
|
||||||
|
setup_err_trap() {
|
||||||
|
# Required so the ERR trap also fires for failures inside sourced
|
||||||
|
# lib/*.sh functions (errtrace) and nested function calls (functrace),
|
||||||
|
# not just for top-level commands in the stage script itself.
|
||||||
|
set -o errtrace
|
||||||
|
set -o functrace
|
||||||
|
trap 'error_handler $? $LINENO "$BASH_COMMAND"' ERR
|
||||||
|
}
|
||||||
|
|
||||||
|
# Re-arms 'set -e' for the *next* command only, via a self-removing DEBUG
|
||||||
|
# trap. Bash normally terminates the script right after an ERR trap when
|
||||||
|
# errexit is set, no matter what the trap does - the only way to make the
|
||||||
|
# script actually resume past the failed line is to have errexit disabled
|
||||||
|
# at the moment the trap returns. Must be the very last statement executed
|
||||||
|
# on the "resume" path (no trailing 'return'/'break'/...): any further
|
||||||
|
# command run inside error_handler itself would consume the DEBUG trap's
|
||||||
|
# one-shot reset before control ever gets back to the failed line.
|
||||||
|
resume_after_error() {
|
||||||
|
set +e
|
||||||
|
trap 'set -e; trap - DEBUG' DEBUG
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ask the user what to do about a failed command.
|
||||||
|
# Echoes one of: r (retry), i (ignore), a (abort). Defaults to "a" when
|
||||||
|
# not running interactively (piped install, CI, DEBIAN_FRONTEND=noninteractive,
|
||||||
|
# AUTO_CONFIRM=1) or when the prompt can't be read, so unattended installs
|
||||||
|
# keep failing hard exactly like before this feature existed.
|
||||||
|
ask_retry_ignore_abort() {
|
||||||
|
local is_interactive=0
|
||||||
|
if { [[ -t 0 ]] || has_tty; } && [[ "${DEBIAN_FRONTEND:-}" != "noninteractive" && "${CI:-}" != "1" && "${AUTO_CONFIRM:-}" != "1" ]]; then
|
||||||
|
is_interactive=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$is_interactive" -ne 1 ]]; then
|
||||||
|
echo "a"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local tty_in="/dev/tty"
|
||||||
|
local tty_out="/dev/tty"
|
||||||
|
if ! has_tty; then
|
||||||
|
tty_in="/dev/stdin"
|
||||||
|
tty_out="/dev/stderr"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local prompt answer=""
|
||||||
|
prompt="$(printf "%b[W]iederholen%b / %b[i]gnorieren%b / %b[a]bbrechen%b (Standard: a): " \
|
||||||
|
"$CLR_BOLD" "$CLR_RESET" "$CLR_BOLD" "$CLR_RESET" "$CLR_BOLD" "$CLR_RESET")"
|
||||||
|
read -r -p "$prompt" answer < "$tty_in" > "$tty_out" 2>&1 || answer="a"
|
||||||
|
answer="$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' <<< "$answer")"
|
||||||
|
|
||||||
|
case "$answer" in
|
||||||
|
[Ww]*) echo "r" ;;
|
||||||
|
[Ii]*) echo "i" ;;
|
||||||
|
*) echo "a" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
error_handler() {
|
||||||
|
local exit_code="$1"
|
||||||
|
local line_no="$2"
|
||||||
|
local bash_command="$3"
|
||||||
|
|
||||||
|
log_error "Command failed with exit code $exit_code at line $line_no: '$bash_command'"
|
||||||
|
|
||||||
|
local choice
|
||||||
|
choice="$(ask_retry_ignore_abort)"
|
||||||
|
|
||||||
|
case "$choice" in
|
||||||
|
r)
|
||||||
|
# Bare shell keywords (return/exit/break/continue) can't be
|
||||||
|
# meaningfully re-run via eval - they'd just unwind the current
|
||||||
|
# function instead of redoing any actual work. Treat these like
|
||||||
|
# "ignore" instead of silently aborting on a nonsensical retry.
|
||||||
|
if [[ "$bash_command" =~ ^(return|exit|break|continue)([[:space:]]|$) ]]; then
|
||||||
|
log_warn "'$bash_command' kann nicht wiederholt werden - Fehler wird ignoriert."
|
||||||
|
resume_after_error
|
||||||
|
else
|
||||||
|
log_info "Wiederhole: $bash_command"
|
||||||
|
if eval "$bash_command"; then
|
||||||
|
log_success "Wiederholung erfolgreich, Installation wird fortgesetzt."
|
||||||
|
resume_after_error
|
||||||
|
else
|
||||||
|
error_handler "$?" "$line_no" "$bash_command"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
i)
|
||||||
|
log_warn "Fehler wird ignoriert, Installation wird fortgesetzt: $bash_command"
|
||||||
|
resume_after_error
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_error "Installation abgebrochen."
|
||||||
|
exit "$exit_code"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# Root permission check
|
||||||
|
require_root() {
|
||||||
|
if [[ "$(id -u)" -ne 0 ]]; then
|
||||||
|
log_error "This script must be run as root (or via sudo)!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if command exists
|
||||||
|
command_exists() {
|
||||||
|
command -v "$1" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Terminal / TTY helpers (required for interactive whiptail dialogs)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Check whether a usable controlling terminal is available
|
||||||
|
has_tty() {
|
||||||
|
[[ -c /dev/tty ]] || return 1
|
||||||
|
{ : < /dev/tty; } 2>/dev/null || return 1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ensure TERM is set to a value newt/whiptail is able to render
|
||||||
|
ensure_term() {
|
||||||
|
case "${TERM:-}" in
|
||||||
|
""|dumb|unknown)
|
||||||
|
if [[ -f /usr/share/terminfo/x/xterm-256color || -f /lib/terminfo/x/xterm-256color ]]; then
|
||||||
|
export TERM="xterm-256color"
|
||||||
|
else
|
||||||
|
export TERM="linux"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# Report the current terminal size as "<lines> <columns>"
|
||||||
|
get_term_size() {
|
||||||
|
local size=""
|
||||||
|
if has_tty; then
|
||||||
|
size="$(stty size < /dev/tty 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
if [[ -z "$size" ]]; then
|
||||||
|
size="$(stty size 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
if [[ ! "$size" =~ ^[0-9]+[[:space:]]+[0-9]+$ ]]; then
|
||||||
|
size="${LINES:-24} ${COLUMNS:-80}"
|
||||||
|
fi
|
||||||
|
echo "$size"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run whiptail with stdin and stdout bound to the controlling terminal.
|
||||||
|
# This is required because the setup may be started through a pipe
|
||||||
|
# (e.g. 'curl ... | bash'), where stdin is not the terminal and all
|
||||||
|
# keystrokes would otherwise be swallowed by the shell instead of the dialog.
|
||||||
|
# Dialog results must be requested via '--output-fd 3'.
|
||||||
|
run_whiptail() {
|
||||||
|
ensure_term
|
||||||
|
if has_tty; then
|
||||||
|
whiptail "$@" < /dev/tty > /dev/tty
|
||||||
|
else
|
||||||
|
whiptail "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ask a yes/no question on the CLI. Returns 0 for yes, 1 for no.
|
||||||
|
# On read failure (e.g. closed stdin), falls back to default_answer.
|
||||||
|
# Usage: confirm_yes_no "<prompt>" "<y|n default answer>" "$tty_in" "$tty_out"
|
||||||
|
confirm_yes_no() {
|
||||||
|
local prompt="$1"
|
||||||
|
local default_answer="$2"
|
||||||
|
local tty_in="$3"
|
||||||
|
local tty_out="$4"
|
||||||
|
local answer=""
|
||||||
|
read -r -p "$prompt" answer < "$tty_in" > "$tty_out" 2>&1 || answer="$default_answer"
|
||||||
|
answer="$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' <<< "$answer")"
|
||||||
|
if [[ -z "$answer" ]]; then
|
||||||
|
answer="$default_answer"
|
||||||
|
fi
|
||||||
|
[[ "$answer" =~ ^[YyJj] ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine and prompt for the non-root target user
|
||||||
|
prompt_target_user() {
|
||||||
|
if [[ -n "${TARGET_USER:-}" ]]; then
|
||||||
|
echo "$TARGET_USER"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local candidate=""
|
||||||
|
if [[ -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then
|
||||||
|
candidate="$SUDO_USER"
|
||||||
|
elif [[ -n "${PKEXEC_UID:-}" ]]; then
|
||||||
|
candidate="$(id -nu "$PKEXEC_UID" 2>/dev/null || true)"
|
||||||
|
elif [[ "$(id -u)" -ne 0 ]]; then
|
||||||
|
candidate="$(id -un)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If candidate is still empty or root, try to detect the first normal user with UID >= 1000
|
||||||
|
if [[ -z "$candidate" || "$candidate" == "root" ]]; then
|
||||||
|
candidate="$(awk -F: '$3 >= 1000 && $3 < 60000 && $1 != "nobody" {print $1; exit}' /etc/passwd 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local chosen=""
|
||||||
|
local is_interactive=0
|
||||||
|
if { [[ -t 0 ]] || has_tty; } && [[ "${DEBIAN_FRONTEND:-}" != "noninteractive" && "${CI:-}" != "1" && "${AUTO_CONFIRM:-}" != "1" ]]; then
|
||||||
|
is_interactive=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$is_interactive" -eq 1 ]]; then
|
||||||
|
local tty_in="/dev/tty"
|
||||||
|
local tty_out="/dev/tty"
|
||||||
|
if ! has_tty; then
|
||||||
|
tty_in="/dev/stdin"
|
||||||
|
tty_out="/dev/stderr"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$candidate" && "$candidate" != "root" ]]; then
|
||||||
|
printf "\n%bTarget User Detection:%b\n" "${CLR_CYAN}" "${CLR_RESET}" > "$tty_out"
|
||||||
|
printf "Detected target user: %b%s%b\n" "${CLR_BOLD}" "$candidate" "${CLR_RESET}" > "$tty_out"
|
||||||
|
if confirm_yes_no "Use target user '$candidate'? [Y/n]: " "y" "$tty_in" "$tty_out"; then
|
||||||
|
chosen="$candidate"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
while [[ -z "$chosen" ]]; do
|
||||||
|
local input_user=""
|
||||||
|
read -r -p "Please enter the target desktop username: " input_user < "$tty_in" > "$tty_out" 2>&1 || true
|
||||||
|
input_user="$(echo "$input_user" | tr -d '[:space:]')"
|
||||||
|
if [[ -z "$input_user" ]]; then
|
||||||
|
printf "%bUsername cannot be empty.%b\n" "${CLR_YELLOW}" "${CLR_RESET}" > "$tty_out"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [[ "$input_user" == "root" ]]; then
|
||||||
|
printf "%bRoot cannot be used as target desktop user.%b\n" "${CLR_YELLOW}" "${CLR_RESET}" > "$tty_out"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if ! id "$input_user" >/dev/null 2>&1; then
|
||||||
|
printf "%bUser '%s' does not exist on this system.%b\n" "${CLR_YELLOW}" "$input_user" "${CLR_RESET}" > "$tty_out"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
chosen="$input_user"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
chosen="$candidate"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$chosen" || "$chosen" == "root" ]]; then
|
||||||
|
log_error "Could not determine a valid non-root desktop target user."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! id "$chosen" >/dev/null 2>&1; then
|
||||||
|
log_error "Target user '$chosen' does not exist."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export TARGET_USER="$chosen"
|
||||||
|
echo "$chosen"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get the target user (memoized / exported in TARGET_USER)
|
||||||
|
get_target_user() {
|
||||||
|
prompt_target_user
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get home directory of target user
|
||||||
|
get_target_home() {
|
||||||
|
local user
|
||||||
|
user="$(get_target_user)"
|
||||||
|
local user_home
|
||||||
|
user_home="$(getent passwd "$user" | cut -d: -f6)"
|
||||||
|
if [[ -z "$user_home" || ! -d "$user_home" ]]; then
|
||||||
|
user_home="/home/$user"
|
||||||
|
fi
|
||||||
|
echo "$user_home"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prompt for Ollama AI models storage path
|
||||||
|
prompt_ollama_models_path() {
|
||||||
|
if [[ -n "${OLLAMA_MODELS:-}" ]]; then
|
||||||
|
echo "$OLLAMA_MODELS"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ollama's own default storage location (used by the official install
|
||||||
|
# script / systemd service, which runs as the "ollama" user).
|
||||||
|
local ollama_default_path="/usr/share/ollama/.ollama/models"
|
||||||
|
# Suggested custom path, only ever pre-filled once the user has
|
||||||
|
# explicitly declined the Ollama default above.
|
||||||
|
local custom_path_suggestion="${OLLAMA_MODELS_DEFAULT:-/mnt/Data/Software/ollama/models}"
|
||||||
|
local chosen=""
|
||||||
|
local is_interactive=0
|
||||||
|
if { [[ -t 0 ]] || has_tty; } && [[ "${DEBIAN_FRONTEND:-}" != "noninteractive" && "${CI:-}" != "1" && "${AUTO_CONFIRM:-}" != "1" ]]; then
|
||||||
|
is_interactive=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$is_interactive" -eq 1 ]]; then
|
||||||
|
local tty_in="/dev/tty"
|
||||||
|
local tty_out="/dev/tty"
|
||||||
|
if ! has_tty; then
|
||||||
|
tty_in="/dev/stdin"
|
||||||
|
tty_out="/dev/stderr"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If whiptail is available and running on an active terminal, first ask
|
||||||
|
# whether Ollama's own default path should be used, and only show the
|
||||||
|
# free-text path dialog (pre-filled with the custom suggestion) if not.
|
||||||
|
if command_exists whiptail && has_tty; then
|
||||||
|
local yesno_status=0
|
||||||
|
run_whiptail --title "Ollama KI-Modelle Speicherort" \
|
||||||
|
--yes-button "Standard verwenden" --no-button "Eigenen Pfad angeben" \
|
||||||
|
--yesno "Standard-Speicherort für Ollama-Modelle:\n\n$ollama_default_path\n\nMöchtest du diesen Speicherort verwenden?" \
|
||||||
|
12 70 || yesno_status=$?
|
||||||
|
if [[ "$yesno_status" -eq 0 || "$yesno_status" -eq 255 ]]; then
|
||||||
|
# Yes, or ESC/Cancel (255) - use the Ollama default,
|
||||||
|
# consistent with the inputbox fallback below where an
|
||||||
|
# empty or cancelled dialog also keeps its own default
|
||||||
|
# instead of asking again.
|
||||||
|
chosen="$ollama_default_path"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$chosen" ]]; then
|
||||||
|
local tui_tmp
|
||||||
|
tui_tmp="$(mktemp)"
|
||||||
|
if run_whiptail --output-fd 3 --title "Ollama KI-Modelle Speicherort" \
|
||||||
|
--inputbox "Bitte gib den Speicherort für die Ollama KI-Modelle an:" \
|
||||||
|
10 70 "$custom_path_suggestion" \
|
||||||
|
3> "$tui_tmp"; then
|
||||||
|
chosen="$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' < "$tui_tmp")"
|
||||||
|
fi
|
||||||
|
rm -f "$tui_tmp"
|
||||||
|
[[ -z "$chosen" ]] && chosen="$custom_path_suggestion"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# CLI fallback if whiptail was not used or failed
|
||||||
|
if [[ -z "$chosen" ]]; then
|
||||||
|
printf "\n%bOllama KI-Modelle Speicherort:%b\n" "${CLR_CYAN}" "${CLR_RESET}" > "$tty_out"
|
||||||
|
printf "Standard-Pfad: %b%s%b\n" "${CLR_BOLD}" "$ollama_default_path" "${CLR_RESET}" > "$tty_out"
|
||||||
|
if confirm_yes_no "Standard-Speicherort verwenden? [J/n]: " "j" "$tty_in" "$tty_out"; then
|
||||||
|
chosen="$ollama_default_path"
|
||||||
|
else
|
||||||
|
local input_path=""
|
||||||
|
read -r -p "Speicherort für Ollama-Modelle eingeben [$custom_path_suggestion]: " input_path < "$tty_in" > "$tty_out" 2>&1 || true
|
||||||
|
input_path="$(echo "$input_path" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
|
||||||
|
if [[ -n "$input_path" ]]; then
|
||||||
|
chosen="$input_path"
|
||||||
|
else
|
||||||
|
chosen="$custom_path_suggestion"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
chosen="$ollama_default_path"
|
||||||
|
fi
|
||||||
|
|
||||||
|
export OLLAMA_MODELS="$chosen"
|
||||||
|
echo "$chosen"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run a command as the target user
|
||||||
|
run_as_target_user() {
|
||||||
|
local user
|
||||||
|
user="$(get_target_user)"
|
||||||
|
if [[ "$(id -un)" == "$user" ]]; then
|
||||||
|
"$@"
|
||||||
|
else
|
||||||
|
su -l "$user" -s /bin/bash -c "$(printf "%q " "$@")"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run_as_user() {
|
||||||
|
run_as_target_user "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create a safe temporary directory with auto-cleanup
|
||||||
|
create_temp_dir() {
|
||||||
|
local prefix="${1:-setup-temp}"
|
||||||
|
local tmp_dir
|
||||||
|
tmp_dir="$(mktemp -d "/tmp/${prefix}.XXXXXX")"
|
||||||
|
echo "$tmp_dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Safe backup function
|
||||||
|
backup_file_or_dir() {
|
||||||
|
local target="$1"
|
||||||
|
local timestamp
|
||||||
|
timestamp="$(date +%Y%m%d_%H%M%S)"
|
||||||
|
if [[ -e "$target" ]]; then
|
||||||
|
local backup_path="${target}.backup_${timestamp}"
|
||||||
|
if cp -r "$target" "$backup_path" 2>/dev/null; then
|
||||||
|
log_info "Created backup: $backup_path"
|
||||||
|
else
|
||||||
|
log_warn "Could not create backup of $target (permission denied)."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
backup_file() {
|
||||||
|
backup_file_or_dir "$@"
|
||||||
|
}
|
||||||
@@ -0,0 +1,281 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# setup.sh - Central Orchestrator for Debian Unstable Setup & Hyprland Installer
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
# Source Libraries & Config
|
||||||
|
source "$SCRIPT_DIR/lib/utils.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/apt.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/btrfs.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/network.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/tui.sh"
|
||||||
|
|
||||||
|
if [[ -f "$SCRIPT_DIR/config/setup.conf" ]]; then
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source "$SCRIPT_DIR/config/setup.conf"
|
||||||
|
fi
|
||||||
|
|
||||||
|
setup_err_trap
|
||||||
|
|
||||||
|
# Reconnect stdin/stdout to the controlling terminal when the script was
|
||||||
|
# started through a pipe (e.g. 'curl ... | bash'). Otherwise keystrokes are
|
||||||
|
# consumed by the shell and never reach the interactive whiptail dialogs.
|
||||||
|
if has_tty; then
|
||||||
|
if [[ ! -t 0 ]]; then
|
||||||
|
exec < /dev/tty
|
||||||
|
fi
|
||||||
|
if [[ ! -t 1 ]]; then
|
||||||
|
exec > /dev/tty
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
ensure_term
|
||||||
|
|
||||||
|
# Show help text
|
||||||
|
show_help() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: ./setup.sh [OPTIONS]
|
||||||
|
|
||||||
|
Debian Unstable Setup & Desktop Customization Framework
|
||||||
|
|
||||||
|
Note: setup.sh elevates itself via sudo once needed, after any interactive
|
||||||
|
prompts have been answered. Run it as your normal desktop user - no need to
|
||||||
|
prefix it with 'sudo' yourself (though doing so still works).
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-a, --all Run all setup stages sequentially (00 through 11)
|
||||||
|
-s, --stages <list> Comma-separated list of stages to run (e.g. --stages 01,02,04)
|
||||||
|
-u, --user <username> Specify target desktop user (defaults to \$SUDO_USER)
|
||||||
|
-d, --dry-run Simulate stage execution without modifying the system
|
||||||
|
-h, --help Show this help message and exit
|
||||||
|
|
||||||
|
Available Stages:
|
||||||
|
00: Pre-flight Checks (System, Privileges, Internet)
|
||||||
|
01: Debian Unstable Migration (Deb822, Pinning, apt-listbugs, dist-upgrade)
|
||||||
|
02: Kernel & Hardware Tuning (XanMod, AMD/Intel P-State, auto-cpufreq)
|
||||||
|
03: Bootloader & Splash (GRUB configuration, Vimix Theme, Plymouth Solar)
|
||||||
|
04: Package Installation (Base, Desktop, Gaming, Virtualization, Multimedia)
|
||||||
|
05: Font Installation (JetBrainsMono, FantasqueSans, VictorMono)
|
||||||
|
06: System Services (AppArmor, Docker, Libvirt, zram, Ollama)
|
||||||
|
07: Skel & User Environment (Zsh, Templates, Dotfiles Sync)
|
||||||
|
08: Flatpak Applications (Flathub, Flatpak package list)
|
||||||
|
09: Standalone Applications & MIME (Spotify, Waydroid, OpenDeck, MIME types)
|
||||||
|
10: Hyprland Desktop (LinuxBeginnings Auto-Installer)
|
||||||
|
11: Linutil (Chris Titus Tech System Toolbox)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
./setup.sh # Interactive TUI mode
|
||||||
|
./setup.sh --all # Full automated install
|
||||||
|
./setup.sh --stages 04,05 # Install only packages and fonts
|
||||||
|
./setup.sh --stages 01 --dry-run # Dry-run stage 01
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# CLI Flags
|
||||||
|
FLAG_ALL=0
|
||||||
|
FLAG_STAGES=""
|
||||||
|
FLAG_DRY_RUN=0
|
||||||
|
export FLAG_DRY_RUN
|
||||||
|
CLI_USER=""
|
||||||
|
|
||||||
|
# Parse CLI arguments
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-a|--all)
|
||||||
|
FLAG_ALL=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-s|--stages)
|
||||||
|
if [[ -n "${2:-}" && ! "$2" =~ ^- ]]; then
|
||||||
|
FLAG_STAGES="$2"
|
||||||
|
shift 2
|
||||||
|
else
|
||||||
|
log_error "Option '$1' requires a comma-separated list of stages."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
-u|--user)
|
||||||
|
if [[ -n "${2:-}" && ! "$2" =~ ^- ]]; then
|
||||||
|
CLI_USER="$2"
|
||||||
|
shift 2
|
||||||
|
else
|
||||||
|
log_error "Option '$1' requires a username."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
-d|--dry-run)
|
||||||
|
FLAG_DRY_RUN=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
show_help
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_error "Unknown option: $1"
|
||||||
|
show_help
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -n "$CLI_USER" ]]; then
|
||||||
|
export TARGET_USER="$CLI_USER"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# NOTE: Root privileges are intentionally NOT required here. All interactive
|
||||||
|
# prompts (whiptail TUI, target user, Ollama path) must run BEFORE escalating
|
||||||
|
# to sudo - see the privilege escalation block below for why.
|
||||||
|
|
||||||
|
# Determine stages to run
|
||||||
|
SELECTED_STAGES=()
|
||||||
|
|
||||||
|
if [[ "$FLAG_ALL" -eq 1 ]]; then
|
||||||
|
SELECTED_STAGES=(01 02 03 04 05 06 07 08 09 10 11)
|
||||||
|
elif [[ -n "$FLAG_STAGES" ]]; then
|
||||||
|
IFS=',' read -r -a raw_stages <<< "$FLAG_STAGES"
|
||||||
|
for st in "${raw_stages[@]}"; do
|
||||||
|
# Format single digit numbers to two digits (e.g. 1 -> 01)
|
||||||
|
st="$(echo "$st" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
|
||||||
|
if [[ "$st" =~ ^[0-9]$ ]]; then
|
||||||
|
st="0$st"
|
||||||
|
fi
|
||||||
|
SELECTED_STAGES+=("$st")
|
||||||
|
done
|
||||||
|
else
|
||||||
|
# Interactive TUI Mode
|
||||||
|
if has_tty; then
|
||||||
|
tui_check_whiptail
|
||||||
|
tui_welcome
|
||||||
|
if ! tui_select_stages; then
|
||||||
|
log_info "Setup aborted by user."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${TUI_SELECTION:-}" ]]; then
|
||||||
|
log_warn "No stages selected. Exiting."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -r -a SELECTED_STAGES <<< "$TUI_SELECTION"
|
||||||
|
|
||||||
|
DETECTED_USER="${TARGET_USER:-${SUDO_USER:-$(logname 2>/dev/null || id -un 2>/dev/null || true)}}"
|
||||||
|
if [[ "$DETECTED_USER" == "root" ]]; then
|
||||||
|
DETECTED_USER=""
|
||||||
|
fi
|
||||||
|
tui_prompt_target_user "$DETECTED_USER"
|
||||||
|
|
||||||
|
if ! tui_confirm_execution "${SELECTED_STAGES[*]}" "$TARGET_USER"; then
|
||||||
|
log_info "Installation aborted by user."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_warn "No controlling terminal available - interactive TUI cannot be displayed."
|
||||||
|
log_info "Non-interactive shell and no parameters provided. Defaulting to --all."
|
||||||
|
SELECTED_STAGES=(01 02 03 04 05 06 07 08 09 10 11)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure TARGET_USER is determined and confirmed for all stages
|
||||||
|
if [[ -z "${TARGET_USER:-}" ]]; then
|
||||||
|
TARGET_USER="$(get_target_user)"
|
||||||
|
export TARGET_USER
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Pre-resolve the Ollama models path here (while still unprivileged and
|
||||||
|
# directly attached to the terminal) if stage 06 will run, so stage
|
||||||
|
# 06-services.sh never has to prompt again after privilege escalation.
|
||||||
|
if [[ "$FLAG_DRY_RUN" -eq 0 ]] && [[ -z "${OLLAMA_MODELS:-}" ]]; then
|
||||||
|
for st in "${SELECTED_STAGES[@]}"; do
|
||||||
|
if [[ "$st" == "06" ]]; then
|
||||||
|
OLLAMA_MODELS="$(prompt_ollama_models_path)"
|
||||||
|
export OLLAMA_MODELS
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escalate to root via sudo now that every interactive prompt (whiptail TUI,
|
||||||
|
# target user, Ollama path) has already been answered while directly attached
|
||||||
|
# to the terminal. sudo run from a non-interactive shell (e.g. 'curl | bash')
|
||||||
|
# does not reliably hand the controlling terminal's foreground process group
|
||||||
|
# to the command it execs, which leaves whiptail/read unable to receive any
|
||||||
|
# keystrokes. Running all interactive dialogs before this point, and passing
|
||||||
|
# the already-resolved choices through as flags/env vars, avoids ever needing
|
||||||
|
# terminal input again after the sudo boundary.
|
||||||
|
if [[ "$FLAG_DRY_RUN" -eq 0 ]] && [[ "$(id -u)" -ne 0 ]]; then
|
||||||
|
if ! command_exists sudo; then
|
||||||
|
log_error "sudo is required to run setup.sh. Please install sudo or run as root."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_info "Elevating privileges via sudo to continue installation..."
|
||||||
|
joined_stages="$(IFS=,; echo "${SELECTED_STAGES[*]}")"
|
||||||
|
sudo_env_args=("TARGET_USER=$TARGET_USER")
|
||||||
|
if [[ -n "${OLLAMA_MODELS:-}" ]]; then
|
||||||
|
sudo_env_args+=("OLLAMA_MODELS=$OLLAMA_MODELS")
|
||||||
|
fi
|
||||||
|
exec sudo "${sudo_env_args[@]}" bash "$SCRIPT_DIR/setup.sh" --stages "$joined_stages" --user "$TARGET_USER"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$FLAG_DRY_RUN" -eq 0 ]]; then
|
||||||
|
require_root
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Map stage identifier to script file
|
||||||
|
get_stage_script() {
|
||||||
|
local stage_id="$1"
|
||||||
|
local match
|
||||||
|
match="$(find "$SCRIPT_DIR/stages" -maxdepth 1 -name "${stage_id}-*.sh" -type f | head -n1)"
|
||||||
|
echo "$match"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Always run preflight check first unless dry-run
|
||||||
|
if [[ "$FLAG_DRY_RUN" -eq 0 ]]; then
|
||||||
|
PREFLIGHT_SCRIPT="$(get_stage_script "00")"
|
||||||
|
if [[ -n "$PREFLIGHT_SCRIPT" && -f "$PREFLIGHT_SCRIPT" ]]; then
|
||||||
|
bash "$PREFLIGHT_SCRIPT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Banner
|
||||||
|
log_step "Starting Debian Unstable Setup Execution"
|
||||||
|
log_info "Target User: $(get_target_user)"
|
||||||
|
log_info "Planned Stages: ${SELECTED_STAGES[*]}"
|
||||||
|
if [[ "$FLAG_DRY_RUN" -eq 1 ]]; then
|
||||||
|
log_warn "DRY-RUN MODE: No changes will be made to the system."
|
||||||
|
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
|
||||||
|
for stage in "${SELECTED_STAGES[@]}"; do
|
||||||
|
# Skip 00 if it was manually added since it already ran
|
||||||
|
if [[ "$stage" == "00" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
SCRIPT="$(get_stage_script "$stage")"
|
||||||
|
if [[ -z "$SCRIPT" || ! -f "$SCRIPT" ]]; then
|
||||||
|
log_warn "Stage script for '$stage' not found in $SCRIPT_DIR/stages! Skipping."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
STAGE_NAME="$(basename "$SCRIPT" .sh)"
|
||||||
|
log_step "Executing Stage: $STAGE_NAME"
|
||||||
|
|
||||||
|
if [[ "$FLAG_DRY_RUN" -eq 1 ]]; then
|
||||||
|
log_info "[DRY-RUN] Would execute: bash $SCRIPT"
|
||||||
|
else
|
||||||
|
bash "$SCRIPT"
|
||||||
|
log_success "Completed Stage: $STAGE_NAME"
|
||||||
|
fi
|
||||||
|
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!"
|
||||||
Executable
+89
@@ -0,0 +1,89 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# stages/00-preflight.sh - System pre-flight checks, permissions & internet connectivity
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/utils.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/btrfs.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/network.sh"
|
||||||
|
|
||||||
|
if [[ -f "$SCRIPT_DIR/config/setup.conf" ]]; then
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source "$SCRIPT_DIR/config/setup.conf"
|
||||||
|
fi
|
||||||
|
|
||||||
|
setup_err_trap
|
||||||
|
|
||||||
|
log_step "Running Stage 00: Pre-flight System Checks..."
|
||||||
|
|
||||||
|
# 1. Check Root Privileges
|
||||||
|
log_substep "Checking root privileges..."
|
||||||
|
require_root
|
||||||
|
log_success "Running with root privileges."
|
||||||
|
|
||||||
|
# 2. Check Target Desktop User
|
||||||
|
log_substep "Detecting non-root target user..."
|
||||||
|
TARGET_USER="$(get_target_user)"
|
||||||
|
TARGET_HOME="$(get_target_home)"
|
||||||
|
|
||||||
|
if [[ -z "$TARGET_USER" || "$TARGET_USER" == "root" ]]; then
|
||||||
|
log_error "Could not determine target user. Run via 'sudo ./setup.sh' as the desktop user or set TARGET_USER=<user>."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Target user identified: $TARGET_USER (Home: $TARGET_HOME)"
|
||||||
|
|
||||||
|
# 3. Configure and Activate Network Interfaces (Loopback & LAN)
|
||||||
|
log_substep "Initializing and activating network interfaces (Loopback & LAN)..."
|
||||||
|
configure_loopback_interface
|
||||||
|
configure_lan_interfaces
|
||||||
|
|
||||||
|
# 4. Check Internet Connectivity
|
||||||
|
log_substep "Checking internet connectivity..."
|
||||||
|
INTERNET_OK=0
|
||||||
|
for host in "deb.debian.org" "1.1.1.1" "8.8.8.8"; do
|
||||||
|
if ping -c 1 -W 3 "$host" >/dev/null 2>&1 || curl -s --head --connect-timeout 3 "http://$host" >/dev/null 2>&1; then
|
||||||
|
INTERNET_OK=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$INTERNET_OK" -ne 1 ]]; then
|
||||||
|
log_error "No active internet connection detected. Please connect to the internet and try again."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "Internet connection verified."
|
||||||
|
|
||||||
|
# 5. Check Operating System
|
||||||
|
log_substep "Verifying operating system..."
|
||||||
|
if [[ -f /etc/os-release ]]; then
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source /etc/os-release
|
||||||
|
log_info "Detected OS: ${NAME:-Debian} ${VERSION_ID:-} (${VERSION_CODENAME:-sid})"
|
||||||
|
if [[ "${ID:-}" != "debian" && "${ID_LIKE:-}" != *"debian"* ]]; then
|
||||||
|
log_warn "This setup script is optimized for Debian GNU/Linux. Current ID is '${ID:-unknown}'."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_warn "/etc/os-release not found. Proceeding with caution."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 6. Check Disk Space
|
||||||
|
log_substep "Checking available disk space..."
|
||||||
|
AVAILABLE_KB=$(df --output=avail / | tail -n1)
|
||||||
|
if [[ "$AVAILABLE_KB" -lt 5242880 ]]; then # Less than 5 GB
|
||||||
|
log_warn "Less than 5 GB of free space available on root partition ($(( AVAILABLE_KB / 1024 )) MB free)."
|
||||||
|
else
|
||||||
|
log_success "Sufficient disk space available ($(( AVAILABLE_KB / 1024 / 1024 )) GB free)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 7. Check & Setup Btrfs Subvolumes (@, @home)
|
||||||
|
log_substep "Checking Btrfs root volume & subvolume layout..."
|
||||||
|
check_and_setup_btrfs_subvolumes
|
||||||
|
|
||||||
|
# 8. 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."
|
||||||
Executable
+83
@@ -0,0 +1,83 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# stages/01-apt-unstable.sh - Migration to Debian Unstable (Deb822), Pinning & Upgrade
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/utils.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
|
||||||
|
|
||||||
|
log_step "Running Stage 01: Debian Unstable Migration & Package Sources Configuration"
|
||||||
|
|
||||||
|
require_root
|
||||||
|
|
||||||
|
# 1. Install prerequisites for APT HTTPS & Keyrings
|
||||||
|
log_substep "Installing APT prerequisites (curl, gnupg, ca-certificates)..."
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get update -y
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||||
|
curl \
|
||||||
|
gnupg \
|
||||||
|
gpg \
|
||||||
|
ca-certificates \
|
||||||
|
apt-transport-https \
|
||||||
|
debian-archive-keyring
|
||||||
|
|
||||||
|
# 2. Enable 32-bit (i386) Multiarch Architecture
|
||||||
|
log_substep "Enabling 32-bit (i386) multiarch architecture..."
|
||||||
|
apt_enable_i386
|
||||||
|
|
||||||
|
# 3. Ensure Keyrings directory exists and install repository keyrings
|
||||||
|
apt_ensure_keyrings_dir
|
||||||
|
log_substep "Installing custom repository keyrings..."
|
||||||
|
apt_add_keyring_from_url "https://gitea.creative-dragonslayer.de/api/packages/Linuxapps/debian/repository.key" "gitea-Linuxapps.asc" || log_warn "Could not fetch gitea-Linuxapps.asc keyring."
|
||||||
|
apt_add_keyring_from_url "https://gitea.creative-dragonslayer.de/api/packages/Mirror/debian/repository.key" "gitea-Mirror.asc" || log_warn "Could not fetch gitea-Mirror.asc keyring."
|
||||||
|
|
||||||
|
# 4. Migrate /etc/apt/sources.list to Deb822 format
|
||||||
|
log_substep "Migrating APT sources to Deb822 format..."
|
||||||
|
if [[ -f /etc/apt/sources.list && ! -f /etc/apt/sources.list.bak_pre_unstable ]]; then
|
||||||
|
cp /etc/apt/sources.list /etc/apt/sources.list.bak_pre_unstable
|
||||||
|
# Comment out active lines in legacy sources.list to avoid duplicate source warnings
|
||||||
|
sed -i 's/^[[:space:]]*deb/# deb/' /etc/apt/sources.list
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Deploy Deb822 sources
|
||||||
|
log_substep "Deploying Deb822 APT sources..."
|
||||||
|
if compgen -G "$SCRIPT_DIR/data/apt/sources.list.d/*.sources" > /dev/null; then
|
||||||
|
for src_file in "$SCRIPT_DIR"/data/apt/sources.list.d/*.sources; do
|
||||||
|
if [[ "$(basename "$src_file")" != "xanmod.sources" ]]; then
|
||||||
|
apt_install_sources_file "$src_file" "$(basename "$src_file")"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 5. Deploy APT Preferences / Pinning
|
||||||
|
log_substep "Configuring APT Pinning preferences..."
|
||||||
|
mkdir -p /etc/apt/preferences.d
|
||||||
|
if compgen -G "$SCRIPT_DIR/data/apt/preferences.d/*.pref" > /dev/null; then
|
||||||
|
for pref_file in "$SCRIPT_DIR"/data/apt/preferences.d/*.pref; do
|
||||||
|
apt_install_preference_file "$pref_file" "$(basename "$pref_file")"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 6. Install apt-listbugs and apt-listchanges to safeguard Unstable upgrades
|
||||||
|
log_substep "Installing protection tools (apt-listbugs, apt-listchanges)..."
|
||||||
|
apt_update
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y apt-listbugs apt-listchanges
|
||||||
|
|
||||||
|
# 7. Perform Full Dist-Upgrade
|
||||||
|
log_substep "Performing full distribution upgrade to Debian Sid..."
|
||||||
|
create_timeshift_snapshot "Stage 01: Pre-dist-upgrade to Debian Sid" "B"
|
||||||
|
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!"
|
||||||
Executable
+143
@@ -0,0 +1,143 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# stages/02-kernel-hardware.sh - XanMod Kernel, Microcode, auto-cpufreq & Hardware Tuning
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/utils.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
|
||||||
|
|
||||||
|
log_step "Running Stage 02: Kernel & Hardware Tuning"
|
||||||
|
|
||||||
|
require_root
|
||||||
|
|
||||||
|
# 1. Setup XanMod Kernel Repository
|
||||||
|
log_substep "Configuring XanMod Kernel repository..."
|
||||||
|
apt_ensure_keyrings_dir
|
||||||
|
apt_add_keyring_from_url "https://dl.xanmod.org/archive.key" "xanmod-archive-keyring.gpg"
|
||||||
|
|
||||||
|
if [[ -f "$SCRIPT_DIR/data/apt/sources.list.d/xanmod.sources" ]]; then
|
||||||
|
apt_install_sources_file "$SCRIPT_DIR/data/apt/sources.list.d/xanmod.sources" "xanmod.sources"
|
||||||
|
fi
|
||||||
|
|
||||||
|
apt_update
|
||||||
|
|
||||||
|
# 2. Determine XanMod Kernel Package
|
||||||
|
log_substep "Detecting CPU microarchitecture level and XanMod branch..."
|
||||||
|
create_timeshift_snapshot "Stage 02: Pre-XanMod kernel installation" "B"
|
||||||
|
BRANCH="${XANMOD_BRANCH:-main}"
|
||||||
|
log_info "Configured XanMod branch: $BRANCH"
|
||||||
|
|
||||||
|
BRANCH_SUFFIX=""
|
||||||
|
if [[ "$BRANCH" != "main" && -n "$BRANCH" ]]; then
|
||||||
|
BRANCH_SUFFIX="-$BRANCH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detect CPU x86-64 microarchitecture level
|
||||||
|
ARCH_SUFFIX="-x64v2"
|
||||||
|
if grep -q "avx2" /proc/cpuinfo 2>/dev/null; then
|
||||||
|
ARCH_SUFFIX="-x64v3"
|
||||||
|
log_info "CPU supports x86-64-v3 (AVX2 detected)."
|
||||||
|
else
|
||||||
|
log_info "CPU does not support AVX2. Using x86-64-v2."
|
||||||
|
fi
|
||||||
|
|
||||||
|
KERNEL_PKG="linux-xanmod${BRANCH_SUFFIX}${ARCH_SUFFIX}"
|
||||||
|
FALLBACK_PKG="linux-xanmod${BRANCH_SUFFIX}"
|
||||||
|
|
||||||
|
log_substep "Installing XanMod kernel package ($KERNEL_PKG)..."
|
||||||
|
if ! DEBIAN_FRONTEND=noninteractive apt-get install -y "$KERNEL_PKG"; then
|
||||||
|
log_warn "Failed to install $KERNEL_PKG, trying fallback package $FALLBACK_PKG..."
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y "$FALLBACK_PKG" || log_warn "XanMod installation failed, standard kernel will remain active."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. CPU Microcode Detection and Installation
|
||||||
|
log_substep "Detecting CPU vendor for microcode installation..."
|
||||||
|
CPU_VENDOR=$(lscpu 2>/dev/null | grep -oP 'Vendor ID:\s*\K.*' | tr -d ' ' || true)
|
||||||
|
|
||||||
|
case "$CPU_VENDOR" in
|
||||||
|
"AuthenticAMD")
|
||||||
|
log_info "AMD CPU detected ($CPU_VENDOR) -> Installing amd64-microcode"
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y amd64-microcode
|
||||||
|
;;
|
||||||
|
"GenuineIntel")
|
||||||
|
log_info "Intel CPU detected ($CPU_VENDOR) -> Installing intel-microcode"
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y intel-microcode
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_warn "Unknown CPU vendor '$CPU_VENDOR'. Skipping vendor-specific microcode."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# 4. Install & Enable auto-cpufreq from source repository
|
||||||
|
log_substep "Installing and configuring auto-cpufreq..."
|
||||||
|
if command_exists auto-cpufreq; then
|
||||||
|
log_info "auto-cpufreq is already installed."
|
||||||
|
else
|
||||||
|
log_info "Cloning and building auto-cpufreq from GitHub repository..."
|
||||||
|
# Ensure git and python3 build essentials are available
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends git python3 python3-pip python3-setuptools 2>/dev/null || true
|
||||||
|
|
||||||
|
TEMP_DIR="$(create_temp_dir "auto-cpufreq")"
|
||||||
|
if git clone --depth=1 https://github.com/AdnanHodzic/auto-cpufreq.git "$TEMP_DIR" 2>/dev/null; then
|
||||||
|
(
|
||||||
|
cd "$TEMP_DIR"
|
||||||
|
./auto-cpufreq-installer --install || ./auto-cpufreq-installer --install --non-interactive || true
|
||||||
|
)
|
||||||
|
rm -rf "$TEMP_DIR"
|
||||||
|
else
|
||||||
|
log_warn "Failed to clone auto-cpufreq repository from GitHub."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command_exists auto-cpufreq; then
|
||||||
|
auto-cpufreq --install 2>/dev/null || true
|
||||||
|
if command_exists systemctl; then
|
||||||
|
systemctl enable --now auto-cpufreq 2>/dev/null || log_warn "Could not enable auto-cpufreq systemd service."
|
||||||
|
fi
|
||||||
|
log_success "auto-cpufreq installed and service enabled."
|
||||||
|
else
|
||||||
|
log_warn "auto-cpufreq installation could not be completed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 5. Environment configuration (Raytracing & Upscaling)
|
||||||
|
log_substep "Deploying graphics and performance environment configurations..."
|
||||||
|
mkdir -p /etc/environment.d
|
||||||
|
if compgen -G "$SCRIPT_DIR/data/etc/environment.d/*.conf" > /dev/null; then
|
||||||
|
for env_file in "$SCRIPT_DIR"/data/etc/environment.d/*.conf; do
|
||||||
|
cp "$env_file" /etc/environment.d/
|
||||||
|
log_info "Installed environment config: $(basename "$env_file")"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 6. Persistent Shader Cache Location (Mesa & DXVK) - optional relocation
|
||||||
|
if [[ -n "${SHADER_CACHE_DIR:-}" ]]; then
|
||||||
|
log_substep "Configuring custom Mesa/DXVK shader cache location..."
|
||||||
|
TARGET_USER="${TARGET_USER:-$(prompt_target_user)}"
|
||||||
|
mkdir -p "$SHADER_CACHE_DIR"
|
||||||
|
chown -R "$TARGET_USER:$TARGET_USER" "$SHADER_CACHE_DIR"
|
||||||
|
cat <<EOF > /etc/environment.d/99-shader-cache.conf
|
||||||
|
# Persistent Mesa (RADV/OpenGL) & DXVK (Wine/Proton) shader/pipeline cache,
|
||||||
|
# relocated off the system disk to avoid re-compilation stutter after reboots.
|
||||||
|
MESA_SHADER_CACHE_DIR=$SHADER_CACHE_DIR
|
||||||
|
DXVK_STATE_CACHE_PATH=$SHADER_CACHE_DIR
|
||||||
|
EOF
|
||||||
|
chmod 0644 /etc/environment.d/99-shader-cache.conf
|
||||||
|
log_success "Shader cache directory set to $SHADER_CACHE_DIR"
|
||||||
|
else
|
||||||
|
log_info "SHADER_CACHE_DIR not set - keeping Mesa's and DXVK's own default shader cache locations."
|
||||||
|
fi
|
||||||
|
|
||||||
|
create_timeshift_snapshot "Stage 02: Post-kernel and hardware tuning" "O"
|
||||||
|
|
||||||
|
log_success "Stage 02: Kernel and Hardware Tuning completed successfully!"
|
||||||
Executable
+122
@@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# stages/03-bootloader.sh - GRUB configuration, CPU P-State, Vimix Theme & Plymouth
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
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
|
||||||
|
|
||||||
|
log_step "Running Stage 03: Bootloader & Splash Screen Configuration"
|
||||||
|
|
||||||
|
require_root
|
||||||
|
|
||||||
|
GRUB_CFG="/etc/default/grub"
|
||||||
|
GRUB_TIMEOUT="${GRUB_TIMEOUT:-5}"
|
||||||
|
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="rcutree.rcu_idle_gp_delay=1"
|
||||||
|
GRUB_THEME_DIR="/usr/share/debian-gaming/grub/grub2-themes"
|
||||||
|
|
||||||
|
# 1. CPU P-State Detection
|
||||||
|
log_substep "Detecting CPU vendor for P-State parameter..."
|
||||||
|
CPU_VENDOR=$(lscpu 2>/dev/null | grep -oP 'Vendor ID:\s*\K.*' | tr -d ' ' || true)
|
||||||
|
CPU_MODEL=$(lscpu 2>/dev/null | grep -oP 'Model name:\s*\K.*' | head -c 30 || true)
|
||||||
|
|
||||||
|
log_info "Detected CPU: $CPU_MODEL ($CPU_VENDOR)"
|
||||||
|
|
||||||
|
CPU_PARAM=""
|
||||||
|
case "$CPU_VENDOR" in
|
||||||
|
"AuthenticAMD")
|
||||||
|
CPU_PARAM="amd_pstate=active"
|
||||||
|
log_info "AMD CPU detected -> Adding amd_pstate=active"
|
||||||
|
;;
|
||||||
|
"GenuineIntel")
|
||||||
|
CPU_PARAM="intel_pstate=active"
|
||||||
|
log_info "Intel CPU detected -> Adding intel_pstate=active"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_warn "Unknown CPU Vendor '$CPU_VENDOR' - no CPU pstate parameter added."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ -n "$CPU_PARAM" ]]; then
|
||||||
|
GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT_BASE $CPU_PARAM"
|
||||||
|
else
|
||||||
|
GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT_BASE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Configure /etc/default/grub
|
||||||
|
if [[ -f "$GRUB_CFG" ]]; then
|
||||||
|
log_substep "Updating $GRUB_CFG..."
|
||||||
|
backup_file_or_dir "$GRUB_CFG"
|
||||||
|
|
||||||
|
# Set parameters with sed
|
||||||
|
sed -i "s|^GRUB_TIMEOUT=.*|GRUB_TIMEOUT=$GRUB_TIMEOUT|" "$GRUB_CFG"
|
||||||
|
sed -i "s|^GRUB_TIMEOUT_STYLE=.*|GRUB_TIMEOUT_STYLE=$GRUB_TIMEOUT_STYLE|" "$GRUB_CFG"
|
||||||
|
sed -i "s|^GRUB_CMDLINE_LINUX_DEFAULT=.*|GRUB_CMDLINE_LINUX_DEFAULT=\"$GRUB_CMDLINE_LINUX_DEFAULT\"|" "$GRUB_CFG"
|
||||||
|
sed -i "s|^GRUB_CMDLINE_LINUX=.*|GRUB_CMDLINE_LINUX=\"$GRUB_CMDLINE_LINUX\"|" "$GRUB_CFG"
|
||||||
|
|
||||||
|
# Ensure entries exist if sed didn't find them
|
||||||
|
grep -q '^GRUB_TIMEOUT=' "$GRUB_CFG" || echo "GRUB_TIMEOUT=$GRUB_TIMEOUT" >> "$GRUB_CFG"
|
||||||
|
grep -q '^GRUB_TIMEOUT_STYLE=' "$GRUB_CFG" || echo "GRUB_TIMEOUT_STYLE=$GRUB_TIMEOUT_STYLE" >> "$GRUB_CFG"
|
||||||
|
grep -q '^GRUB_CMDLINE_LINUX_DEFAULT=' "$GRUB_CFG" || echo "GRUB_CMDLINE_LINUX_DEFAULT=\"$GRUB_CMDLINE_LINUX_DEFAULT\"" >> "$GRUB_CFG"
|
||||||
|
grep -q '^GRUB_CMDLINE_LINUX=' "$GRUB_CFG" || echo "GRUB_CMDLINE_LINUX=\"$GRUB_CMDLINE_LINUX\"" >> "$GRUB_CFG"
|
||||||
|
else
|
||||||
|
log_warn "$GRUB_CFG not found. Is GRUB installed?"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Install & Configure Plymouth
|
||||||
|
log_substep "Installing and configuring Plymouth splash theme..."
|
||||||
|
if DEBIAN_FRONTEND=noninteractive apt-get install -y plymouth plymouth-themes 2>/dev/null; then
|
||||||
|
PLYMOUTH_THEME="${PLYMOUTH_THEME:-solar}"
|
||||||
|
if command_exists plymouth-set-default-theme; then
|
||||||
|
plymouth-set-default-theme -R "$PLYMOUTH_THEME" 2>/dev/null || log_warn "Could not set Plymouth theme to $PLYMOUTH_THEME"
|
||||||
|
log_success "Plymouth configured with theme: $PLYMOUTH_THEME"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. Install Vimix GRUB Theme
|
||||||
|
log_substep "Installing Vimix GRUB theme..."
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y git 2>/dev/null || true
|
||||||
|
mkdir -p "$GRUB_THEME_DIR"
|
||||||
|
if [[ ! -d "$GRUB_THEME_DIR/.git" ]]; then
|
||||||
|
if git clone --depth 1 https://github.com/vinceliuice/grub2-themes.git "$GRUB_THEME_DIR" 2>/dev/null; then
|
||||||
|
"$GRUB_THEME_DIR/install.sh" -b -t "${GRUB_THEME:-vimix}" 2>/dev/null || log_warn "Vimix theme installer encountered an issue."
|
||||||
|
log_success "Vimix GRUB theme installed."
|
||||||
|
else
|
||||||
|
log_warn "Failed to clone grub2-themes repository. Skipping theme."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_info "GRUB Theme repository already cloned; updating..."
|
||||||
|
(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
|
||||||
|
|
||||||
|
# 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..."
|
||||||
|
if command_exists update-grub; then
|
||||||
|
update-grub
|
||||||
|
fi
|
||||||
|
if command_exists update-initramfs; then
|
||||||
|
update-initramfs -u 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Stage 03: Bootloader and Splash configuration completed successfully!"
|
||||||
Executable
+60
@@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# stages/04-packages.sh - Installation of categorised system and desktop packages
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/utils.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
|
||||||
|
|
||||||
|
log_step "Running Stage 04: Thematic Package Installation"
|
||||||
|
|
||||||
|
require_root
|
||||||
|
|
||||||
|
PACKAGES_DIR="$SCRIPT_DIR/config/packages"
|
||||||
|
|
||||||
|
if [[ ! -d "$PACKAGES_DIR" ]]; then
|
||||||
|
log_error "Package configuration directory $PACKAGES_DIR not found!"
|
||||||
|
exit 1
|
||||||
|
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)
|
||||||
|
APT_LISTS=(
|
||||||
|
"01-base.list"
|
||||||
|
"02-desktop.list"
|
||||||
|
"03-gaming.list"
|
||||||
|
"04-virtualization.list"
|
||||||
|
"05-multimedia.list"
|
||||||
|
)
|
||||||
|
|
||||||
|
log_substep "Ensuring 32-bit (i386) multiarch architecture is enabled..."
|
||||||
|
apt_enable_i386
|
||||||
|
|
||||||
|
log_substep "Updating package lists before installation..."
|
||||||
|
apt_update
|
||||||
|
|
||||||
|
for list_name in "${APT_LISTS[@]}"; do
|
||||||
|
list_path="$PACKAGES_DIR/$list_name"
|
||||||
|
if [[ -f "$list_path" ]]; then
|
||||||
|
log_step "Installing package category: $list_name"
|
||||||
|
apt_install_package_list_file "$list_path" || log_warn "Some packages from $list_name could not be installed."
|
||||||
|
else
|
||||||
|
log_warn "Package list file $list_name not found in $PACKAGES_DIR."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
create_timeshift_snapshot "Stage 04: Post-thematic package installation" "O"
|
||||||
|
|
||||||
|
log_success "Stage 04: Thematic package installation completed successfully!"
|
||||||
Executable
+74
@@ -0,0 +1,74 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# stages/05-fonts.sh - Download and clean installation of Coding & Nerd Fonts
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/utils.sh"
|
||||||
|
|
||||||
|
setup_err_trap
|
||||||
|
|
||||||
|
log_step "Running Stage 05: Coding & Nerd Fonts Installation"
|
||||||
|
|
||||||
|
require_root
|
||||||
|
|
||||||
|
FONT_DIR="/usr/share/fonts"
|
||||||
|
TEMP_DIR="$(create_temp_dir "fonts-setup")"
|
||||||
|
|
||||||
|
# Ensure cleanup of temp directory on exit
|
||||||
|
cleanup() {
|
||||||
|
if [[ -d "$TEMP_DIR" ]]; then
|
||||||
|
log_info "Cleaning up temporary font download directory: $TEMP_DIR"
|
||||||
|
rm -rf "$TEMP_DIR"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
mkdir -p "$FONT_DIR"
|
||||||
|
|
||||||
|
# 1. JetBrains Mono Nerd Font
|
||||||
|
log_substep "Downloading and installing JetBrainsMono Nerd Font..."
|
||||||
|
JB_ARCHIVE="$TEMP_DIR/JetBrainsMono.tar.xz"
|
||||||
|
JB_DEST="$FONT_DIR/JetBrainsMonoNerd"
|
||||||
|
if curl -fsSL "https://github.com/ryanoasis/nerd-fonts/releases/latest/download/JetBrainsMono.tar.xz" -o "$JB_ARCHIVE"; then
|
||||||
|
mkdir -p "$JB_DEST"
|
||||||
|
tar -xJkf "$JB_ARCHIVE" -C "$JB_DEST" 2>/dev/null || tar -xJf "$JB_ARCHIVE" -C "$JB_DEST"
|
||||||
|
log_success "JetBrainsMono Nerd Font installed to $JB_DEST"
|
||||||
|
else
|
||||||
|
log_warn "Failed to download JetBrainsMono Nerd Font."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Fantasque Sans Mono Nerd Font
|
||||||
|
log_substep "Downloading and installing Fantasque Sans Mono Nerd Font..."
|
||||||
|
FANTASQUE_ARCHIVE="$TEMP_DIR/FantasqueSansMono.zip"
|
||||||
|
FANTASQUE_DEST="$FONT_DIR/FantasqueSansMonoNerd"
|
||||||
|
if curl -fsSL "https://github.com/ryanoasis/nerd-fonts/releases/download/v3.3.0/FantasqueSansMono.zip" -o "$FANTASQUE_ARCHIVE"; then
|
||||||
|
mkdir -p "$FANTASQUE_DEST"
|
||||||
|
unzip -o -q "$FANTASQUE_ARCHIVE" -d "$FANTASQUE_DEST"
|
||||||
|
log_success "FantasqueSansMono Nerd Font installed to $FANTASQUE_DEST"
|
||||||
|
else
|
||||||
|
log_warn "Failed to download FantasqueSansMono Nerd Font."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Victor Mono Font
|
||||||
|
log_substep "Downloading and installing Victor Mono Font..."
|
||||||
|
VICTOR_ARCHIVE="$TEMP_DIR/VictorMonoAll.zip"
|
||||||
|
VICTOR_DEST="$FONT_DIR/VictorMono"
|
||||||
|
if curl -fsSL "https://rubjo.github.io/victor-mono/VictorMonoAll.zip" -o "$VICTOR_ARCHIVE"; then
|
||||||
|
mkdir -p "$VICTOR_DEST"
|
||||||
|
unzip -o -q "$VICTOR_ARCHIVE" -d "$VICTOR_DEST"
|
||||||
|
log_success "Victor Mono Font installed to $VICTOR_DEST"
|
||||||
|
else
|
||||||
|
log_warn "Failed to download Victor Mono Font."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. Refresh Font Cache
|
||||||
|
log_substep "Updating font cache..."
|
||||||
|
if command_exists fc-cache; then
|
||||||
|
fc-cache -f
|
||||||
|
log_success "Font cache refreshed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Stage 05: Fonts installation completed successfully!"
|
||||||
Executable
+227
@@ -0,0 +1,227 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# stages/06-services.sh - System services activation (AppArmor, Docker, Libvirt, zram, Ollama)
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/utils.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/apt.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/btrfs.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/network.sh"
|
||||||
|
|
||||||
|
if [[ -f "$SCRIPT_DIR/config/setup.conf" ]]; then
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source "$SCRIPT_DIR/config/setup.conf"
|
||||||
|
fi
|
||||||
|
|
||||||
|
setup_err_trap
|
||||||
|
|
||||||
|
log_step "Running Stage 06: System Services Configuration & User Privileges"
|
||||||
|
|
||||||
|
require_root
|
||||||
|
|
||||||
|
TARGET_USER="$(get_target_user)"
|
||||||
|
|
||||||
|
# 1. AppArmor Service
|
||||||
|
log_substep "Enabling and starting AppArmor service..."
|
||||||
|
if command_exists systemctl; then
|
||||||
|
systemctl enable apparmor 2>/dev/null || true
|
||||||
|
systemctl start apparmor 2>/dev/null || true
|
||||||
|
log_success "AppArmor service enabled."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. zram-tools Configuration
|
||||||
|
log_substep "Configuring zram swap service..."
|
||||||
|
if command_exists systemctl; then
|
||||||
|
systemctl enable --now zramswap.service 2>/dev/null || true
|
||||||
|
log_success "zram service enabled."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Docker Service & Repository
|
||||||
|
log_substep "Setting up Docker service & repository..."
|
||||||
|
apt_ensure_keyrings_dir
|
||||||
|
apt_add_keyring_from_url "https://download.docker.com/linux/debian/gpg" "docker.asc"
|
||||||
|
|
||||||
|
DEBIAN_CODENAME="trixie"
|
||||||
|
if [[ -f /etc/os-release ]]; then
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source /etc/os-release
|
||||||
|
DEBIAN_CODENAME="${VERSION_CODENAME:-trixie}"
|
||||||
|
# Docker uses 'bookworm' or 'trixie' upstream repo suite for Debian testing/unstable if sid is not explicitly provided
|
||||||
|
if [[ "$DEBIAN_CODENAME" == "sid" ]]; then
|
||||||
|
DEBIAN_CODENAME="trixie"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
ARCH="$(dpkg --print-architecture)"
|
||||||
|
cat <<EOF > /etc/apt/sources.list.d/docker.sources
|
||||||
|
Types: deb
|
||||||
|
URIs: https://download.docker.com/linux/debian
|
||||||
|
Suites: $DEBIAN_CODENAME
|
||||||
|
Components: stable
|
||||||
|
Architectures: $ARCH
|
||||||
|
Signed-By: /etc/apt/keyrings/docker.asc
|
||||||
|
EOF
|
||||||
|
chmod 0644 /etc/apt/sources.list.d/docker.sources
|
||||||
|
|
||||||
|
apt_update
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||||
|
docker-ce \
|
||||||
|
docker-ce-cli \
|
||||||
|
containerd.io \
|
||||||
|
docker-buildx-plugin \
|
||||||
|
docker-compose-plugin 2>/dev/null || log_warn "Could not install all Docker packages."
|
||||||
|
|
||||||
|
if command_exists systemctl; then
|
||||||
|
systemctl enable --now docker.service 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. Libvirt / KVM Virtualization
|
||||||
|
log_substep "Configuring Virtualization (Libvirt / KVM)..."
|
||||||
|
HAS_VIRT=$(grep -Eoc '(vmx|svm)' /proc/cpuinfo 2>/dev/null || echo 0)
|
||||||
|
if [[ "$HAS_VIRT" -gt 0 ]]; then
|
||||||
|
log_info "Hardware virtualization support detected."
|
||||||
|
if command_exists systemctl; then
|
||||||
|
systemctl enable --now libvirtd.service 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if command_exists virsh; then
|
||||||
|
virsh net-start default 2>/dev/null || true
|
||||||
|
virsh net-autostart default 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
# Add target user to libvirt and kvm groups
|
||||||
|
groupadd -f libvirt
|
||||||
|
groupadd -f kvm
|
||||||
|
usermod -aG libvirt,kvm "$TARGET_USER"
|
||||||
|
log_success "Target user $TARGET_USER added to libvirt & kvm groups."
|
||||||
|
else
|
||||||
|
log_info "No hardware virtualization extensions (VT-x/AMD-V) detected in /proc/cpuinfo."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 5. Ollama AI Service & Configuration
|
||||||
|
log_substep "Configuring Ollama AI Service..."
|
||||||
|
OLLAMA_MODELS_DIR="$(prompt_ollama_models_path)"
|
||||||
|
log_info "Ollama models directory set to: $OLLAMA_MODELS_DIR"
|
||||||
|
mkdir -p "$OLLAMA_MODELS_DIR"
|
||||||
|
|
||||||
|
log_substep "Installing / Updating Ollama..."
|
||||||
|
if curl -fsSL https://ollama.com/install.sh | sh; then
|
||||||
|
log_success "Ollama installation completed."
|
||||||
|
else
|
||||||
|
log_warn "Ollama installation script returned a non-zero exit code."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure permissions on models directory for ollama user
|
||||||
|
if id ollama >/dev/null 2>&1; then
|
||||||
|
chown -R ollama:ollama "$OLLAMA_MODELS_DIR" 2>/dev/null || true
|
||||||
|
chmod -R 775 "$OLLAMA_MODELS_DIR" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Configure systemd drop-in override for Ollama service
|
||||||
|
log_substep "Configuring systemd override for Ollama service..."
|
||||||
|
mkdir -p /etc/systemd/system/ollama.service.d
|
||||||
|
cat <<EOF > /etc/systemd/system/ollama.service.d/override.conf
|
||||||
|
[Service]
|
||||||
|
Environment="OLLAMA_MODELS=$OLLAMA_MODELS_DIR"
|
||||||
|
Environment="OLLAMA_FLASH_ATTENTION=${OLLAMA_FLASH_ATTENTION:-1}"
|
||||||
|
Environment="OLLAMA_KV_CACHE_TYPE=${OLLAMA_KV_CACHE_TYPE:-q8_0}"
|
||||||
|
Environment="OLLAMA_KEEP_ALIVE=${OLLAMA_KEEP_ALIVE:-30m}"
|
||||||
|
EOF
|
||||||
|
chmod 0644 /etc/systemd/system/ollama.service.d/override.conf
|
||||||
|
|
||||||
|
if command_exists systemctl; then
|
||||||
|
systemctl daemon-reload 2>/dev/null || true
|
||||||
|
systemctl enable --now ollama.service 2>/dev/null || true
|
||||||
|
systemctl restart ollama.service 2>/dev/null || true
|
||||||
|
log_success "Ollama systemd service configured and enabled."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 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. Network Configuration & Services (Loopback, LAN, NetworkManager)
|
||||||
|
log_substep "Configuring and activating network interfaces (Loopback, all LAN interfaces, NetworkManager)..."
|
||||||
|
configure_network_all
|
||||||
|
|
||||||
|
# 8. User Group Memberships
|
||||||
|
log_substep "Updating user group memberships for $TARGET_USER..."
|
||||||
|
groupadd -f sudo
|
||||||
|
groupadd -f docker
|
||||||
|
groupadd -f netdev
|
||||||
|
groupadd -f ollama 2>/dev/null || true
|
||||||
|
usermod -aG sudo,docker,netdev "$TARGET_USER"
|
||||||
|
if getent group ollama >/dev/null 2>&1; then
|
||||||
|
usermod -aG ollama "$TARGET_USER" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
# "render" (GPU compute/Vulkan access) is created by Debian's udev sysusers
|
||||||
|
# config and is always present; "gamemode" only exists once the gamemode
|
||||||
|
# package is installed (stage 04), so it is added conditionally.
|
||||||
|
if getent group render >/dev/null 2>&1; then
|
||||||
|
usermod -aG render "$TARGET_USER" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if getent group gamemode >/dev/null 2>&1; then
|
||||||
|
usermod -aG gamemode "$TARGET_USER" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
log_success "Target user $TARGET_USER added to sudo, docker, netdev, ollama, render and gamemode groups."
|
||||||
|
|
||||||
|
# 9. GameMode Performance Daemon & CoreCtrl Configuration
|
||||||
|
log_substep "Deploying GameMode performance configuration..."
|
||||||
|
if [[ -f "$SCRIPT_DIR/data/etc/gamemode.ini" ]]; then
|
||||||
|
backup_file_or_dir "/etc/gamemode.ini"
|
||||||
|
cp "$SCRIPT_DIR/data/etc/gamemode.ini" /etc/gamemode.ini
|
||||||
|
chmod 0644 /etc/gamemode.ini
|
||||||
|
log_success "GameMode configuration deployed to /etc/gamemode.ini."
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_substep "Configuring passwordless CoreCtrl access for administrative users..."
|
||||||
|
if [[ -f "$SCRIPT_DIR/data/etc/polkit-1/rules.d/90-corectrl.rules" ]]; then
|
||||||
|
mkdir -p /etc/polkit-1/rules.d
|
||||||
|
cp "$SCRIPT_DIR/data/etc/polkit-1/rules.d/90-corectrl.rules" /etc/polkit-1/rules.d/90-corectrl.rules
|
||||||
|
chmod 0644 /etc/polkit-1/rules.d/90-corectrl.rules
|
||||||
|
log_success "CoreCtrl PolicyKit rule installed for the 'sudo' group."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 10. systemd-oomd Out-of-Memory Protection
|
||||||
|
log_substep "Enabling systemd-oomd out-of-memory protection..."
|
||||||
|
if command_exists systemctl; then
|
||||||
|
systemctl enable --now systemd-oomd.service 2>/dev/null || log_warn "Could not enable systemd-oomd.service."
|
||||||
|
log_success "systemd-oomd enabled (protects against freezes under heavy memory/swap pressure)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 11. Sched-ext (scx) Gaming Scheduler
|
||||||
|
log_substep "Configuring sched-ext (scx) gaming scheduler..."
|
||||||
|
apt_ensure_keyrings_dir
|
||||||
|
apt_add_keyring_from_url "https://dl.xanmod.org/archive.key" "xanmod-archive-keyring.gpg" || true
|
||||||
|
if [[ -f "$SCRIPT_DIR/data/apt/sources.list.d/xanmod.sources" ]]; then
|
||||||
|
apt_install_sources_file "$SCRIPT_DIR/data/apt/sources.list.d/xanmod.sources" "xanmod.sources"
|
||||||
|
fi
|
||||||
|
apt_update
|
||||||
|
if DEBIAN_FRONTEND=noninteractive apt-get install -y scx-scheds 2>/dev/null; then
|
||||||
|
if [[ -f /etc/default/scx ]]; then
|
||||||
|
# scx_lavd (Latency-criticality Aware Virtual Deadline) is purpose-built
|
||||||
|
# for gaming/interactive desktop workloads; --autopilot lets it balance
|
||||||
|
# performance vs. powersave automatically based on system load.
|
||||||
|
sed -i 's|^SCX_SCHEDULER=.*|SCX_SCHEDULER=scx_lavd|' /etc/default/scx
|
||||||
|
grep -q '^SCX_SCHEDULER=' /etc/default/scx || echo "SCX_SCHEDULER=scx_lavd" >> /etc/default/scx
|
||||||
|
sed -i "s|^#\?SCX_FLAGS=.*|SCX_FLAGS='--autopilot'|" /etc/default/scx
|
||||||
|
grep -q '^SCX_FLAGS=' /etc/default/scx || echo "SCX_FLAGS='--autopilot'" >> /etc/default/scx
|
||||||
|
fi
|
||||||
|
if command_exists systemctl; then
|
||||||
|
systemctl daemon-reload 2>/dev/null || true
|
||||||
|
systemctl enable --now scx.service 2>/dev/null || log_warn "scx.service could not be started - a reboot into a sched_ext-capable kernel (e.g. XanMod) may be required."
|
||||||
|
fi
|
||||||
|
log_success "sched-ext gaming scheduler (scx_lavd) configured and enabled."
|
||||||
|
else
|
||||||
|
log_warn "Could not install scx-scheds. Skipping sched-ext scheduler configuration."
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Stage 06: System services configuration completed successfully!"
|
||||||
Executable
+146
@@ -0,0 +1,146 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# stages/07-skel-and-user.sh - /etc/skel templates, Zsh setup, user environment sync & chsh
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/utils.sh"
|
||||||
|
|
||||||
|
setup_err_trap
|
||||||
|
|
||||||
|
log_step "Running Stage 07: User Environment, Skel Templates & Shell Configuration"
|
||||||
|
|
||||||
|
require_root
|
||||||
|
|
||||||
|
TARGET_USER="$(get_target_user)"
|
||||||
|
TARGET_HOME="$(get_target_home)"
|
||||||
|
|
||||||
|
log_info "Configuring userland environment for target user: $TARGET_USER ($TARGET_HOME)"
|
||||||
|
|
||||||
|
# 1. Deploy System Profile Scripts (/etc/profile.d)
|
||||||
|
log_substep "Deploying system profile scripts to /etc/profile.d/..."
|
||||||
|
mkdir -p /etc/profile.d
|
||||||
|
if [[ -d "$SCRIPT_DIR/data/etc/profile.d" ]]; then
|
||||||
|
for profile_script in "$SCRIPT_DIR"/data/etc/profile.d/*.sh; do
|
||||||
|
[[ -f "$profile_script" ]] || continue
|
||||||
|
cp "$profile_script" /etc/profile.d/
|
||||||
|
chmod 0755 "/etc/profile.d/$(basename "$profile_script")"
|
||||||
|
log_info "Installed profile script: $(basename "$profile_script")"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure Zsh profiles source /etc/profile
|
||||||
|
mkdir -p /etc/zsh
|
||||||
|
grep -q '^source /etc/profile' /etc/zsh/zprofile 2>/dev/null || echo 'source /etc/profile' >> /etc/zsh/zprofile
|
||||||
|
|
||||||
|
# 2. Provision /etc/skel for Future New Users
|
||||||
|
log_substep "Provisioning /etc/skel with default templates and configs..."
|
||||||
|
mkdir -p /etc/skel/Templates /etc/skel/.junie/skills
|
||||||
|
if [[ -d "$SCRIPT_DIR/data/skel/Templates" ]]; then
|
||||||
|
cp -r "$SCRIPT_DIR"/data/skel/Templates/* /etc/skel/Templates/ 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if [[ -f "$SCRIPT_DIR/data/skel/.zshrc" ]]; then
|
||||||
|
cp "$SCRIPT_DIR/data/skel/.zshrc" /etc/skel/.zshrc
|
||||||
|
fi
|
||||||
|
if [[ -d "$SCRIPT_DIR/data/skel/.config" ]]; then
|
||||||
|
mkdir -p /etc/skel/.config
|
||||||
|
cp -r "$SCRIPT_DIR"/data/skel/.config/* /etc/skel/.config/ 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Provision Oh-My-Zsh template in /etc/skel so newly created users receive it automatically
|
||||||
|
if command_exists git; then
|
||||||
|
if [[ ! -d /etc/skel/.oh-my-zsh ]]; then
|
||||||
|
log_info "Provisioning Oh-My-Zsh template in /etc/skel/.oh-my-zsh..."
|
||||||
|
git clone --depth=1 https://github.com/ohmyzsh/ohmyzsh.git /etc/skel/.oh-my-zsh 2>/dev/null || log_warn "Could not clone Oh-My-Zsh into /etc/skel."
|
||||||
|
fi
|
||||||
|
if [[ -d /etc/skel/.oh-my-zsh ]]; then
|
||||||
|
mkdir -p /etc/skel/.oh-my-zsh/custom/plugins
|
||||||
|
[[ -d /etc/skel/.oh-my-zsh/custom/plugins/zsh-autosuggestions ]] || \
|
||||||
|
git clone --depth=1 https://github.com/zsh-users/zsh-autosuggestions /etc/skel/.oh-my-zsh/custom/plugins/zsh-autosuggestions 2>/dev/null || true
|
||||||
|
[[ -d /etc/skel/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting ]] || \
|
||||||
|
git clone --depth=1 https://github.com/zsh-users/zsh-syntax-highlighting /etc/skel/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
chown -R root:root /etc/skel
|
||||||
|
|
||||||
|
# 3. Synchronize User Environment for Target User
|
||||||
|
log_substep "Synchronizing Dotfiles & Templates into $TARGET_HOME..."
|
||||||
|
mkdir -p "$TARGET_HOME/Templates" "$TARGET_HOME/.config" "$TARGET_HOME/.junie/skills"
|
||||||
|
|
||||||
|
# Backup existing .zshrc if present
|
||||||
|
if [[ -f "$TARGET_HOME/.zshrc" ]]; then
|
||||||
|
backup_file_or_dir "$TARGET_HOME/.zshrc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "$SCRIPT_DIR/data/skel/.zshrc" ]]; then
|
||||||
|
cp "$SCRIPT_DIR/data/skel/.zshrc" "$TARGET_HOME/.zshrc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -d "$SCRIPT_DIR/data/skel/Templates" ]]; then
|
||||||
|
cp -r "$SCRIPT_DIR"/data/skel/Templates/* "$TARGET_HOME/Templates/" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Backup existing Hyprland config if present
|
||||||
|
if [[ -f "$TARGET_HOME/.config/hypr/hyprland.conf" ]]; then
|
||||||
|
backup_file_or_dir "$TARGET_HOME/.config/hypr/hyprland.conf"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -d "$SCRIPT_DIR/data/skel/.config" ]]; then
|
||||||
|
cp -r "$SCRIPT_DIR"/data/skel/.config/* "$TARGET_HOME/.config/" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. Install Oh-My-Zsh for Target User (if not already installed)
|
||||||
|
if [[ ! -d "$TARGET_HOME/.oh-my-zsh" ]]; then
|
||||||
|
log_substep "Installing Oh-My-Zsh repository for $TARGET_USER..."
|
||||||
|
if [[ -d /etc/skel/.oh-my-zsh ]]; then
|
||||||
|
cp -r /etc/skel/.oh-my-zsh "$TARGET_HOME/.oh-my-zsh"
|
||||||
|
elif command_exists git; then
|
||||||
|
run_as_target_user git clone --depth=1 https://github.com/ohmyzsh/ohmyzsh.git "$TARGET_HOME/.oh-my-zsh" 2>/dev/null || log_warn "Could not clone Oh-My-Zsh repository."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -d "$TARGET_HOME/.oh-my-zsh" ]]; then
|
||||||
|
mkdir -p "$TARGET_HOME/.oh-my-zsh/custom/plugins"
|
||||||
|
if [[ ! -d "$TARGET_HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions" ]] && command_exists git; then
|
||||||
|
run_as_target_user git clone --depth=1 https://github.com/zsh-users/zsh-autosuggestions "$TARGET_HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if [[ ! -d "$TARGET_HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting" ]] && command_exists git; then
|
||||||
|
run_as_target_user git clone --depth=1 https://github.com/zsh-users/zsh-syntax-highlighting "$TARGET_HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fix ownership
|
||||||
|
chown -R "$TARGET_USER:$TARGET_USER" "$TARGET_HOME/.zshrc" "$TARGET_HOME/Templates" "$TARGET_HOME/.config" "$TARGET_HOME/.junie"
|
||||||
|
if [[ -d "$TARGET_HOME/.oh-my-zsh" ]]; then
|
||||||
|
chown -R "$TARGET_USER:$TARGET_USER" "$TARGET_HOME/.oh-my-zsh"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 5. Set Default Shell to Zsh using standard chsh
|
||||||
|
log_substep "Setting default shell to Zsh for $TARGET_USER..."
|
||||||
|
ZSH_BIN="$(command -v zsh || echo "/usr/bin/zsh")"
|
||||||
|
if [[ -x "$ZSH_BIN" ]]; then
|
||||||
|
# Ensure zsh is listed in /etc/shells
|
||||||
|
grep -q "^$ZSH_BIN$" /etc/shells 2>/dev/null || echo "$ZSH_BIN" >> /etc/shells
|
||||||
|
|
||||||
|
CURRENT_SHELL="$(getent passwd "$TARGET_USER" | cut -d: -f7)"
|
||||||
|
if [[ "$CURRENT_SHELL" != "$ZSH_BIN" ]]; then
|
||||||
|
chsh -s "$ZSH_BIN" "$TARGET_USER"
|
||||||
|
log_success "Default shell for $TARGET_USER set to $ZSH_BIN"
|
||||||
|
else
|
||||||
|
log_info "Default shell for $TARGET_USER is already $ZSH_BIN"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Configure default shell for newly created users
|
||||||
|
if command_exists useradd; then
|
||||||
|
useradd -D -s "$ZSH_BIN" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if [[ -f /etc/adduser.conf ]]; then
|
||||||
|
sed -i -E 's|^#?\s*DSHELL=.*|DSHELL='"$ZSH_BIN"'|' /etc/adduser.conf 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_warn "Zsh binary not found at $ZSH_BIN. Skipping chsh."
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Stage 07: Skel & userland configuration completed successfully!"
|
||||||
Executable
+41
@@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# stages/08-flatpaks.sh - Flathub repository setup & Flatpak package installation
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/utils.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/apt.sh"
|
||||||
|
|
||||||
|
setup_err_trap
|
||||||
|
|
||||||
|
log_step "Running Stage 08: Flatpaks & Flathub Repository Setup"
|
||||||
|
|
||||||
|
require_root
|
||||||
|
|
||||||
|
TARGET_USER="$(get_target_user)"
|
||||||
|
|
||||||
|
# 1. Flathub Repository Setup
|
||||||
|
log_substep "Configuring Flathub remote repository..."
|
||||||
|
if command_exists flatpak; then
|
||||||
|
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo || log_warn "Could not add Flathub remote."
|
||||||
|
log_success "Flathub repository enabled."
|
||||||
|
else
|
||||||
|
log_warn "flatpak command not found. Skipping Flatpak installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Install Flatpaks from config/packages/06-flatpaks.list
|
||||||
|
FLATPAK_LIST="$SCRIPT_DIR/config/packages/06-flatpaks.list"
|
||||||
|
if command_exists flatpak && [[ -f "$FLATPAK_LIST" ]]; then
|
||||||
|
log_substep "Installing Flatpak applications from $FLATPAK_LIST..."
|
||||||
|
while IFS= read -r app_id || [[ -n "$app_id" ]]; do
|
||||||
|
app_id="$(echo "$app_id" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
|
||||||
|
[[ -z "$app_id" || "$app_id" =~ ^# ]] && continue
|
||||||
|
log_info "Installing Flatpak: $app_id"
|
||||||
|
flatpak install -y flathub "$app_id" 2>/dev/null || log_warn "Failed to install Flatpak: $app_id"
|
||||||
|
done < "$FLATPAK_LIST"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Stage 08: Flatpaks and Flathub repository setup completed successfully!"
|
||||||
Executable
+76
@@ -0,0 +1,76 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# stages/09-apps.sh - Spotify, Waydroid, OpenDeck, custom desktop apps & MIME associations
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/utils.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/apt.sh"
|
||||||
|
|
||||||
|
setup_err_trap
|
||||||
|
|
||||||
|
log_step "Running Stage 09: Standalone Applications, Integrations & MIME Associations"
|
||||||
|
|
||||||
|
require_root
|
||||||
|
|
||||||
|
TARGET_USER="$(get_target_user)"
|
||||||
|
|
||||||
|
# 1. Spotify Client Installation (Debian APT repo)
|
||||||
|
log_substep "Configuring Spotify repository and client..."
|
||||||
|
if apt_add_keyring_from_url "https://download.spotify.com/debian/pubkey_5384CE82BA52C83A.asc" "spotify.gpg"; then
|
||||||
|
cat <<EOF > /etc/apt/sources.list.d/spotify.sources
|
||||||
|
Types: deb
|
||||||
|
URIs: http://repository.spotify.com
|
||||||
|
Suites: stable
|
||||||
|
Components: non-free
|
||||||
|
Signed-By: /etc/apt/keyrings/spotify.gpg
|
||||||
|
EOF
|
||||||
|
chmod 0644 /etc/apt/sources.list.d/spotify.sources
|
||||||
|
apt_update
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y spotify-client 2>/dev/null || log_warn "Could not install spotify-client via APT."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Deploy Custom Desktop Applications
|
||||||
|
if [[ -d "$SCRIPT_DIR/data/usr/share/applications" ]]; then
|
||||||
|
log_substep "Deploying custom desktop entries..."
|
||||||
|
mkdir -p /usr/share/applications
|
||||||
|
cp -r "$SCRIPT_DIR"/data/usr/share/applications/* /usr/share/applications/ 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Waydroid Container Setup
|
||||||
|
log_substep "Checking Waydroid installation..."
|
||||||
|
if ! command_exists waydroid; then
|
||||||
|
log_info "Installing Waydroid repository and package..."
|
||||||
|
if curl -fsSL https://repo.waydro.id | bash 2>/dev/null; then
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y waydroid 2>/dev/null || log_warn "Failed to install waydroid package."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if command_exists systemctl && command_exists waydroid; then
|
||||||
|
systemctl enable --now waydroid-container 2>/dev/null || true
|
||||||
|
log_success "Waydroid container service enabled."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. OpenDeck Installation
|
||||||
|
log_substep "Installing / Updating OpenDeck..."
|
||||||
|
if curl -sSL https://raw.githubusercontent.com/nekename/OpenDeck/main/install_opendeck.sh | bash; then
|
||||||
|
log_success "OpenDeck installation completed."
|
||||||
|
else
|
||||||
|
log_warn "OpenDeck installation script returned a non-zero exit code."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 5. Set Default Desktop MIME Types for Target User
|
||||||
|
log_substep "Setting default XDG MIME handlers for $TARGET_USER..."
|
||||||
|
run_as_target_user xdg-mime default org.gnome.Loupe.desktop image/jpeg 2>/dev/null || true
|
||||||
|
run_as_target_user xdg-mime default org.gnome.Loupe.desktop image/png 2>/dev/null || true
|
||||||
|
run_as_target_user xdg-mime default io.bassi.Amberol.desktop audio/mpeg 2>/dev/null || true
|
||||||
|
run_as_target_user xdg-mime default io.bassi.Amberol.desktop audio/x-wav 2>/dev/null || true
|
||||||
|
run_as_target_user xdg-mime default io.bassi.Amberol.desktop audio/flac 2>/dev/null || true
|
||||||
|
run_as_target_user xdg-mime default io.bassi.Amberol.desktop audio/ogg 2>/dev/null || true
|
||||||
|
run_as_target_user xdg-mime default org.gnome.TextEditor.desktop text/plain 2>/dev/null || true
|
||||||
|
run_as_target_user xdg-mime default org.gnome.TextEditor.desktop text/x-log 2>/dev/null || true
|
||||||
|
run_as_target_user xdg-mime default org.gnome.TextEditor.desktop text/markdown 2>/dev/null || true
|
||||||
|
run_as_target_user xdg-mime default eu.betterbird.Betterbird.desktop x-scheme-handler/mailto 2>/dev/null || true
|
||||||
|
|
||||||
|
log_success "Stage 09: Standalone Applications, Integrations and MIME associations completed successfully!"
|
||||||
Executable
+100
@@ -0,0 +1,100 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# stages/10-hyprland.sh - LinuxBeginnings Debian-Hyprland auto-installer & desktop setup
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
# shellcheck source=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
|
||||||
|
|
||||||
|
log_step "Running Stage 10: Hyprland Desktop (LinuxBeginnings Debian-Hyprland Installer)"
|
||||||
|
|
||||||
|
require_root
|
||||||
|
|
||||||
|
TARGET_USER="$(get_target_user)"
|
||||||
|
TARGET_HOME="$(get_target_home)"
|
||||||
|
|
||||||
|
log_info "Configuring Hyprland Desktop for target user: $TARGET_USER ($TARGET_HOME)"
|
||||||
|
|
||||||
|
# 1. Execute 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
|
||||||
|
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
|
||||||
|
log_success "LinuxBeginnings Debian-Hyprland installation completed successfully."
|
||||||
|
else
|
||||||
|
log_warn "LinuxBeginnings installer exited with non-zero status. Check logs if needed."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_error "curl is required to download Debian-Hyprland installer."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Install first-login-setup system-wide (shared by all users, not copied
|
||||||
|
# into individual home directories) and enable its systemd --user service
|
||||||
|
# globally, exactly as 'systemctl --global enable' would apply to every user.
|
||||||
|
log_substep "Installing first-login-setup system-wide to /usr/local/lib/first-login-setup..."
|
||||||
|
if [[ -d "$SCRIPT_DIR/data/usr/local/lib/first-login-setup" ]]; then
|
||||||
|
mkdir -p /usr/local/lib/first-login-setup
|
||||||
|
cp "$SCRIPT_DIR"/data/usr/local/lib/first-login-setup/*.sh /usr/local/lib/first-login-setup/
|
||||||
|
chmod 0755 /usr/local/lib/first-login-setup/*.sh
|
||||||
|
chown -R root:root /usr/local/lib/first-login-setup
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "$SCRIPT_DIR/data/etc/systemd/user/first-login-setup.service" ]]; then
|
||||||
|
mkdir -p /etc/systemd/user
|
||||||
|
cp "$SCRIPT_DIR/data/etc/systemd/user/first-login-setup.service" /etc/systemd/user/first-login-setup.service
|
||||||
|
if command_exists systemctl; then
|
||||||
|
systemctl --global enable first-login-setup.service 2>/dev/null || log_warn "Could not globally enable first-login-setup.service via systemctl."
|
||||||
|
else
|
||||||
|
# Fallback: replicate what 'systemctl --global enable' would do.
|
||||||
|
mkdir -p /etc/systemd/user/default.target.wants
|
||||||
|
ln -sfn "../first-login-setup.service" /etc/systemd/user/default.target.wants/first-login-setup.service
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Ensure rudimentary Hyprland config is deployed to /etc/skel
|
||||||
|
log_substep "Deploying rudimentary Hyprland configuration to /etc/skel..."
|
||||||
|
mkdir -p /etc/skel/.config/hypr
|
||||||
|
if [[ -f "$SCRIPT_DIR/data/skel/.config/hypr/hyprland.conf" ]]; then
|
||||||
|
cp "$SCRIPT_DIR/data/skel/.config/hypr/hyprland.conf" /etc/skel/.config/hypr/hyprland.conf
|
||||||
|
fi
|
||||||
|
chown -R root:root /etc/skel/.config
|
||||||
|
|
||||||
|
# 4. Deploy rudimentary Hyprland config to target user
|
||||||
|
log_substep "Deploying rudimentary Hyprland configuration to $TARGET_HOME/.config/hypr..."
|
||||||
|
mkdir -p "$TARGET_HOME/.config/hypr"
|
||||||
|
|
||||||
|
if [[ -f "$TARGET_HOME/.config/hypr/hyprland.conf" ]]; then
|
||||||
|
backup_file_or_dir "$TARGET_HOME/.config/hypr/hyprland.conf"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "$SCRIPT_DIR/data/skel/.config/hypr/hyprland.conf" ]]; then
|
||||||
|
cp "$SCRIPT_DIR/data/skel/.config/hypr/hyprland.conf" "$TARGET_HOME/.config/hypr/hyprland.conf"
|
||||||
|
fi
|
||||||
|
|
||||||
|
chown -R "$TARGET_USER:$TARGET_USER" "$TARGET_HOME/.config/hypr"
|
||||||
|
|
||||||
|
log_success "Rudimentary Hyprland configuration deployed:"
|
||||||
|
log_info " - SUPER+ENTER: Kitty Terminal"
|
||||||
|
log_info " - SUPER+Q: Close Active Window"
|
||||||
|
log_info " - Ersteinrichtung (Dotfiles, Oh-My-Zsh, JetBrains Junie, Claude Code & Skills) startet automatisch beim"
|
||||||
|
log_info " nächsten Login jedes Benutzers über den systemweiten systemd --user Service"
|
||||||
|
log_info " 'first-login-setup.service' (unabhängig vom gewählten Compositor, NICHT über"
|
||||||
|
log_info " hyprland.conf). Läuft nur beim ersten Login bzw. nach 'Später' erneut."
|
||||||
|
log_info " Manueller Aufruf jederzeit möglich mit: /usr/local/lib/first-login-setup/first-login-setup.sh --force"
|
||||||
|
|
||||||
|
create_timeshift_snapshot "Stage 10: Post-Hyprland desktop setup" "O"
|
||||||
|
|
||||||
|
log_success "Stage 10: Hyprland Desktop installation & configuration completed successfully!"
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# stages/11-linutil.sh - Linutil (Chris Titus Tech System Toolbox), final install step
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
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
|
||||||
|
|
||||||
|
log_step "Running Stage 11: Linutil (Chris Titus Tech System Toolbox)"
|
||||||
|
|
||||||
|
require_root
|
||||||
|
|
||||||
|
TARGET_USER="$(get_target_user)"
|
||||||
|
|
||||||
|
log_info "Launching Linutil as target user '$TARGET_USER'..."
|
||||||
|
create_timeshift_snapshot "Stage 11: Pre-Linutil" "B"
|
||||||
|
|
||||||
|
if command_exists curl; then
|
||||||
|
if has_tty; then
|
||||||
|
# Linutil is an interactive TUI - run it as the target user (it
|
||||||
|
# elevates via sudo itself where needed), attached to the controlling
|
||||||
|
# terminal so its menu can actually receive keystrokes.
|
||||||
|
if run_as_target_user bash -c 'sh <(curl -fsSL https://christitus.com/linux)' < /dev/tty > /dev/tty; then
|
||||||
|
log_success "Linutil finished."
|
||||||
|
else
|
||||||
|
log_warn "Linutil exited with a non-zero status. Check logs if needed."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_warn "No controlling terminal available - Linutil's interactive menu cannot be displayed. Skipping."
|
||||||
|
log_info "Run it manually later with: curl -fsSL https://christitus.com/linux | sh"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_error "curl is required to run Linutil."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
create_timeshift_snapshot "Stage 11: Post-Linutil" "O"
|
||||||
|
|
||||||
|
log_success "Stage 11: Linutil completed successfully!"
|
||||||
Reference in New Issue
Block a user