Compare commits
22
Commits
f093cce7dc
...
dev
@@ -37,26 +37,29 @@ Dieses Repository ist ein modulares, automatisiertes Setup- und Konfigurations-F
|
|||||||
│ ├── 04-virtualization.list # QEMU, KVM, Libvirt, Bridge-Tools
|
│ ├── 04-virtualization.list # QEMU, KVM, Libvirt, Bridge-Tools
|
||||||
│ ├── 05-multimedia.list # VLC, Amberol, GIMP, Loupe, LibreOffice
|
│ ├── 05-multimedia.list # VLC, Amberol, GIMP, Loupe, LibreOffice
|
||||||
│ └── 06-flatpaks.list # Flathub App-IDs
|
│ └── 06-flatpaks.list # Flathub App-IDs
|
||||||
├── stages/ # Sequenziell und modular ausführbare Phasen (00 bis 10)
|
├── stages/ # Sequenziell und modular ausführbare Phasen (00 bis 11)
|
||||||
│ ├── 00-preflight.sh # Systemvoraussetzungen, Root/User-Erkennung, Internetverbindung
|
│ ├── 00-preflight.sh # Systemvoraussetzungen, Root/User-Erkennung, Internetverbindung
|
||||||
│ ├── 01-apt-unstable.sh # Deb822-Quellen, Keyrings, Pinning, apt-listbugs, dist-upgrade
|
│ ├── 01-apt-unstable.sh # Deb822-Quellen, Keyrings, Pinning, apt-listbugs, dist-upgrade
|
||||||
│ ├── 02-kernel-hardware.sh # XanMod-Kernel, CPU P-State, Microcode, auto-cpufreq
|
│ ├── 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
|
│ ├── 03-bootloader.sh # GRUB Kernel-Parameter, Vimix-Theme, Plymouth Solar
|
||||||
│ ├── 04-packages.sh # Installation der thematischen Paketlisten
|
│ ├── 04-packages.sh # Installation der thematischen Paketlisten
|
||||||
│ ├── 05-fonts.sh # Download & Installation von Coding- & Nerd-Fonts
|
│ ├── 05-fonts.sh # Download & Installation von Coding- & Nerd-Fonts
|
||||||
│ ├── 06-services.sh # Aktivierung von AppArmor, Docker, Libvirt, zram, Ollama
|
│ ├── 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
|
│ ├── 07-skel-and-user.sh # /etc/skel Vorlagen, ZSH-Shell-Setzen, Dotfiles-Sync
|
||||||
│ ├── 08-flatpaks.sh # Flathub Setup, Flatpak-Paketlisten-Installation
|
│ ├── 08-flatpaks.sh # Flathub Setup, Flatpak-Paketlisten-Installation
|
||||||
│ ├── 09-apps.sh # Spotify, Waydroid, OpenDeck, Desktop-Starter, MIME-Types
|
│ ├── 09-apps.sh # Spotify, Waydroid, OpenDeck, Desktop-Starter, MIME-Types
|
||||||
│ └── 10-hyprland.sh # LinuxBeginnings Debian-Hyprland Auto-Installer & Desktop-Setup
|
│ ├── 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
|
├── data/ # Statische Konfigurationsdateien & Vorlagen
|
||||||
│ ├── apt/
|
│ ├── apt/
|
||||||
│ │ ├── sources.list.d/ # Deb822-Quellen (debian-unstable.sources, xanmod.sources)
|
│ │ ├── sources.list.d/ # Deb822-Quellen (debian-unstable.sources, xanmod.sources)
|
||||||
│ │ └── preferences.d/ # Pinning-Konfigurationen
|
│ │ └── preferences.d/ # Pinning-Konfigurationen
|
||||||
│ ├── etc/
|
│ ├── etc/
|
||||||
│ │ ├── environment.d/ # Globale Umgebungsvariablen (Raytracing, Upscaling)
|
│ │ ├── 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)
|
│ │ └── profile.d/ # Globale Profil-Skripte (z. B. XDG User Dirs Update)
|
||||||
│ ├── skel/ # Vorlagen für /etc/skel (.zshrc, Dokumentvorlagen)
|
│ ├── skel/ # Vorlagen für /etc/skel (.zshrc, .config/MangoHud, .config/vkBasalt, Dokumentvorlagen)
|
||||||
│ └── usr/share/applications/ # Desktop-Starter-Dateien
|
│ └── usr/share/applications/ # Desktop-Starter-Dateien
|
||||||
├── README.md # Projektdokumentation für Endanwender
|
├── README.md # Projektdokumentation für Endanwender
|
||||||
└── LICENSE # MIT-Lizenz
|
└── LICENSE # MIT-Lizenz
|
||||||
@@ -90,6 +93,7 @@ Dieses Repository ist ein modulares, automatisiertes Setup- und Konfigurations-F
|
|||||||
source "$SCRIPT_DIR/../lib/utils.sh"
|
source "$SCRIPT_DIR/../lib/utils.sh"
|
||||||
setup_err_trap
|
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:
|
- Verwende für temporäre Verzeichnisse immer `mktemp -d` und stelle sicher, dass am Skriptende oder bei Fehlern aufgeräumt wird:
|
||||||
```bash
|
```bash
|
||||||
TMP_DIR="$(mktemp -d)"
|
TMP_DIR="$(mktemp -d)"
|
||||||
@@ -122,6 +126,8 @@ Dieses Repository ist ein modulares, automatisiertes Setup- und Konfigurations-F
|
|||||||
- `log_error "Nachricht"`: Rote Fehlermeldung (`[ERROR]`).
|
- `log_error "Nachricht"`: Rote Fehlermeldung (`[ERROR]`).
|
||||||
- `log_step "Titel"`: Hervorgehobener Abschnitts-Header.
|
- `log_step "Titel"`: Hervorgehobener Abschnitts-Header.
|
||||||
- `log_substep "Titel"`: Eingerückter Unterabschnitt.
|
- `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.
|
- `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.
|
- `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_target_user`: Erkennt `$SUDO_USER` oder fragt interaktiv nach dem Zielbenutzer.
|
||||||
@@ -156,7 +162,11 @@ Dieses Repository ist ein modulares, automatisiertes Setup- und Konfigurations-F
|
|||||||
- `configure_network_all`: Zentraler Orchestrator zur vollständigen Netzwerkeinrichtung.
|
- `configure_network_all`: Zentraler Orchestrator zur vollständigen Netzwerkeinrichtung.
|
||||||
|
|
||||||
### `lib/tui.sh`
|
### `lib/tui.sh`
|
||||||
- `tui_show_stage_selection`: Zeigt ein `whiptail` Checklist-Menü zur interaktiven Auswahl der auszuführenden Phasen.
|
- `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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ Das Setup kann direkt ohne vorheriges manuelles Klonen via `curl` gestartet werd
|
|||||||
# Interaktiver Modus (whiptail Menü)
|
# Interaktiver Modus (whiptail Menü)
|
||||||
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
|
||||||
|
|
||||||
# Vollautomatischer Modus (alle Stages 00 bis 10)
|
# Vollautomatischer Modus (alle Stages 00 bis 11)
|
||||||
curl -fsSL https://gitea.creative-dragonslayer.de/Scripts/Setup/raw/branch/main/install.sh | bash -s -- --all
|
curl -fsSL https://gitea.creative-dragonslayer.de/Scripts/Setup/raw/branch/main/install.sh | bash -s -- --all
|
||||||
|
|
||||||
# Gezielte Modulauswahl (z. B. Paketlisten und Schriften)
|
# Gezielte Modulauswahl (z. B. Paketlisten und Schriften)
|
||||||
@@ -23,29 +23,31 @@ curl -fsSL https://gitea.creative-dragonslayer.de/Scripts/Setup/raw/branch/main/
|
|||||||
### 2. Manuelles Klonen & Starten
|
### 2. Manuelles Klonen & Starten
|
||||||
Alternativ kann das Repository manuell geklont und ausgeführt werden:
|
Alternativ kann das Repository manuell geklont und ausgeführt werden:
|
||||||
```bash
|
```bash
|
||||||
git clone https://gitea.creative-dragonslayer.de/Scripts/Setup.git && cd Setup && sudo ./setup.sh
|
git clone https://gitea.creative-dragonslayer.de/Scripts/Setup.git && cd Setup && ./setup.sh
|
||||||
```
|
```
|
||||||
Oder direkt vollautomatisch alle Stages (`00` bis `10`) ausführen:
|
Oder direkt vollautomatisch alle Stages (`00` bis `10`) ausführen:
|
||||||
```bash
|
```bash
|
||||||
git clone https://gitea.creative-dragonslayer.de/Scripts/Setup.git && cd Setup && sudo ./setup.sh --all
|
git clone https://gitea.creative-dragonslayer.de/Scripts/Setup.git && cd Setup && ./setup.sh --all
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> `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.
|
||||||
|
|
||||||
### 3. Interaktiver TUI-Modus
|
### 3. Interaktiver TUI-Modus
|
||||||
Starte das grafische Terminal-Menü (`whiptail`) zur flexiblen Auswahl einzelner Module:
|
Starte das grafische Terminal-Menü (`whiptail`) zur flexiblen Auswahl einzelner Module:
|
||||||
```bash
|
```bash
|
||||||
sudo ./setup.sh
|
./setup.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. Vollautomatischer CLI-Modus
|
### 4. Vollautomatischer CLI-Modus
|
||||||
Führe alle Phasen (`00` bis `10`) ohne Interaktion aus:
|
Führe alle Phasen (`00` bis `10`) ohne Interaktion aus:
|
||||||
```bash
|
```bash
|
||||||
sudo ./setup.sh --all
|
./setup.sh --all
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5. Gezielte Modulauswahl
|
### 5. Gezielte Modulauswahl
|
||||||
Installiere nur bestimmte Phasen (z. B. nur Paketlisten und Schriften):
|
Installiere nur bestimmte Phasen (z. B. nur Paketlisten und Schriften):
|
||||||
```bash
|
```bash
|
||||||
sudo ./setup.sh --stages 04,05
|
./setup.sh --stages 04,05
|
||||||
```
|
```
|
||||||
|
|
||||||
### 6. Simulationslauf (Dry-Run)
|
### 6. Simulationslauf (Dry-Run)
|
||||||
@@ -100,15 +102,18 @@ sudo ./setup.sh --stages 04,05
|
|||||||
│ ├── 07-skel-and-user.sh # /etc/skel Vorlagen, ZSH-Shell-Setzen, Dotfiles-Sync
|
│ ├── 07-skel-and-user.sh # /etc/skel Vorlagen, ZSH-Shell-Setzen, Dotfiles-Sync
|
||||||
│ ├── 08-flatpaks.sh # Flathub Setup, Flatpak-Paketlisten-Installation
|
│ ├── 08-flatpaks.sh # Flathub Setup, Flatpak-Paketlisten-Installation
|
||||||
│ ├── 09-apps.sh # Spotify, Waydroid, OpenDeck, Desktop-Starter, MIME-Types
|
│ ├── 09-apps.sh # Spotify, Waydroid, OpenDeck, Desktop-Starter, MIME-Types
|
||||||
│ └── 10-hyprland.sh # LinuxBeginnings Debian-Hyprland Auto-Installer & Dotfiles
|
│ ├── 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
|
└── data/ # Statische Konfigurationsdateien & Vorlagen
|
||||||
├── apt/
|
├── apt/
|
||||||
│ ├── sources.list.d/ # debian-unstable.sources, xanmod.sources
|
│ ├── sources.list.d/ # debian-unstable.sources, xanmod.sources
|
||||||
│ └── preferences.d/ # Pinning-Konfigurationen (unstable, xanmod)
|
│ └── preferences.d/ # Pinning-Konfigurationen (unstable, xanmod)
|
||||||
├── etc/
|
├── etc/
|
||||||
│ ├── environment.d/ # 99-raytracing.conf, 99-upscaling.conf
|
│ ├── 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
|
│ └── profile.d/ # Bereinigte System-Profile-Skripte
|
||||||
├── skel/ # .zshrc, .config/hypr, Templates für Dokumente & Skripte
|
├── skel/ # .zshrc, .config/hypr, .config/MangoHud, .config/vkBasalt, Templates für Dokumente & Skripte
|
||||||
└── usr/share/applications/ # Desktop-Dateien (z. B. Spotify)
|
└── usr/share/applications/ # Desktop-Dateien (z. B. Spotify)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -141,6 +146,23 @@ sudo ./setup.sh --stages 04,05
|
|||||||
|
|
||||||
## 🎮 Gaming & Performance-Optimierungen
|
## 🎮 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
|
### CPU P-State & Kernel-Parameter
|
||||||
In `stages/03-bootloader.sh` wird der CPU-Hersteller automatisch ermittelt und in `/etc/default/grub` eingetragen:
|
In `stages/03-bootloader.sh` wird der CPU-Hersteller automatisch ermittelt und in `/etc/default/grub` eingetragen:
|
||||||
- **AMD CPUs**: `amd_pstate=active`
|
- **AMD CPUs**: `amd_pstate=active`
|
||||||
@@ -154,6 +176,9 @@ gamemoderun gamescope -W 2560 -H 1440 --force-grab-cursor --hdr-enabled --adapti
|
|||||||
|
|
||||||
# Raytracing für DX12 Titel (RADV / Mesa)
|
# 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%
|
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%
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ ripgrep
|
|||||||
rpi-imager
|
rpi-imager
|
||||||
shellcheck
|
shellcheck
|
||||||
sl
|
sl
|
||||||
|
systemd-oomd
|
||||||
systemd-timesyncd
|
systemd-timesyncd
|
||||||
tar
|
tar
|
||||||
tealdeer
|
tealdeer
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
# config/packages/03-gaming.list - Gaming tools, Vulkan drivers, Wine & Emulation
|
# config/packages/03-gaming.list - Gaming tools, Vulkan drivers, Wine & Emulation
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
|
|
||||||
|
corectrl
|
||||||
gameconqueror
|
gameconqueror
|
||||||
gamemode
|
gamemode
|
||||||
gamescope
|
gamescope
|
||||||
@@ -17,6 +18,8 @@ protontricks
|
|||||||
radeontop
|
radeontop
|
||||||
steam-installer
|
steam-installer
|
||||||
steam-devices
|
steam-devices
|
||||||
|
vkbasalt
|
||||||
|
vkbasalt:i386
|
||||||
vulkan-tools
|
vulkan-tools
|
||||||
wine
|
wine
|
||||||
winetricks
|
winetricks
|
||||||
|
|||||||
@@ -23,7 +23,17 @@ GRUB_TIMEOUT_STYLE="menu"
|
|||||||
# Shell & Userland
|
# Shell & Userland
|
||||||
DEFAULT_SHELL="zsh"
|
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
|
# 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_MODELS_DEFAULT="/mnt/Data/Software/ollama/models"
|
||||||
OLLAMA_FLASH_ATTENTION="1"
|
OLLAMA_FLASH_ATTENTION="1"
|
||||||
OLLAMA_KV_CACHE_TYPE="q8_0"
|
OLLAMA_KV_CACHE_TYPE="q8_0"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
Types: deb
|
Types: deb
|
||||||
URIs: http://deb.xanmod.org
|
URIs: http://deb.xanmod.org
|
||||||
Suites: releases
|
Suites: sid
|
||||||
Components: main
|
Components: main
|
||||||
Signed-By: /etc/apt/keyrings/xanmod-archive-keyring.gpg
|
Signed-By: /etc/apt/keyrings/xanmod-archive-keyring.gpg
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -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
|
||||||
@@ -90,8 +90,11 @@ input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Autostart nach der Anmeldung
|
# Ersteinrichtung
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Fragt nach der ersten Anmeldung nach der gewünschten Dotfiles-Konfiguration
|
# Dotfiles-Auswahl, Oh-My-Zsh, JetBrains Junie CLI & AwesomeJunieSkills starten
|
||||||
# (LinuxBeginnings Hyprland-Dots oder eigenes Git-Repository für ~/.config)
|
# automatisch beim Login über den systemweiten systemd --user Service
|
||||||
exec-once = ~/.config/hypr/scripts/install-dots-prompt.sh
|
# '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
|
||||||
|
|||||||
@@ -1,357 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# ==============================================================================
|
|
||||||
# ~/.config/hypr/scripts/install-dots-prompt.sh
|
|
||||||
# Ersteinrichtung beim ersten Login:
|
|
||||||
# 1) Desktop-Konfiguration (LinuxBeginnings Hyprland-Dots oder eigenes Git-Repo)
|
|
||||||
# 2) Installation der JetBrains Junie CLI & AwesomeJunieSkills
|
|
||||||
# ==============================================================================
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
MARKER_FILE="$HOME/.config/hypr/.dots_prompted"
|
|
||||||
FORCE=0
|
|
||||||
|
|
||||||
for arg in "$@"; do
|
|
||||||
case "$arg" in
|
|
||||||
-f|--force)
|
|
||||||
FORCE=1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
# Wenn bereits abgefragt und kein --force übergeben wurde, beenden
|
|
||||||
if [[ "$FORCE" -eq 0 && -f "$MARKER_FILE" ]]; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Wenn nicht in einem interaktiven Terminal ausgeführt (z. B. via Hyprland exec-once),
|
|
||||||
# starte das Skript in einem Kitty-Fenster:
|
|
||||||
if [[ ! -t 0 || ! -t 1 ]]; then
|
|
||||||
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
|
|
||||||
|
|
||||||
# ANSI Farben
|
|
||||||
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'
|
|
||||||
|
|
||||||
mark_as_prompted() {
|
|
||||||
mkdir -p "$(dirname "$MARKER_FILE")" 2>/dev/null || true
|
|
||||||
touch "$MARKER_FILE" 2>/dev/null || true
|
|
||||||
}
|
|
||||||
|
|
||||||
install_oh_my_zsh() {
|
|
||||||
local zsh_dir="$HOME/.oh-my-zsh"
|
|
||||||
printf "\n"
|
|
||||||
printf "%b\n" "${CLR_BOLD}${CLR_CYAN}==============================================================================${CLR_RESET}"
|
|
||||||
printf "%b\n" "${CLR_BOLD}${CLR_CYAN} Oh-My-Zsh & Shell-Konfiguration ${CLR_RESET}"
|
|
||||||
printf "%b\n" "${CLR_BOLD}${CLR_CYAN}==============================================================================${CLR_RESET}"
|
|
||||||
printf "\n"
|
|
||||||
|
|
||||||
if [[ ! -d "$zsh_dir" ]]; then
|
|
||||||
printf "%b\n" "${CLR_CYAN}[INFO] Richte Oh-My-Zsh für $USER ein...${CLR_RESET}"
|
|
||||||
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
|
|
||||||
printf "%b\n" "${CLR_GREEN}[✓] Oh-My-Zsh erfolgreich eingerichtet.${CLR_RESET}"
|
|
||||||
else
|
|
||||||
printf "%b\n" "${CLR_YELLOW}[WARNUNG] Oh-My-Zsh konnte nicht geklont werden.${CLR_RESET}"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
printf "%b\n" "${CLR_RED}[FEHLER] 'git' ist nicht installiert. Oh-My-Zsh konnte nicht eingerichtet werden.${CLR_RESET}"
|
|
||||||
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
|
|
||||||
printf "%b\n" "${CLR_GREEN}[✓] Oh-My-Zsh ist bereits vorhanden und konfiguriert.${CLR_RESET}"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
install_junie_cli_and_skills() {
|
|
||||||
printf "\n"
|
|
||||||
printf "%b\n" "${CLR_BOLD}${CLR_CYAN}==============================================================================${CLR_RESET}"
|
|
||||||
printf "%b\n" "${CLR_BOLD}${CLR_CYAN} JetBrains Junie CLI & AwesomeJunieSkills Installation ${CLR_RESET}"
|
|
||||||
printf "%b\n" "${CLR_BOLD}${CLR_CYAN}==============================================================================${CLR_RESET}"
|
|
||||||
printf "\n"
|
|
||||||
|
|
||||||
# 1. JetBrains Junie CLI installieren
|
|
||||||
printf "%b\n" "${CLR_CYAN}[1/2] Installiere JetBrains Junie CLI...${CLR_RESET}"
|
|
||||||
if command -v curl >/dev/null 2>&1; then
|
|
||||||
if curl -fsSL https://junie.jetbrains.com/install.sh | bash; then
|
|
||||||
printf "%b\n" "${CLR_GREEN}[✓] JetBrains Junie CLI erfolgreich installiert.${CLR_RESET}"
|
|
||||||
else
|
|
||||||
printf "%b\n" "${CLR_YELLOW}[WARNUNG] Installation von JetBrains Junie CLI fehlgeschlagen oder unterbrochen.${CLR_RESET}"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
printf "%b\n" "${CLR_RED}[FEHLER] 'curl' ist nicht installiert. Junie CLI konnte nicht geladen werden.${CLR_RESET}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 2. AwesomeJunieSkills im Benutzer-Skillverzeichnis (~/.junie/skills) klonen / aktualisieren
|
|
||||||
printf "\n"
|
|
||||||
printf "%b\n" "${CLR_CYAN}[2/2] Installiere AwesomeJunieSkills im Benutzer-Skillverzeichnis (~/.junie/skills)...${CLR_RESET}"
|
|
||||||
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
|
|
||||||
if [[ -d "$repo_dir/.git" ]]; then
|
|
||||||
printf "%b\n" "${CLR_BLUE}[INFO] Repository existiert bereits in $repo_dir. Führe 'git pull' aus...${CLR_RESET}"
|
|
||||||
if git -C "$repo_dir" pull --ff-only 2>/dev/null; then
|
|
||||||
printf "%b\n" "${CLR_GREEN}[✓] AwesomeJunieSkills erfolgreich aktualisiert.${CLR_RESET}"
|
|
||||||
else
|
|
||||||
printf "%b\n" "${CLR_YELLOW}[WARNUNG] 'git pull' in $repo_dir fehlgeschlagen.${CLR_RESET}"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
printf "%b\n" "${CLR_BLUE}[INFO] Klone AwesomeJunieSkills nach $repo_dir...${CLR_RESET}"
|
|
||||||
rm -rf "$repo_dir" 2>/dev/null || true
|
|
||||||
if git clone https://github.com/alebaffa/awesome-junie-skills.git "$repo_dir"; then
|
|
||||||
printf "%b\n" "${CLR_GREEN}[✓] AwesomeJunieSkills erfolgreich nach $repo_dir geklont.${CLR_RESET}"
|
|
||||||
else
|
|
||||||
printf "%b\n" "${CLR_YELLOW}[WARNUNG] Klonen von AwesomeJunieSkills fehlgeschlagen.${CLR_RESET}"
|
|
||||||
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
|
|
||||||
printf "%b\n" "${CLR_GREEN}[✓] $count Skills verknüpft in '$skills_dir' (kann via 'git pull' in '$repo_dir' aktualisiert werden).${CLR_RESET}"
|
|
||||||
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
|
|
||||||
else
|
|
||||||
printf "%b\n" "${CLR_RED}[FEHLER] 'git' ist nicht installiert. AwesomeJunieSkills konnte nicht eingerichtet werden.${CLR_RESET}"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
printf "%b\n" "${CLR_RED}[FEHLER] 'curl' ist nicht installiert. Bitte installieren Sie curl und wiederholen Sie den Vorgang.${CLR_RESET}"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
clone_custom_repo() {
|
|
||||||
if ! command -v git >/dev/null 2>&1; then
|
|
||||||
printf "\n%b\n" "${CLR_RED}[FEHLER] 'git' ist nicht installiert. Bitte installieren Sie git zuerst.${CLR_RESET}"
|
|
||||||
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
|
|
||||||
printf "%b\n" "${CLR_YELLOW}[INFO] Vorgang abgebrochen. Kehre zum Hauptmenü zurück.${CLR_RESET}"
|
|
||||||
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"
|
|
||||||
printf "%b\n" "${CLR_YELLOW}[WARNUNG] Es wurde kein lokaler SSH-Privatschlüssel in ~/.ssh/ gefunden.${CLR_RESET}"
|
|
||||||
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
|
|
||||||
printf "%b\n" "${CLR_GREEN}(In die Zwischenablage kopiert!)${CLR_RESET}"
|
|
||||||
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
|
|
||||||
printf "%b\n" "${CLR_GREEN}[✓] Hyprland erfolgreich aktualisiert.${CLR_RESET}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf "\n"
|
|
||||||
printf "%b\n" "${CLR_GREEN}Dotfiles-Einrichtung abgeschlossen!${CLR_RESET}"
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
printf "\n"
|
|
||||||
printf "%b\n" "${CLR_RED}${CLR_BOLD}[FEHLER] Git Clone ist fehlgeschlagen (Authentifizierungsfehler, falsche URL oder Netzwerkproblem).${CLR_RESET}"
|
|
||||||
printf "%b\n" "${CLR_YELLOW}[ROLLBACK] Stelle vorheriges ~/.config-Verzeichnis aus dem Backup wieder her...${CLR_RESET}"
|
|
||||||
rm -rf "$HOME/.config" 2>/dev/null || true
|
|
||||||
if [[ -d "$backup_dir" ]]; then
|
|
||||||
mv "$backup_dir" "$HOME/.config"
|
|
||||||
printf "%b\n" "${CLR_GREEN}[✓] Ursprüngliches ~/.config-Verzeichnis wurde vollständig wiederhergestellt.${CLR_RESET}"
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
clear 2>/dev/null || true
|
|
||||||
printf "%b\n" "${CLR_BOLD}${CLR_CYAN}==============================================================================${CLR_RESET}"
|
|
||||||
printf "%b\n" "${CLR_BOLD}${CLR_CYAN} Hyprland Desktop - Dotfiles Setup & Konfiguration ${CLR_RESET}"
|
|
||||||
printf "%b\n" "${CLR_BOLD}${CLR_CYAN}==============================================================================${CLR_RESET}"
|
|
||||||
printf "\n"
|
|
||||||
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" "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"
|
|
||||||
printf "%b\n" "${CLR_YELLOW}[INFO] Dotfiles-Einrichtung übersprungen. Die Basiskonfiguration bleibt aktiv.${CLR_RESET}"
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
printf "%b\n" "${CLR_RED}Ungültige Eingabe. Bitte 1, 2 oder 3 wählen.${CLR_RESET}"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
# Oh-My-Zsh & Plugins einrichten
|
|
||||||
install_oh_my_zsh
|
|
||||||
|
|
||||||
# JetBrains Junie CLI & AwesomeJunieSkills installieren
|
|
||||||
install_junie_cli_and_skills
|
|
||||||
|
|
||||||
# Marker-Datei setzen
|
|
||||||
mark_as_prompted
|
|
||||||
|
|
||||||
printf "\n"
|
|
||||||
printf "%b\n" "${CLR_GREEN}${CLR_BOLD}[✓] Ersteinrichtung vollständig abgeschlossen!${CLR_RESET}"
|
|
||||||
printf "%b\n" "Sie können dieses Menü jederzeit erneut aufrufen mit:"
|
|
||||||
printf "%b\n" " ${CLR_BOLD}~/.config/hypr/scripts/install-dots-prompt.sh --force${CLR_RESET}"
|
|
||||||
@@ -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
+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
|
||||||
+22
-22
@@ -59,7 +59,7 @@ trap cleanup EXIT INT TERM
|
|||||||
|
|
||||||
main() {
|
main() {
|
||||||
local repo_url="${REPO_URL:-https://gitea.creative-dragonslayer.de/Scripts/Setup.git}"
|
local repo_url="${REPO_URL:-https://gitea.creative-dragonslayer.de/Scripts/Setup.git}"
|
||||||
local repo_branch="${REPO_BRANCH:-}"
|
local repo_branch="${REPO_BRANCH:-dev}"
|
||||||
|
|
||||||
log_step "Debian Unstable & Hyprland Setup Framework Installer"
|
log_step "Debian Unstable & Hyprland Setup Framework Installer"
|
||||||
log_info "Repository: $repo_url ${repo_branch:+(Branch: $repo_branch)}"
|
log_info "Repository: $repo_url ${repo_branch:+(Branch: $repo_branch)}"
|
||||||
@@ -110,32 +110,32 @@ main() {
|
|||||||
|
|
||||||
log_success "Repository cloned successfully. Starting setup orchestrator..."
|
log_success "Repository cloned successfully. Starting setup orchestrator..."
|
||||||
|
|
||||||
# Check if dry-run flag is requested
|
# setup.sh itself elevates to root via sudo once it needs it - after all
|
||||||
local is_dry_run=0
|
# interactive TUI/prompts have already run as the current user. Escalating
|
||||||
for arg in "$@"; do
|
# here instead would wrap the whiptail dialogs in sudo, which (when sudo is
|
||||||
if [[ "$arg" == "-d" || "$arg" == "--dry-run" ]]; then
|
# invoked from this non-interactive piped shell) leaves them unable to
|
||||||
is_dry_run=1
|
# receive keystrokes. See setup.sh for details.
|
||||||
break
|
local -a setup_cmd=(./setup.sh "$@")
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Prepare command to execute setup.sh
|
# Reconnect stdin/stdout to /dev/tty before executing setup.sh if piped
|
||||||
local -a setup_cmd=()
|
# (e.g. curl ... | bash), otherwise the interactive TUI receives no keystrokes
|
||||||
if [[ "$(id -u)" -ne 0 ]] && [[ "$is_dry_run" -eq 0 ]]; then
|
if [[ -c /dev/tty ]] && ( : < /dev/tty ) 2>/dev/null; then
|
||||||
if ! command -v sudo >/dev/null 2>&1; then
|
if [[ ! -t 0 ]]; then
|
||||||
log_error "sudo is required to run setup.sh. Please install sudo or run as root."
|
exec < /dev/tty
|
||||||
exit 1
|
fi
|
||||||
|
if [[ ! -t 1 ]]; then
|
||||||
|
exec > /dev/tty
|
||||||
fi
|
fi
|
||||||
setup_cmd+=(sudo)
|
|
||||||
fi
|
fi
|
||||||
setup_cmd+=(./setup.sh "$@")
|
|
||||||
|
|
||||||
# Reconnect stdin to /dev/tty if stdin is currently piped and /dev/tty is accessible (for interactive prompts/TUI)
|
# Ensure a terminal type whiptail/newt is able to render
|
||||||
if [[ ! -t 0 ]] && [[ -e /dev/tty ]] && ( : < /dev/tty ) 2>/dev/null; then
|
case "${TERM:-}" in
|
||||||
"${setup_cmd[@]}" < /dev/tty
|
""|dumb|unknown)
|
||||||
else
|
export TERM="xterm-256color"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
"${setup_cmd[@]}"
|
"${setup_cmd[@]}"
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
+120
-30
@@ -5,28 +5,91 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
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
|
# Check if whiptail is available
|
||||||
tui_check_whiptail() {
|
tui_check_whiptail() {
|
||||||
if ! command_exists whiptail; then
|
if ! command_exists whiptail; then
|
||||||
log_warn "whiptail is not installed. Attempting to install whiptail..."
|
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
|
DEBIAN_FRONTEND=noninteractive apt-get update -y && DEBIAN_FRONTEND=noninteractive apt-get install -y whiptail
|
||||||
fi
|
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
|
# Display welcome message
|
||||||
tui_welcome() {
|
tui_welcome() {
|
||||||
whiptail --title "Debian Unstable Setup & Hyprland Installer" \
|
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." \
|
--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." \
|
||||||
14 70
|
"$TUI_HEIGHT" "$TUI_WIDTH" || true
|
||||||
}
|
}
|
||||||
|
|
||||||
# Present stage checklist to user
|
# Present stage checklist to user
|
||||||
# Returns space-separated list of selected stage numbers (e.g., "01 02 03 04 05 06 07 08 09")
|
# Sets TUI_SELECTION and returns 0 on success, 1 on cancel
|
||||||
tui_select_stages() {
|
tui_select_stages() {
|
||||||
local choices
|
local tmp_file
|
||||||
choices=$(whiptail --title "Modulauswahl / Stages" \
|
tmp_file="$(mktemp)"
|
||||||
--checklist "Wähle die gewünschten Installations-Phasen mit der LEERTASTE aus:" \
|
|
||||||
23 78 10 \
|
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 \
|
"01" "Debian Unstable (Deb822, Pinning, Upgrade)" ON \
|
||||||
"02" "Kernel & Hardware (XanMod, P-State, Microcode)" ON \
|
"02" "Kernel & Hardware (XanMod, P-State, Microcode)" ON \
|
||||||
"03" "Bootloader & Theme (GRUB, Vimix, Plymouth)" ON \
|
"03" "Bootloader & Theme (GRUB, Vimix, Plymouth)" ON \
|
||||||
@@ -37,15 +100,19 @@ tui_select_stages() {
|
|||||||
"08" "Flatpaks (Flathub Repository & Flatpaks)" ON \
|
"08" "Flatpaks (Flathub Repository & Flatpaks)" ON \
|
||||||
"09" "Zusatz-Apps & MIME (Spotify, Waydroid, OpenDeck, MIME)" ON \
|
"09" "Zusatz-Apps & MIME (Spotify, Waydroid, OpenDeck, MIME)" ON \
|
||||||
"10" "Hyprland Desktop (LinuxBeginnings Debian-Hyprland Installer)" ON \
|
"10" "Hyprland Desktop (LinuxBeginnings Debian-Hyprland Installer)" ON \
|
||||||
3>&1 1>&2 2>&3) || {
|
"11" "Linutil (Chris Titus Tech System Toolbox)" ON \
|
||||||
log_info "Setup durch Benutzer abgebrochen."
|
3> "$tmp_file"; then
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
|
|
||||||
# Clean quotes from whiptail output (e.g. '"01" "02"' -> '01 02')
|
TUI_SELECTION="$(tr -d '"' < "$tmp_file")"
|
||||||
local cleaned_choices
|
export TUI_SELECTION
|
||||||
cleaned_choices=$(echo "$choices" | tr -d '"')
|
rm -f "$tmp_file"
|
||||||
echo "$cleaned_choices"
|
return 0
|
||||||
|
else
|
||||||
|
rm -f "$tmp_file"
|
||||||
|
TUI_SELECTION=""
|
||||||
|
export TUI_SELECTION
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Prompt user for target user via Whiptail dialog
|
# Prompt user for target user via Whiptail dialog
|
||||||
@@ -53,39 +120,60 @@ tui_prompt_target_user() {
|
|||||||
local default_user="${1:-}"
|
local default_user="${1:-}"
|
||||||
local chosen=""
|
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
|
if [[ -n "$default_user" && "$default_user" != "root" ]]; then
|
||||||
if whiptail --title "Ziel-Benutzer bestätigen" \
|
tui_calc_geometry 10 65
|
||||||
|
if run_whiptail --title "Ziel-Benutzer bestätigen" \
|
||||||
--yes-button "Verwenden" \
|
--yes-button "Verwenden" \
|
||||||
--no-button "Anderer Benutzer" \
|
--no-button "Anderer Benutzer" \
|
||||||
--yesno "Erkannter Ziel-Benutzer: $default_user\n\nMöchtest du diesen Benutzer für die Konfiguration (Dotfiles, Gruppen, Hyprland) verwenden?" \
|
--yesno "Erkannter Ziel-Benutzer: $default_user\n\nMöchtest du diesen Benutzer für die Konfiguration (Dotfiles, Gruppen, Hyprland) verwenden?" \
|
||||||
10 65; then
|
"$TUI_HEIGHT" "$TUI_WIDTH"; then
|
||||||
chosen="$default_user"
|
chosen="$default_user"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
local tmp_file
|
||||||
|
tmp_file="$(mktemp)"
|
||||||
|
|
||||||
while [[ -z "$chosen" ]]; do
|
while [[ -z "$chosen" ]]; do
|
||||||
local input_user
|
tui_calc_geometry 10 60
|
||||||
input_user=$(whiptail --title "Ziel-Benutzer eingeben" \
|
if run_whiptail --output-fd 3 --title "Ziel-Benutzer eingeben" \
|
||||||
--inputbox "Bitte gib den Benutzernamen des Desktop-Benutzers ein:" \
|
--inputbox "Bitte gib den Benutzernamen des Desktop-Benutzers ein:" \
|
||||||
10 60 "$default_user" \
|
"$TUI_HEIGHT" "$TUI_WIDTH" "$default_user" \
|
||||||
3>&1 1>&2 2>&3) || {
|
3> "$tmp_file"; then
|
||||||
log_info "Setup durch Benutzer abgebrochen."
|
|
||||||
exit 0
|
local input_user
|
||||||
}
|
input_user="$(tr -d '[:space:]' < "$tmp_file")"
|
||||||
input_user="$(echo "$input_user" | tr -d '[:space:]')"
|
|
||||||
if [[ -z "$input_user" || "$input_user" == "root" ]]; then
|
if [[ -z "$input_user" || "$input_user" == "root" ]]; then
|
||||||
whiptail --title "Ungültiger Benutzer" --msgbox "Ungültiger Benutzername (darf nicht leer oder root sein)." 8 50
|
run_whiptail --title "Ungültiger Benutzer" --msgbox "Ungültiger Benutzername (darf nicht leer oder root sein)." 8 55 || true
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
if ! id "$input_user" >/dev/null 2>&1; then
|
if ! id "$input_user" >/dev/null 2>&1; then
|
||||||
whiptail --title "Benutzer nicht gefunden" --msgbox "Benutzer '$input_user' existiert nicht im System." 8 50
|
run_whiptail --title "Benutzer nicht gefunden" --msgbox "Benutzer '$input_user' existiert nicht im System." 8 55 || true
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
chosen="$input_user"
|
chosen="$input_user"
|
||||||
|
else
|
||||||
|
rm -f "$tmp_file"
|
||||||
|
log_info "Setup durch Benutzer abgebrochen."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
rm -f "$tmp_file"
|
||||||
export TARGET_USER="$chosen"
|
export TARGET_USER="$chosen"
|
||||||
echo "$chosen"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Confirmation dialog before running
|
# Confirmation dialog before running
|
||||||
@@ -93,7 +181,9 @@ tui_confirm_execution() {
|
|||||||
local selected_stages="$1"
|
local selected_stages="$1"
|
||||||
local target_user="$2"
|
local target_user="$2"
|
||||||
|
|
||||||
whiptail --title "Installation starten" \
|
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?" \
|
--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?" \
|
||||||
14 70
|
"$TUI_HEIGHT" "$TUI_WIDTH"
|
||||||
}
|
}
|
||||||
|
|||||||
+202
-24
@@ -58,15 +58,101 @@ log_substep() {
|
|||||||
|
|
||||||
# Error trap handler
|
# Error trap handler
|
||||||
setup_err_trap() {
|
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
|
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() {
|
error_handler() {
|
||||||
local exit_code="$1"
|
local exit_code="$1"
|
||||||
local line_no="$2"
|
local line_no="$2"
|
||||||
local bash_command="$3"
|
local bash_command="$3"
|
||||||
|
|
||||||
log_error "Command failed with exit code $exit_code at line $line_no: '$bash_command'"
|
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"
|
exit "$exit_code"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
# Root permission check
|
# Root permission check
|
||||||
@@ -82,6 +168,76 @@ command_exists() {
|
|||||||
command -v "$1" >/dev/null 2>&1
|
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
|
# Determine and prompt for the non-root target user
|
||||||
prompt_target_user() {
|
prompt_target_user() {
|
||||||
if [[ -n "${TARGET_USER:-}" ]]; then
|
if [[ -n "${TARGET_USER:-}" ]]; then
|
||||||
@@ -105,14 +261,14 @@ prompt_target_user() {
|
|||||||
|
|
||||||
local chosen=""
|
local chosen=""
|
||||||
local is_interactive=0
|
local is_interactive=0
|
||||||
if [[ -t 0 || -c /dev/tty ]] && [[ "${DEBIAN_FRONTEND:-}" != "noninteractive" && "${CI:-}" != "1" && "${AUTO_CONFIRM:-}" != "1" ]]; then
|
if { [[ -t 0 ]] || has_tty; } && [[ "${DEBIAN_FRONTEND:-}" != "noninteractive" && "${CI:-}" != "1" && "${AUTO_CONFIRM:-}" != "1" ]]; then
|
||||||
is_interactive=1
|
is_interactive=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$is_interactive" -eq 1 ]]; then
|
if [[ "$is_interactive" -eq 1 ]]; then
|
||||||
local tty_in="/dev/tty"
|
local tty_in="/dev/tty"
|
||||||
local tty_out="/dev/tty"
|
local tty_out="/dev/tty"
|
||||||
if [[ ! -c /dev/tty ]]; then
|
if ! has_tty; then
|
||||||
tty_in="/dev/stdin"
|
tty_in="/dev/stdin"
|
||||||
tty_out="/dev/stderr"
|
tty_out="/dev/stderr"
|
||||||
fi
|
fi
|
||||||
@@ -120,9 +276,7 @@ prompt_target_user() {
|
|||||||
if [[ -n "$candidate" && "$candidate" != "root" ]]; then
|
if [[ -n "$candidate" && "$candidate" != "root" ]]; then
|
||||||
printf "\n%bTarget User Detection:%b\n" "${CLR_CYAN}" "${CLR_RESET}" > "$tty_out"
|
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"
|
printf "Detected target user: %b%s%b\n" "${CLR_BOLD}" "$candidate" "${CLR_RESET}" > "$tty_out"
|
||||||
local answer=""
|
if confirm_yes_no "Use target user '$candidate'? [Y/n]: " "y" "$tty_in" "$tty_out"; then
|
||||||
read -r -p "Use target user '$candidate'? [Y/n]: " answer < "$tty_in" > "$tty_out" 2>&1 || answer="y"
|
|
||||||
if [[ -z "$answer" || "$answer" =~ ^[YyJj] ]]; then
|
|
||||||
chosen="$candidate"
|
chosen="$candidate"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -187,52 +341,76 @@ prompt_ollama_models_path() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local default_path="${OLLAMA_MODELS_DEFAULT:-/mnt/Data/Software/ollama/models}"
|
# 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 chosen=""
|
||||||
local is_interactive=0
|
local is_interactive=0
|
||||||
if [[ -t 0 || -c /dev/tty ]] && [[ "${DEBIAN_FRONTEND:-}" != "noninteractive" && "${CI:-}" != "1" && "${AUTO_CONFIRM:-}" != "1" ]]; then
|
if { [[ -t 0 ]] || has_tty; } && [[ "${DEBIAN_FRONTEND:-}" != "noninteractive" && "${CI:-}" != "1" && "${AUTO_CONFIRM:-}" != "1" ]]; then
|
||||||
is_interactive=1
|
is_interactive=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$is_interactive" -eq 1 ]]; then
|
if [[ "$is_interactive" -eq 1 ]]; then
|
||||||
local tty_in="/dev/tty"
|
local tty_in="/dev/tty"
|
||||||
local tty_out="/dev/tty"
|
local tty_out="/dev/tty"
|
||||||
if [[ ! -c /dev/tty ]]; then
|
if ! has_tty; then
|
||||||
tty_in="/dev/stdin"
|
tty_in="/dev/stdin"
|
||||||
tty_out="/dev/stderr"
|
tty_out="/dev/stderr"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# If whiptail is available and running on an active terminal, use whiptail dialog
|
# If whiptail is available and running on an active terminal, first ask
|
||||||
if command_exists whiptail && [[ -t 1 || -c /dev/tty ]]; then
|
# whether Ollama's own default path should be used, and only show the
|
||||||
local tui_input
|
# free-text path dialog (pre-filled with the custom suggestion) if not.
|
||||||
if tui_input=$(whiptail --title "Ollama KI-Modelle Speicherort" \
|
if command_exists whiptail && has_tty; then
|
||||||
--inputbox "Bitte gib den Speicherort für die Ollama KI-Modelle an:" \
|
local yesno_status=0
|
||||||
10 70 "$default_path" \
|
run_whiptail --title "Ollama KI-Modelle Speicherort" \
|
||||||
3>&1 1>&2 2>&3); then
|
--yes-button "Standard verwenden" --no-button "Eigenen Pfad angeben" \
|
||||||
tui_input="$(echo "$tui_input" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
|
--yesno "Standard-Speicherort für Ollama-Modelle:\n\n$ollama_default_path\n\nMöchtest du diesen Speicherort verwenden?" \
|
||||||
if [[ -n "$tui_input" ]]; then
|
12 70 || yesno_status=$?
|
||||||
chosen="$tui_input"
|
if [[ "$yesno_status" -eq 0 || "$yesno_status" -eq 255 ]]; then
|
||||||
else
|
# Yes, or ESC/Cancel (255) - use the Ollama default,
|
||||||
chosen="$default_path"
|
# 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
|
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
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# CLI fallback if whiptail was not used or failed
|
# CLI fallback if whiptail was not used or failed
|
||||||
if [[ -z "$chosen" ]]; then
|
if [[ -z "$chosen" ]]; then
|
||||||
printf "\n%bOllama KI-Modelle Speicherort:%b\n" "${CLR_CYAN}" "${CLR_RESET}" > "$tty_out"
|
printf "\n%bOllama KI-Modelle Speicherort:%b\n" "${CLR_CYAN}" "${CLR_RESET}" > "$tty_out"
|
||||||
printf "Standard-Pfad: %b%s%b\n" "${CLR_BOLD}" "$default_path" "${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=""
|
local input_path=""
|
||||||
read -r -p "Speicherort für Ollama-Modelle eingeben [$default_path]: " input_path < "$tty_in" > "$tty_out" 2>&1 || true
|
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:]]*$//')"
|
input_path="$(echo "$input_path" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
|
||||||
if [[ -n "$input_path" ]]; then
|
if [[ -n "$input_path" ]]; then
|
||||||
chosen="$input_path"
|
chosen="$input_path"
|
||||||
else
|
else
|
||||||
chosen="$default_path"
|
chosen="$custom_path_suggestion"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
chosen="$default_path"
|
chosen="$ollama_default_path"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export OLLAMA_MODELS="$chosen"
|
export OLLAMA_MODELS="$chosen"
|
||||||
|
|||||||
@@ -21,15 +21,32 @@ fi
|
|||||||
|
|
||||||
setup_err_trap
|
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 text
|
||||||
show_help() {
|
show_help() {
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Usage: sudo ./setup.sh [OPTIONS]
|
Usage: ./setup.sh [OPTIONS]
|
||||||
|
|
||||||
Debian Unstable Setup & Desktop Customization Framework
|
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:
|
Options:
|
||||||
-a, --all Run all setup stages sequentially (00 through 10)
|
-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)
|
-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)
|
-u, --user <username> Specify target desktop user (defaults to \$SUDO_USER)
|
||||||
-d, --dry-run Simulate stage execution without modifying the system
|
-d, --dry-run Simulate stage execution without modifying the system
|
||||||
@@ -47,12 +64,13 @@ Available Stages:
|
|||||||
08: Flatpak Applications (Flathub, Flatpak package list)
|
08: Flatpak Applications (Flathub, Flatpak package list)
|
||||||
09: Standalone Applications & MIME (Spotify, Waydroid, OpenDeck, MIME types)
|
09: Standalone Applications & MIME (Spotify, Waydroid, OpenDeck, MIME types)
|
||||||
10: Hyprland Desktop (LinuxBeginnings Auto-Installer)
|
10: Hyprland Desktop (LinuxBeginnings Auto-Installer)
|
||||||
|
11: Linutil (Chris Titus Tech System Toolbox)
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
sudo ./setup.sh # Interactive TUI mode
|
./setup.sh # Interactive TUI mode
|
||||||
sudo ./setup.sh --all # Full automated install
|
./setup.sh --all # Full automated install
|
||||||
sudo ./setup.sh --stages 04,05 # Install only packages and fonts
|
./setup.sh --stages 04,05 # Install only packages and fonts
|
||||||
sudo ./setup.sh --stages 01 --dry-run # Dry-run stage 01
|
./setup.sh --stages 01 --dry-run # Dry-run stage 01
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,16 +126,15 @@ if [[ -n "$CLI_USER" ]]; then
|
|||||||
export TARGET_USER="$CLI_USER"
|
export TARGET_USER="$CLI_USER"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure root privileges unless dry-run
|
# NOTE: Root privileges are intentionally NOT required here. All interactive
|
||||||
if [[ "$FLAG_DRY_RUN" -eq 0 ]]; then
|
# prompts (whiptail TUI, target user, Ollama path) must run BEFORE escalating
|
||||||
require_root
|
# to sudo - see the privilege escalation block below for why.
|
||||||
fi
|
|
||||||
|
|
||||||
# Determine stages to run
|
# Determine stages to run
|
||||||
SELECTED_STAGES=()
|
SELECTED_STAGES=()
|
||||||
|
|
||||||
if [[ "$FLAG_ALL" -eq 1 ]]; then
|
if [[ "$FLAG_ALL" -eq 1 ]]; then
|
||||||
SELECTED_STAGES=(01 02 03 04 05 06 07 08 09 10)
|
SELECTED_STAGES=(01 02 03 04 05 06 07 08 09 10 11)
|
||||||
elif [[ -n "$FLAG_STAGES" ]]; then
|
elif [[ -n "$FLAG_STAGES" ]]; then
|
||||||
IFS=',' read -r -a raw_stages <<< "$FLAG_STAGES"
|
IFS=',' read -r -a raw_stages <<< "$FLAG_STAGES"
|
||||||
for st in "${raw_stages[@]}"; do
|
for st in "${raw_stages[@]}"; do
|
||||||
@@ -130,29 +147,35 @@ elif [[ -n "$FLAG_STAGES" ]]; then
|
|||||||
done
|
done
|
||||||
else
|
else
|
||||||
# Interactive TUI Mode
|
# Interactive TUI Mode
|
||||||
if [[ -t 0 && -t 1 ]]; then
|
if has_tty; then
|
||||||
tui_check_whiptail
|
tui_check_whiptail
|
||||||
tui_welcome
|
tui_welcome
|
||||||
TUI_SELECTION=$(tui_select_stages)
|
if ! tui_select_stages; then
|
||||||
|
log_info "Setup aborted by user."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -z "$TUI_SELECTION" ]]; then
|
if [[ -z "${TUI_SELECTION:-}" ]]; then
|
||||||
log_warn "No stages selected. Exiting."
|
log_warn "No stages selected. Exiting."
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
read -r -a SELECTED_STAGES <<< "$TUI_SELECTION"
|
read -r -a SELECTED_STAGES <<< "$TUI_SELECTION"
|
||||||
|
|
||||||
DETECTED_USER="${TARGET_USER:-$(get_target_user 2>/dev/null || true)}"
|
DETECTED_USER="${TARGET_USER:-${SUDO_USER:-$(logname 2>/dev/null || id -un 2>/dev/null || true)}}"
|
||||||
TARGET_USER_NAME="$(tui_prompt_target_user "$DETECTED_USER")"
|
if [[ "$DETECTED_USER" == "root" ]]; then
|
||||||
export TARGET_USER="$TARGET_USER_NAME"
|
DETECTED_USER=""
|
||||||
|
fi
|
||||||
|
tui_prompt_target_user "$DETECTED_USER"
|
||||||
|
|
||||||
if ! tui_confirm_execution "${SELECTED_STAGES[*]}" "$TARGET_USER_NAME"; then
|
if ! tui_confirm_execution "${SELECTED_STAGES[*]}" "$TARGET_USER"; then
|
||||||
log_info "Installation aborted by user."
|
log_info "Installation aborted by user."
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
|
log_warn "No controlling terminal available - interactive TUI cannot be displayed."
|
||||||
log_info "Non-interactive shell and no parameters provided. Defaulting to --all."
|
log_info "Non-interactive shell and no parameters provided. Defaulting to --all."
|
||||||
SELECTED_STAGES=(01 02 03 04 05 06 07 08 09 10)
|
SELECTED_STAGES=(01 02 03 04 05 06 07 08 09 10 11)
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -162,6 +185,45 @@ if [[ -z "${TARGET_USER:-}" ]]; then
|
|||||||
export TARGET_USER
|
export TARGET_USER
|
||||||
fi
|
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
|
# Map stage identifier to script file
|
||||||
get_stage_script() {
|
get_stage_script() {
|
||||||
local stage_id="$1"
|
local stage_id="$1"
|
||||||
|
|||||||
@@ -120,6 +120,24 @@ if compgen -G "$SCRIPT_DIR/data/etc/environment.d/*.conf" > /dev/null; then
|
|||||||
done
|
done
|
||||||
fi
|
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"
|
create_timeshift_snapshot "Stage 02: Post-kernel and hardware tuning" "O"
|
||||||
|
|
||||||
log_success "Stage 02: Kernel and Hardware Tuning completed successfully!"
|
log_success "Stage 02: Kernel and Hardware Tuning completed successfully!"
|
||||||
|
|||||||
+61
-1
@@ -162,6 +162,66 @@ usermod -aG sudo,docker,netdev "$TARGET_USER"
|
|||||||
if getent group ollama >/dev/null 2>&1; then
|
if getent group ollama >/dev/null 2>&1; then
|
||||||
usermod -aG ollama "$TARGET_USER" 2>/dev/null || true
|
usermod -aG ollama "$TARGET_USER" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
log_success "Target user $TARGET_USER added to sudo, docker, netdev and ollama groups."
|
# "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!"
|
log_success "Stage 06: System services configuration completed successfully!"
|
||||||
|
|||||||
@@ -47,9 +47,6 @@ fi
|
|||||||
if [[ -d "$SCRIPT_DIR/data/skel/.config" ]]; then
|
if [[ -d "$SCRIPT_DIR/data/skel/.config" ]]; then
|
||||||
mkdir -p /etc/skel/.config
|
mkdir -p /etc/skel/.config
|
||||||
cp -r "$SCRIPT_DIR"/data/skel/.config/* /etc/skel/.config/ 2>/dev/null || true
|
cp -r "$SCRIPT_DIR"/data/skel/.config/* /etc/skel/.config/ 2>/dev/null || true
|
||||||
if [[ -d /etc/skel/.config/hypr/scripts ]]; then
|
|
||||||
chmod 0755 /etc/skel/.config/hypr/scripts/*.sh 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Provision Oh-My-Zsh template in /etc/skel so newly created users receive it automatically
|
# Provision Oh-My-Zsh template in /etc/skel so newly created users receive it automatically
|
||||||
@@ -92,9 +89,6 @@ fi
|
|||||||
|
|
||||||
if [[ -d "$SCRIPT_DIR/data/skel/.config" ]]; then
|
if [[ -d "$SCRIPT_DIR/data/skel/.config" ]]; then
|
||||||
cp -r "$SCRIPT_DIR"/data/skel/.config/* "$TARGET_HOME/.config/" 2>/dev/null || true
|
cp -r "$SCRIPT_DIR"/data/skel/.config/* "$TARGET_HOME/.config/" 2>/dev/null || true
|
||||||
if [[ -d "$TARGET_HOME/.config/hypr/scripts" ]]; then
|
|
||||||
chmod 0755 "$TARGET_HOME"/.config/hypr/scripts/*.sh 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 4. Install Oh-My-Zsh for Target User (if not already installed)
|
# 4. Install Oh-My-Zsh for Target User (if not already installed)
|
||||||
|
|||||||
+32
-13
@@ -41,21 +41,40 @@ else
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 2. Ensure rudimentary Hyprland config is deployed to /etc/skel
|
# 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..."
|
log_substep "Deploying rudimentary Hyprland configuration to /etc/skel..."
|
||||||
mkdir -p /etc/skel/.config/hypr/scripts
|
mkdir -p /etc/skel/.config/hypr
|
||||||
if [[ -f "$SCRIPT_DIR/data/skel/.config/hypr/hyprland.conf" ]]; then
|
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
|
cp "$SCRIPT_DIR/data/skel/.config/hypr/hyprland.conf" /etc/skel/.config/hypr/hyprland.conf
|
||||||
fi
|
fi
|
||||||
if [[ -f "$SCRIPT_DIR/data/skel/.config/hypr/scripts/install-dots-prompt.sh" ]]; then
|
|
||||||
cp "$SCRIPT_DIR/data/skel/.config/hypr/scripts/install-dots-prompt.sh" /etc/skel/.config/hypr/scripts/install-dots-prompt.sh
|
|
||||||
chmod 0755 /etc/skel/.config/hypr/scripts/install-dots-prompt.sh
|
|
||||||
fi
|
|
||||||
chown -R root:root /etc/skel/.config
|
chown -R root:root /etc/skel/.config
|
||||||
|
|
||||||
# 3. Deploy rudimentary Hyprland config to target user
|
# 4. Deploy rudimentary Hyprland config to target user
|
||||||
log_substep "Deploying rudimentary Hyprland configuration to $TARGET_HOME/.config/hypr..."
|
log_substep "Deploying rudimentary Hyprland configuration to $TARGET_HOME/.config/hypr..."
|
||||||
mkdir -p "$TARGET_HOME/.config/hypr/scripts"
|
mkdir -p "$TARGET_HOME/.config/hypr"
|
||||||
|
|
||||||
if [[ -f "$TARGET_HOME/.config/hypr/hyprland.conf" ]]; then
|
if [[ -f "$TARGET_HOME/.config/hypr/hyprland.conf" ]]; then
|
||||||
backup_file_or_dir "$TARGET_HOME/.config/hypr/hyprland.conf"
|
backup_file_or_dir "$TARGET_HOME/.config/hypr/hyprland.conf"
|
||||||
@@ -64,17 +83,17 @@ fi
|
|||||||
if [[ -f "$SCRIPT_DIR/data/skel/.config/hypr/hyprland.conf" ]]; then
|
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"
|
cp "$SCRIPT_DIR/data/skel/.config/hypr/hyprland.conf" "$TARGET_HOME/.config/hypr/hyprland.conf"
|
||||||
fi
|
fi
|
||||||
if [[ -f "$SCRIPT_DIR/data/skel/.config/hypr/scripts/install-dots-prompt.sh" ]]; then
|
|
||||||
cp "$SCRIPT_DIR/data/skel/.config/hypr/scripts/install-dots-prompt.sh" "$TARGET_HOME/.config/hypr/scripts/install-dots-prompt.sh"
|
|
||||||
chmod 0755 "$TARGET_HOME/.config/hypr/scripts/install-dots-prompt.sh"
|
|
||||||
fi
|
|
||||||
|
|
||||||
chown -R "$TARGET_USER:$TARGET_USER" "$TARGET_HOME/.config/hypr"
|
chown -R "$TARGET_USER:$TARGET_USER" "$TARGET_HOME/.config/hypr"
|
||||||
|
|
||||||
log_success "Rudimentary Hyprland configuration deployed:"
|
log_success "Rudimentary Hyprland configuration deployed:"
|
||||||
log_info " - SUPER+ENTER: Kitty Terminal"
|
log_info " - SUPER+ENTER: Kitty Terminal"
|
||||||
log_info " - SUPER+Q: Close Active Window"
|
log_info " - SUPER+Q: Close Active Window"
|
||||||
log_info " - Login Prompt: Asks user after login to choose Dotfiles setup & installs JetBrains Junie CLI + AwesomeJunieSkills"
|
log_info " - 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"
|
create_timeshift_snapshot "Stage 10: Post-Hyprland desktop setup" "O"
|
||||||
|
|
||||||
|
|||||||
@@ -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