diff options
Diffstat (limited to 'scripts')
| -rwxr-xr-x | scripts/copy_menu.sh | 82 | ||||
| -rw-r--r-- | scripts/lib_apps.sh | 116 | ||||
| -rw-r--r-- | scripts/lib_backup.sh | 72 | ||||
| -rw-r--r-- | scripts/lib_copy.sh | 362 | ||||
| -rw-r--r-- | scripts/lib_detect.sh | 49 | ||||
| -rw-r--r-- | scripts/lib_prompts.sh | 249 | ||||
| -rw-r--r-- | scripts/lib_update.sh | 84 |
7 files changed, 1014 insertions, 0 deletions
diff --git a/scripts/copy_menu.sh b/scripts/copy_menu.sh new file mode 100755 index 00000000..87f9301f --- /dev/null +++ b/scripts/copy_menu.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash + +# show_copy_menu +# Arguments: +# $1 - express_supported flag (1 if available, 0 otherwise) +# Sets global COPY_MENU_CHOICE to one of: install, upgrade, express, quit +show_copy_menu() { + local express_supported="${1:-0}" + local menu_title=" KooL's Hyprland Dotfiles " + local prompt="Select what you would like to do:" + + local install_tag="Install" + local upgrade_tag="Upgrade" + local express_tag="Express" + local update_tag="Update" + local quit_tag="Quit" + + local install_desc="Fresh copy" + local upgrade_desc="Backups + prompts" + local express_desc="Skips restores & wallpapers" + local update_desc="Stash + git pull" + local quit_desc="Exit without changes" + + local choice="" + run_basic_menu() { + while true; do + printf "\n%s\n" "$menu_title" + printf "%s\n" "$prompt" + printf " 1) Install - %s\n" "$install_desc" + printf " 2) Upgrade - %s\n" "$upgrade_desc" + if [ "$express_supported" -eq 1 ]; then + printf " 3) Express - %s\n" "$express_desc" + else + printf " 3) Express - %s (disabled)\n" "$express_desc" + fi + printf " 4) Update - %s\n" "$update_desc" + printf " 5) Quit - %s\n" "$quit_desc" + printf "Enter choice [1-5]: " + read -r text_choice + case "$text_choice" in + 1) choice="$install_tag"; break ;; + 2) choice="$upgrade_tag"; break ;; + 3) + if [ "$express_supported" -eq 1 ]; then + choice="$express_tag" + break + else + echo "Express is disabled on this system." + fi + ;; + 4) choice="$update_tag"; break ;; + 5) choice="$quit_tag"; break ;; + *) echo "Invalid selection. Please choose 1-5." ;; + esac + done + } + + if [ "$COPY_TUI_BACKEND" = "basic" ]; then + run_basic_menu + COPY_MENU_CHOICE="$choice" + return 0 + fi + + # Fallback to whiptail if present + if command -v whiptail >/dev/null 2>&1; then + if ! choice=$(whiptail --title "$menu_title" --menu "$prompt" 17 60 8 \ + "$install_tag" "$install_desc" \ + "$upgrade_tag" "$upgrade_desc" \ + "$express_tag" "$express_desc" \ + "$update_tag" "$update_desc" \ + "$quit_tag" "$quit_desc" 3>&1 1>&2 2>&3); then + COPY_MENU_CHOICE="quit" + return 1 + fi + else + # Plain-text fallback + run_basic_menu + fi + + # shellcheck disable=SC2034 # used by parent script after sourcing this file + COPY_MENU_CHOICE="$choice" +} diff --git a/scripts/lib_apps.sh b/scripts/lib_apps.sh new file mode 100644 index 00000000..562e5c5b --- /dev/null +++ b/scripts/lib_apps.sh @@ -0,0 +1,116 @@ +#!/usr/bin/env bash +# App enablement and editor selection helpers. + +enable_asusctl() { + local log="$1" + if command -v asusctl >/dev/null 2>&1; then + local OVERLAY_SA="config/hypr/configs/Startup_Apps.conf" + mkdir -p "$(dirname "$OVERLAY_SA")" + touch "$OVERLAY_SA" + grep -qx 'exec-once = rog-control-center' "$OVERLAY_SA" || echo 'exec-once = rog-control-center' >>"$OVERLAY_SA" + fi +} + +enable_blueman() { + local log="$1" + if command -v blueman-applet >/dev/null 2>&1; then + local OVERLAY_SA="config/hypr/configs/Startup_Apps.conf" + mkdir -p "$(dirname "$OVERLAY_SA")" + touch "$OVERLAY_SA" + grep -qx 'exec-once = blueman-applet' "$OVERLAY_SA" || echo 'exec-once = blueman-applet' >>"$OVERLAY_SA" + fi +} + +enable_ags() { + local log="$1" + if command -v ags >/dev/null 2>&1; then + echo "${INFO:-[INFO]} AGS detected - enabling in startup and refresh scripts" 2>&1 | tee -a "$log" + local OVERLAY_SA="config/hypr/configs/Startup_Apps.conf" + mkdir -p "$(dirname "$OVERLAY_SA")" + touch "$OVERLAY_SA" + grep -qx 'exec-once = ags' "$OVERLAY_SA" || echo 'exec-once = ags' >>"$OVERLAY_SA" + sed -i '/#ags -q && ags &/s/^#//' config/hypr/scripts/RefreshNoWaybar.sh + sed -i '/#ags -q && ags &/s/^#//' config/hypr/scripts/Refresh.sh + fi +} + +enable_quickshell() { + local log="$1" + if command -v qs >/dev/null 2>&1; then + echo "${INFO:-[INFO]} Quickshell detected - enabling in startup and refresh scripts" 2>&1 | tee -a "$log" + local OVERLAY_SA="config/hypr/configs/Startup_Apps.conf" + mkdir -p "$(dirname "$OVERLAY_SA")" + touch "$OVERLAY_SA" + grep -qx 'exec-once = qs' "$OVERLAY_SA" || echo 'exec-once = qs' >>"$OVERLAY_SA" + sed -i '/#pkill qs && qs &/s/^#//' config/hypr/scripts/RefreshNoWaybar.sh + sed -i '/#pkill qs && qs &/s/^#//' config/hypr/scripts/Refresh.sh + fi +} + +ensure_keybinds_init() { + local log="$1" + local OVERLAY_SA="config/hypr/configs/Startup_Apps.conf" + mkdir -p "$(dirname "$OVERLAY_SA")" + if ! grep -qx 'exec-once = \$scriptsDir/KeybindsLayoutInit.sh' "$OVERLAY_SA"; then + echo 'exec-once = $scriptsDir/KeybindsLayoutInit.sh' >>"$OVERLAY_SA" + echo "${INFO:-[INFO]} Added KeybindsLayoutInit.sh to user Startup_Apps overlay" 2>&1 | tee -a "$log" + fi +} + +install_terminal_configs() { + local log="$1" + + # Ghostty + local GHOSTTY_SRC="config/ghostty/ghostty.config" + local GHOSTTY_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/ghostty" + local GHOSTTY_DEST="$GHOSTTY_DIR/config" + if [ -f "$GHOSTTY_SRC" ]; then + mkdir -p "$GHOSTTY_DIR" + install -m 0644 "$GHOSTTY_SRC" "$GHOSTTY_DEST" 2>&1 | tee -a "$log" + if [ -f "$GHOSTTY_DIR/wallust.conf" ]; then + sed -i -E 's/^(\\s*palette\\s*=\\s*)([0-9]{1,2}):/\\1\\2=/' "$GHOSTTY_DIR/wallust.conf" 2>&1 | tee -a "$log" || true + fi + else + echo "${ERROR:-[ERROR]} - $GHOSTTY_SRC not found; skipping Ghostty config install." 2>&1 | tee -a "$log" + fi + + # WezTerm + local WEZTERM_SRC="config/wezterm/wezterm.lua" + local WEZTERM_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/wezterm" + local WEZTERM_DEST="$WEZTERM_DIR/wezterm.lua" + if [ -f "$WEZTERM_SRC" ]; then + mkdir -p "$WEZTERM_DIR" + install -m 0644 "$WEZTERM_SRC" "$WEZTERM_DEST" 2>&1 | tee -a "$log" + else + echo "${ERROR:-[ERROR]} - $WEZTERM_SRC not found; skipping WezTerm config install." 2>&1 | tee -a "$log" + fi +} + +choose_default_editor() { + local log="$1" + local editor_set=0 + update_editor() { + local editor=$1 + sed -i "s/#env = EDITOR,.*/env = EDITOR,$editor #default editor/" config/hypr/UserConfigs/01-UserDefaults.conf + echo "${OK:-[OK]} Default editor set to ${MAGENTA:-}$editor${RESET:-}." 2>&1 | tee -a "$log" + } + if command -v nvim &>/dev/null; then + printf "${INFO:-[INFO]} ${MAGENTA:-}neovim${RESET:-} is detected as installed\n" + if ! read -r -p "${CAT:-[ACTION]} Do you want to make ${MAGENTA:-}neovim${RESET:-} the default editor? (y/N): " EDITOR_CHOICE </dev/tty; then + : + elif [[ "$EDITOR_CHOICE" == "y" || "$EDITOR_CHOICE" == "Y" ]]; then + update_editor "nvim" + editor_set=1 + fi + fi + printf "\n" + if [[ "$editor_set" -eq 0 ]] && command -v vim &>/dev/null; then + printf "${INFO:-[INFO]} ${MAGENTA:-}vim${RESET:-} is detected as installed\n" + if read -r -p "${CAT:-[ACTION]} Do you want to make ${MAGENTA:-}vim${RESET:-} the default editor? (y/N): " EDITOR_CHOICE </dev/tty; then + if [[ "$EDITOR_CHOICE" == "y" || "$EDITOR_CHOICE" == "Y" ]]; then + update_editor "vim" + editor_set=1 + fi + fi + fi +} diff --git a/scripts/lib_backup.sh b/scripts/lib_backup.sh new file mode 100644 index 00000000..6867fb6d --- /dev/null +++ b/scripts/lib_backup.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# Backup helper utilities shared by copy.sh (and future scripts). + +# Create a unique backup directory name with month, day, hours, and minutes. +get_backup_dirname() { + echo "back-up_$(date +"%m%d_%H%M")" +} + +# Move a directory to a timestamped backup alongside the original. +# Usage: backup_dir "/path/to/dir" [logfile] +backup_dir() { + local dir="$1" + local log="${2:-/dev/null}" + local backup_suffix + + [ -z "$dir" ] && return 1 + backup_suffix=$(get_backup_dirname) + mv "$dir" "${dir}-backup-${backup_suffix}" 2>&1 | tee -a "$log" +} + +# Cleanup old backups under ~/.config, keeping the newest for each base dir. +# mode: "auto" (no prompts) or "prompt" (asks before delete); log optional. +cleanup_backups() { + local mode="${1:-prompt}" + local log="${2:-/dev/null}" + local CONFIG_DIR="$HOME/.config" + local BACKUP_PREFIX="-backup" + + for DIR in "$CONFIG_DIR"/*; do + [ -d "$DIR" ] || continue + local BACKUP_DIRS=() + + for BACKUP in "$DIR"$BACKUP_PREFIX*; do + [ -d "$BACKUP" ] && BACKUP_DIRS+=("$BACKUP") + done + + [ ${#BACKUP_DIRS[@]} -gt 1 ] || continue + + # Determine latest backup by mtime + local latest_backup="${BACKUP_DIRS[0]}" + for BACKUP in "${BACKUP_DIRS[@]}"; do + [ "$BACKUP" -nt "$latest_backup" ] && latest_backup="$BACKUP" + done + + if [ "$mode" = "auto" ]; then + for BACKUP in "${BACKUP_DIRS[@]}"; do + if [ "$BACKUP" != "$latest_backup" ]; then + rm -rf "$BACKUP" + fi + done + echo "${INFO:-[INFO]} Express mode: trimmed backups for ${YELLOW:-}${DIR##*/}${RESET:-}, keeping ${MAGENTA:-}${latest_backup##*/}${RESET:-}." 2>&1 | tee -a "$log" + continue + fi + + printf "\n%s Found multiple backups for: %s\n" "${INFO:-[INFO]}" "${DIR##*/}" + echo "${YELLOW:-}Backups:${RESET:-}" + for BACKUP in "${BACKUP_DIRS[@]}"; do + echo " - ${BACKUP##*/}" + done + echo -n "${CAT:-[ACTION]} Delete older backups and keep only the latest? (y/N): " + read back_choice + if [[ "$back_choice" == [Yy]* ]]; then + for BACKUP in "${BACKUP_DIRS[@]}"; do + if [ "$BACKUP" != "$latest_backup" ]; then + rm -rf "$BACKUP" + echo "Deleted: ${BACKUP##*/}" + fi + done + echo "Kept: ${latest_backup##*/}" + fi + done +} diff --git a/scripts/lib_copy.sh b/scripts/lib_copy.sh new file mode 100644 index 00000000..fa1231c5 --- /dev/null +++ b/scripts/lib_copy.sh @@ -0,0 +1,362 @@ +#!/usr/bin/env bash +# Copy helpers split into phases to keep copy.sh lean. + +copy_phase1() { + local log="$1" + local dirs="fastfetch kitty rofi swaync" + for DIR2 in $dirs; do + local DIRPATH="$HOME/.config/$DIR2" + if [ -d "$DIRPATH" ]; then + while true; do + printf "\n${INFO:-[INFO]} Found ${YELLOW:-}$DIR2${RESET:-} config found in ~/.config/\n" + echo -n "${CAT:-[ACTION]} Do you want to replace ${YELLOW:-}$DIR2${RESET:-} config? (y/n): " + read DIR1_CHOICE + case "$DIR1_CHOICE" in + [Yy]*) BACKUP_DIR=$(get_backup_dirname) + mv "$DIRPATH" "$DIRPATH-backup-$BACKUP_DIR" 2>&1 | tee -a "$log" + echo -e "${NOTE:-[NOTE]} - Backed up $DIR2 to $DIRPATH-backup-$BACKUP_DIR." 2>&1 | tee -a "$log" + cp -r "config/$DIR2" "$HOME/.config/$DIR2" 2>&1 | tee -a "$log" + echo -e "${OK:-[OK]} - Replaced $DIR2 with new configuration." 2>&1 | tee -a "$log" + if [ "$DIR2" = "rofi" ]; then + if [ -d "$DIRPATH-backup-$BACKUP_DIR/themes" ]; then + for file in "$DIRPATH-backup-$BACKUP_DIR/themes"/*; do + [ -e "$file" ] || continue + cp -n "$file" "$HOME/.config/rofi/themes/" >>"$log" 2>&1 || true + done || true + fi + if [ -f "$DIRPATH-backup-$BACKUP_DIR/0-shared-fonts.rasi" ]; then + cp "$DIRPATH-backup-$BACKUP_DIR/0-shared-fonts.rasi" "$HOME/.config/rofi/0-shared-fonts.rasi" >>"$log" 2>&1 + fi + fi + break ;; + [Nn]*) echo -e "${NOTE:-[NOTE]} - Skipping ${YELLOW:-}$DIR2${RESET:-}" 2>&1 | tee -a "$log"; break ;; + *) echo -e "${WARN:-[WARN]} - Invalid choice. Please enter Y or N." ;; + esac + done + else + cp -r "config/$DIR2" "$HOME/.config/$DIR2" 2>&1 | tee -a "$log" + echo -e "${OK:-[OK]} - Copy completed for ${YELLOW:-}$DIR2${RESET:-}" 2>&1 | tee -a "$log" + fi + done +} + +copy_waybar() { + local log="$1" + local DIRW="waybar" + local DIRPATHw="$HOME/.config/$DIRW" + if [ -d "$DIRPATHw" ]; then + while true; do + echo -n "${CAT:-[ACTION]} Do you want to replace ${YELLOW:-}$DIRW${RESET:-} config? (y/n): " + read DIR1_CHOICE + case "$DIR1_CHOICE" in + [Yy]*) BACKUP_DIR=$(get_backup_dirname) + cp -r "$DIRPATHw" "$DIRPATHw-backup-$BACKUP_DIR" 2>&1 | tee -a "$log" + echo -e "${NOTE:-[NOTE]} - Backed up $DIRW to $DIRPATHw-backup-$BACKUP_DIR." 2>&1 | tee -a "$log" + rm -rf "$DIRPATHw" && cp -r "config/$DIRW" "$DIRPATHw" 2>&1 | tee -a "$log" + for file in "config" "style.css"; do + symlink="$DIRPATHw-backup-$BACKUP_DIR/$file" + target_file="$DIRPATHw/$file" + if [ -L "$symlink" ]; then + symlink_target=$(readlink "$symlink") + if [ -f "$symlink_target" ]; then + rm -f "$target_file" && cp -f "$symlink_target" "$target_file" + fi + fi + done + for dir in "$DIRPATHw-backup-$BACKUP_DIR/configs"/*; do + [ -e "$dir" ] || continue + if [ -d "$dir" ]; then + target_dir="$HOME/.config/waybar/configs/$(basename "$dir")" + [ -d "$target_dir" ] || cp -r "$dir" "$HOME/.config/waybar/configs/" + fi + done + for file in "$DIRPATHw-backup-$BACKUP_DIR/configs"/*; do + [ -e "$file" ] || continue + target_file="$HOME/.config/waybar/configs/$(basename "$file")" + [ -e "$target_file" ] || cp "$file" "$HOME/.config/waybar/configs/" + done || true + for file in "$DIRPATHw-backup-$BACKUP_DIR/style"/*; do + [ -e "$file" ] || continue + if [ -d "$file" ]; then + target_dir="$HOME/.config/waybar/style/$(basename "$file")" + [ -d "$target_dir" ] || cp -r "$file" "$HOME/.config/waybar/style/" + else + target_file="$HOME/.config/waybar/style/$(basename "$file")" + [ -e "$target_file" ] || cp "$file" "$HOME/.config/waybar/style/" + fi + done || true + BACKUP_FILEw="$DIRPATHw-backup-$BACKUP_DIR/UserModules" + [ -f "$BACKUP_FILEw" ] && cp -f "$BACKUP_FILEw" "$DIRPATHw/UserModules" + break ;; + [Nn]*) echo -e "${NOTE:-[NOTE]} - Skipping ${YELLOW:-}$DIRW${RESET:-} config replacement." 2>&1 | tee -a "$log"; break ;; + *) echo -e "${WARN:-[WARN]} - Invalid choice. Please enter Y or N." ;; + esac + done + else + cp -r "config/$DIRW" "$DIRPATHw" 2>&1 | tee -a "$log" + echo -e "${OK:-[OK]} - Copy completed for ${YELLOW:-}$DIRW${RESET:-}" 2>&1 | tee -a "$log" + fi +} + +copy_phase2() { + local log="$1" + local DIR="btop cava hypr Kvantum qt5ct qt6ct swappy wallust wlogout" + for DIR_NAME in $DIR; do + local DIRPATH="$HOME/.config/$DIR_NAME" + if [ -d "$DIRPATH" ]; then + echo -e "\n${NOTE:-[NOTE]} - Config for ${YELLOW:-}$DIR_NAME${RESET:-} found, attempting to back up." + BACKUP_DIR=$(get_backup_dirname) + mv "$DIRPATH" "$DIRPATH-backup-$BACKUP_DIR" 2>&1 | tee -a "$log" + fi + if [ -d "config/$DIR_NAME" ]; then + cp -r "config/$DIR_NAME/" "$HOME/.config/$DIR_NAME" 2>&1 | tee -a "$log" + echo "${OK:-[OK]} - Copy of config for ${YELLOW:-}$DIR_NAME${RESET:-} completed!" 2>&1 | tee -a "$log" + else + echo "${ERROR:-[ERROR]} - Directory config/$DIR_NAME does not exist to copy." 2>&1 | tee -a "$log" + fi + done + install_terminal_configs "$log" +} + +# Restore Animations and Monitor Profiles plus key hypr files from backup +restore_hypr_assets() { + local log="$1" + local express_mode="$2" + + local HYPR_DIR="$HOME/.config/hypr" + local BACKUP_DIR + BACKUP_DIR=$(get_backup_dirname) + local BACKUP_HYPR_PATH="$HYPR_DIR-backup-$BACKUP_DIR" + + if [ -d "$BACKUP_HYPR_PATH" ]; then + if [ "$express_mode" -eq 1 ]; then + echo "${NOTE:-[NOTE]} Express mode: skipping automatic restoration of animations and monitor profiles." 2>&1 | tee -a "$log" + return + fi + + echo -e "\n${NOTE:-[NOTE]} Restoring ${SKY_BLUE:-}Animations & Monitor Profiles${RESET:-} into ${YELLOW:-}$HYPR_DIR${RESET:-}..." + + local DIR_B=("Monitor_Profiles" "animations" "wallpaper_effects") + for DIR_RESTORE in "${DIR_B[@]}"; do + local BACKUP_SUBDIR="$BACKUP_HYPR_PATH/$DIR_RESTORE" + if [ -d "$BACKUP_SUBDIR" ]; then + cp -r "$BACKUP_SUBDIR" "$HYPR_DIR/" 2>&1 | tee -a "$log" + echo "${OK:-[OK]} - Restored directory: ${MAGENTA:-}$DIR_RESTORE${RESET:-}" 2>&1 | tee -a "$log" + fi + done + + local FILE_B=("monitors.conf" "workspaces.conf") + for FILE_RESTORE in "${FILE_B[@]}"; do + local BACKUP_FILE="$BACKUP_HYPR_PATH/$FILE_RESTORE" + if [ -f "$BACKUP_FILE" ]; then + cp "$BACKUP_FILE" "$HYPR_DIR/$FILE_RESTORE" 2>&1 | tee -a "$log" + echo "${OK:-[OK]} - Restored file: ${MAGENTA:-}$FILE_RESTORE${RESET:-}" 2>&1 | tee -a "$log" + fi + done + fi +} + +# Helper to extract overlay additions/disables from previous user file vs base +compose_overlay_from_backup() { + local type="$1" # startup|windowrules + local base_file="$2" + local old_user_file="$3" + local new_user_file="$4" + local disable_file="$5" + + mkdir -p "$(dirname "$new_user_file")" + : >"$new_user_file" + : >"$disable_file" + + if [ "$type" = "startup" ]; then + grep -E '^\s*exec-once\s*=' "$old_user_file" | sed -E 's/^\s+//;s/\s+$//' | sort -u >"$old_user_file.tmp.exec" + grep -E '^\s*exec-once\s*=' "$base_file" | sed -E 's/^\s+//;s/\s+$//' | sort -u >"$base_file.tmp.exec" + comm -23 "$old_user_file.tmp.exec" "$base_file.tmp.exec" >"$new_user_file" + grep -E '^\s*#\s*exec-once\s*=' "$old_user_file" | + sed -E 's/^\s*#\s*exec-once\s*=\s*//' | + sed -E 's/^\s+//;s/\s+$//' | + grep -Ev '^\$scriptsDir/KeybindsLayoutInit\.sh$' | + sort -u >"$disable_file" + rm -f "$old_user_file.tmp.exec" "$base_file.tmp.exec" + elif [ "$type" = "windowrules" ]; then + grep -E '^(windowrule|layerrule)\s*=' "$old_user_file" | sed -E 's/^\s+//;s/\s+$//' | sort -u >"$old_user_file.tmp.rules" + grep -E '^(windowrule|layerrule)\s*=' "$base_file" | sed -E 's/^\s+//;s/\s+$//' | sort -u >"$base_file.tmp.rules" + comm -23 "$old_user_file.tmp.rules" "$base_file.tmp.rules" >"$new_user_file" + grep -E '^\s*#\s*(windowrule|layerrule)\s*=' "$old_user_file" | sed -E 's/^\s*#\s*//' | sed -E 's/^\s+//;s/\s+$//' | sort -u >"$disable_file" + rm -f "$old_user_file.tmp.rules" "$base_file.tmp.rules" + fi +} + +restore_user_configs() { + local log="$1" + local express_mode="$2" + + local DIRPATH="$HOME/.config/hypr" + local BACKUP_DIR + BACKUP_DIR=$(get_backup_dirname) + local BACKUP_DIR_PATH="$DIRPATH-backup-$BACKUP_DIR/UserConfigs" + + if [ -z "$BACKUP_DIR" ]; then + echo "${ERROR:-[ERROR]} - Backup directory name is empty. Exiting." 2>&1 | tee -a "$log" + exit 1 + fi + + if [ -d "$BACKUP_DIR_PATH" ] && [ "$express_mode" -eq 1 ]; then + echo "${NOTE:-[NOTE]} Express mode: skipping UserConfigs restoration prompts." 2>&1 | tee -a "$log" + return + fi + + if [ -d "$BACKUP_DIR_PATH" ] && [ "$express_mode" -eq 0 ]; then + local VERSION_FILE + VERSION_FILE=$(find "$DIRPATH" -maxdepth 1 -name "v*.*.*" | head -n 1) + local CURRENT_VERSION="999.9.9" + if [ -n "$VERSION_FILE" ]; then + CURRENT_VERSION=$(basename "$VERSION_FILE" | sed 's/^v//') + fi + + local TARGET_VERSION="2.3.19" + + echo -e "${NOTE:-[NOTE]} Restoring previous ${MAGENTA:-}User-Configs${RESET:-}... " 2>&1 | tee -a "$log" + printf "${WARNING:-}\ + █▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█\n\ + NOTES for RESTORING PREVIOUS CONFIGS\n\ + █▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█\n\n\ + The 'UserConfigs' directory is for all your personal settings.\n\ + Files in this directory will override the default configurations,\n\ + so your customizations are not lost when you update.\n\ +" >&2 + + if version_gte "$CURRENT_VERSION" "$TARGET_VERSION"; then + read -r -p "${CAT:-[ACTION]} Do you want to restore your previous UserConfigs directory? (Y/n): " restore_userconfigs_dir + if [[ "$restore_userconfigs_dir" != [Nn]* ]]; then + echo "${NOTE:-[NOTE]} Restoring UserConfigs directory..." 2>&1 | tee -a "$log" + rsync -a "$BACKUP_DIR_PATH/" "$DIRPATH/UserConfigs/" 2>&1 | tee -a "$log" + echo "${OK:-[OK]} - UserConfigs directory restored." 2>&1 | tee -a "$log" + else + echo "${NOTE:-[NOTE]} - Skipped restoring UserConfigs." 2>&1 | tee -a "$log" + fi + else + echo -e "${NOTE:-[NOTE]} Detected version ${YELLOW:-}v$CURRENT_VERSION${RESET:-} (older than v$TARGET_VERSION). Using legacy restoration mode." 2>&1 | tee -a "$log" + + local FILES_TO_RESTORE=( + "01-UserDefaults.conf" + "ENVariables.conf" + "LaptopDisplay.conf" + "Laptops.conf" + "Startup_Apps.conf" + "UserDecorations.conf" + "UserAnimations.conf" + "UserKeybinds.conf" + "UserSettings.conf" + "WindowRules.conf" + ) + + for FILE_NAME in "${FILES_TO_RESTORE[@]}"; do + local BACKUP_FILE="$BACKUP_DIR_PATH/$FILE_NAME" + if [ -f "$BACKUP_FILE" ]; then + if [ "$FILE_NAME" = "Startup_Apps.conf" ]; then + compose_overlay_from_backup "startup" "$DIRPATH/configs/Startup_Apps.conf" "$BACKUP_FILE" "$DIRPATH/UserConfigs/Startup_Apps.conf" "$DIRPATH/UserConfigs/Startup_Apps.disable" + echo "${OK:-[OK]} - Migrated overlay for ${YELLOW:-}$FILE_NAME${RESET:-}" 2>&1 | tee -a "$log" + continue + fi + if [ "$FILE_NAME" = "WindowRules.conf" ]; then + compose_overlay_from_backup "windowrules" "$DIRPATH/configs/WindowRules.conf" "$BACKUP_FILE" "$DIRPATH/UserConfigs/WindowRules.conf" "$DIRPATH/UserConfigs/WindowRules.disable" + echo "${OK:-[OK]} - Migrated overlay for ${YELLOW:-}$FILE_NAME${RESET:-}" 2>&1 | tee -a "$log" + continue + fi + + printf "\n${INFO:-[INFO]} Found ${YELLOW:-}$FILE_NAME${RESET:-} in hypr backup...\n" + read -r -p "${CAT:-[ACTION]} Do you want to restore ${YELLOW:-}$FILE_NAME${RESET:-} from backup? (Y/n): " file_restore + + if [[ "$file_restore" != [Nn]* ]]; then + if cp "$BACKUP_FILE" "$DIRPATH/UserConfigs/$FILE_NAME"; then + echo "${OK:-[OK]} - $FILE_NAME restored!" 2>&1 | tee -a "$log" + else + echo "${ERROR:-[ERROR]} - Failed to restore $FILE_NAME!" 2>&1 | tee -a "$log" + fi + else + echo "${NOTE:-[NOTE]} - Skipped restoring $FILE_NAME." 2>&1 | tee -a "$log" + fi + fi + done + fi + fi +} + +restore_user_scripts() { + local log="$1" + local express_mode="$2" + + local DIRSHPATH="$HOME/.config/hypr" + local BACKUP_DIR + BACKUP_DIR=$(get_backup_dirname) + local BACKUP_DIR_PATH_S="$DIRSHPATH-backup-$BACKUP_DIR/UserScripts" + local SCRIPTS_TO_RESTORE=("RofiBeats.sh" "Weather.py" "Weather.sh") + + if [ -d "$BACKUP_DIR_PATH_S" ] && [ "$express_mode" -eq 1 ]; then + echo "${NOTE:-[NOTE]} Express mode: skipping UserScripts restoration prompts." 2>&1 | tee -a "$log" + return + fi + + if [ -d "$BACKUP_DIR_PATH_S" ] && [ "$express_mode" -eq 0 ]; then + echo -e "${NOTE:-[NOTE]} Restoring previous ${MAGENTA:-}User-Scripts${RESET:-}..." 2>&1 | tee -a "$log" + + for SCRIPT_NAME in "${SCRIPTS_TO_RESTORE[@]}"; do + local BACKUP_SCRIPT="$BACKUP_DIR_PATH_S/$SCRIPT_NAME" + if [ -f "$BACKUP_SCRIPT" ]; then + printf "\n${INFO:-[INFO]} Found ${YELLOW:-}$SCRIPT_NAME${RESET:-} in hypr backup...\n" + read -r -p "${CAT:-[ACTION]} Do you want to restore ${YELLOW:-}$SCRIPT_NAME${RESET:-} from backup? (y/N): " script_restore + + if [[ "$script_restore" == [Yy]* ]]; then + if cp "$BACKUP_SCRIPT" "$DIRSHPATH/UserScripts/$SCRIPT_NAME"; then + echo "${OK:-[OK]} - $SCRIPT_NAME restored!" 2>&1 | tee -a "$log" + else + echo "${ERROR:-[ERROR]} - Failed to restore $SCRIPT_NAME!" 2>&1 | tee -a "$log" + fi + else + echo "${NOTE:-[NOTE]} - Skipped restoring $SCRIPT_NAME." 2>&1 | tee -a "$log" + fi + fi + done + fi +} + +restore_hypr_files() { + local log="$1" + local express_mode="$2" + + local DIRPATH="$HOME/.config/hypr" + local BACKUP_DIR + BACKUP_DIR=$(get_backup_dirname) + local BACKUP_DIR_PATH_F="$DIRPATH-backup-$BACKUP_DIR" + local FILES_2_RESTORE=("hyprlock.conf" "hypridle.conf") + + if [ -d "$BACKUP_DIR_PATH_F" ] && [ "$express_mode" -eq 1 ]; then + echo "${NOTE:-[NOTE]} Express mode: skipping individual hypr file restoration prompts." 2>&1 | tee -a "$log" + return + fi + + if [ -d "$BACKUP_DIR_PATH_F" ] && [ "$express_mode" -eq 0 ]; then + echo -e "${NOTE:-[NOTE]} Restoring some files in ${MAGENTA:-}$HOME/.config/hypr directory${RESET:-}..." 2>&1 | tee -a "$log" + + for FILE_RESTORE in "${FILES_2_RESTORE[@]}"; do + local BACKUP_FILE="$BACKUP_DIR_PATH_F/$FILE_RESTORE" + if [ -f "$BACKUP_FILE" ]; then + echo -e "\n${INFO:-[INFO]} Found ${YELLOW:-}$FILE_RESTORE${RESET:-} in hypr backup..." + read -r -p "${CAT:-[ACTION]} Do you want to restore ${YELLOW:-}$FILE_RESTORE${RESET:-} from backup? (y/N): " file2restore + + if [[ "$file2restore" == [Yy]* ]]; then + if cp "$BACKUP_FILE" "$DIRPATH/$FILE_RESTORE"; then + echo "${OK:-[OK]} - $FILE_RESTORE restored!" 2>&1 | tee -a "$log" + else + echo "${ERROR:-[ERROR]} - Failed to restore $FILE_RESTORE!" 2>&1 | tee -a "$log" + fi + else + echo "${NOTE:-[NOTE]} - Skipped restoring $FILE_RESTORE." 2>&1 | tee -a "$log" + fi + else + echo "${ERROR:-[ERROR]} - Backup file $BACKUP_FILE does not exist." 2>&1 | tee -a "$log" + fi + done + fi +} diff --git a/scripts/lib_detect.sh b/scripts/lib_detect.sh new file mode 100644 index 00000000..5cb26c1b --- /dev/null +++ b/scripts/lib_detect.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +# Detection and environment adjustment helpers shared by copy.sh. + +# Nvidia tweaks: uncomments envs and adjusts hardware cursor setting. +detect_nvidia_adjust() { + local log="$1" + if lspci -k | grep -A 2 -E "(VGA|3D)" | grep -iq nvidia; then + echo "${INFO:-[INFO]} Nvidia GPU detected. Setting up proper env's and configs" 2>&1 | tee -a "$log" || true + sed -i '/env = LIBVA_DRIVER_NAME,nvidia/s/^#//' config/hypr/configs/ENVariables.conf + sed -i '/env = __GLX_VENDOR_LIBRARY_NAME,nvidia/s/^#//' config/hypr/configs/ENVariables.conf + sed -i '/env = NVD_BACKEND,direct/s/^#//' config/hypr/configs/ENVariables.conf + sed -i '/env = GSK_RENDERER,ngl/s/^#//' config/hypr/configs/ENVariables.conf + sed -i 's/^\([[:space:]]*no_hardware_cursors[[:space:]]*=[[:space:]]*\)2/\1 1/' config/hypr/configs/SystemSettings.conf + fi +} + +# VM tweaks: enable software renderer envs and virtual monitor defaults. +detect_vm_adjust() { + local log="$1" + if hostnamectl | grep -q 'Chassis: vm'; then + echo "${INFO:-[INFO]} System is running in a virtual machine. Setting up proper env's and configs" 2>&1 | tee -a "$log" || true + sed -i 's/^\([[:space:]]*no_hardware_cursors[[:space:]]*=[[:space:]]*\)2/\1 1/' config/hypr/configs/SystemSettings.conf + sed -i '/env = WLR_RENDERER_ALLOW_SOFTWARE,1/s/^#//' config/hypr/configs/ENVariables.conf + sed -i '/monitor = Virtual-1, 1920x1080@60,auto,1/s/^#//' config/hypr/monitors.conf + fi +} + +# NixOS tweaks: ensure polkit overlay is enabled and default disabled. +detect_nixos_adjust() { + local log="$1" + if hostnamectl | grep -q 'Operating System: NixOS'; then + echo "${INFO:-[INFO]} NixOS Distro Detected. Setting up proper env's and configs." 2>&1 | tee -a "$log" || true + local OVERLAY_SA="config/hypr/configs/Startup_Apps.conf" + local DISABLE_SA="config/hypr/configs/Startup_Apps.disable" + mkdir -p "$(dirname "$OVERLAY_SA")" + touch "$OVERLAY_SA" "$DISABLE_SA" + grep -qx 'exec-once = $scriptsDir/Polkit-NixOS.sh' "$OVERLAY_SA" || echo 'exec-once = $scriptsDir/Polkit-NixOS.sh' >>"$OVERLAY_SA" + grep -qx '\$scriptsDir/Polkit.sh' "$DISABLE_SA" || echo '$scriptsDir/Polkit.sh' >>"$DISABLE_SA" + fi +} + +# Decide waybar config/style based on chassis type. Echoes chosen config path. +detect_waybar_config() { + if hostnamectl | grep -q 'Chassis: desktop'; then + echo "desktop" + else + echo "laptop" + fi +} diff --git a/scripts/lib_prompts.sh b/scripts/lib_prompts.sh new file mode 100644 index 00000000..bf6fafd7 --- /dev/null +++ b/scripts/lib_prompts.sh @@ -0,0 +1,249 @@ +#!/usr/bin/env bash +# User interaction helpers extracted from copy.sh. Each helper echoes state or sets +# globals deliberately to minimize side effects. + +# Detect keyboard layout via localectl or setxkbmap. +prompt_detect_layout() { + if command -v localectl >/dev/null 2>&1; then + local layout + layout=$(localectl status --no-pager | awk '/X11 Layout/ {print $3}') + [ -n "$layout" ] && { echo "$layout"; return; } + fi + if command -v setxkbmap >/dev/null 2>&1; then + local layout + layout=$(setxkbmap -query | awk '/layout/ {print $2}') + [ -n "$layout" ] && { echo "$layout"; return; } + fi + echo "(unset)" +} + +# Confirm or set keyboard layout; writes to SystemSettings.conf. +prompt_keyboard_layout() { + local layout="$1" + local log="$2" + + if [ "$layout" = "(unset)" ]; then + while true; do + printf "\n%.0s" {1..1} + print_color $WARNING "\n █▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█ + STOP AND READ + █▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█ + + !!! IMPORTANT WARNING !!! + +The Default Keyboard Layout could not be detected +You need to set it Manually + + !!! WARNING !!! + +Setting a wrong Keyboard Layout will cause Hyprland to crash +If you are not sure, just type ${YELLOW}us${RESET} +${SKYBLUE}You can change later in ~/.config/hypr/UserConfigs/UserSettings.conf${RESET} + +${MAGENTA} NOTE:${RESET} +• You can also set more than 2 keyboard layouts +• For example: ${YELLOW}us, kr, gb, ru${RESET} +" + printf "\n%.0s" {1..1} + + echo -n "${CAT} - Please enter the correct keyboard layout: " + read new_layout + + if [ -n "$new_layout" ]; then + layout="$new_layout" + break + else + echo "${CAT} Please enter a keyboard layout." + fi + done + fi + + printf "${NOTE} Detecting keyboard layout to prepare proper Hyprland Settings\n" + while true; do + printf "${INFO} Current keyboard layout is ${MAGENTA}$layout${RESET}\n" + echo -n "${CAT} Is this correct? [y/n] " + read keyboard_layout + case $keyboard_layout in + [yY]) + awk -v layout="$layout" '/kb_layout/ {$0 = " kb_layout = " layout} 1' config/hypr/configs/SystemSettings.conf >temp.conf + mv temp.conf config/hypr/configs/SystemSettings.conf + echo "${NOTE} kb_layout ${MAGENTA}$layout${RESET} configured in settings." 2>&1 | tee -a "$log" + break + ;; + [nN]) + printf "\n%.0s" {1..2} + print_color $WARNING " + █▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█ + STOP AND READ + █▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█ + + !!! IMPORTANT WARNING !!! + +The Default Keyboard Layout could not be detected +You need to set it Manually + + !!! WARNING !!! + +Setting a wrong Keyboard Layout will cause Hyprland to crash +If you are not sure, just type ${YELLOW}us${RESET} +${SKYBLUE}You can change later in ~/.config/hypr/UserConfigs/UserSettings.conf${RESET} + +${MAGENTA} NOTE:${RESET} +• You can also set more than 2 keyboard layouts +• For example: ${YELLOW}us, kr, gb, ru${RESET} +" + printf "\n%.0s" {1..1} + echo -n "${CAT} - Please enter the correct keyboard layout: " + read new_layout + awk -v new_layout="$new_layout" '/kb_layout/ {$0 = " kb_layout = " new_layout} 1' config/hypr/configs/SystemSettings.conf >temp.conf + mv temp.conf config/hypr/configs/SystemSettings.conf + echo "${OK} kb_layout $new_layout configured in settings." 2>&1 | tee -a "$log" + break + ;; + *) + echo "${ERROR} Please enter either 'y' or 'n'." + ;; + esac + done +} + +# Prompt for resolution choice; echoes "< 1440p" or "≥ 1440p". +prompt_resolution_choice() { + local choice + while true; do + echo "${INFO:-[INFO]} Select monitor resolution for scaling:" + echo " 1) < 1440p (lower DPI; smaller displays)" + echo " 2) ≥ 1440p (default; 1440p/2k/4k)" + + if ! read -r -p "${CAT} Enter the number of your choice (1 or 2): " choice </dev/tty; then + echo "${ERROR} Unable to read input (tty unavailable)." + continue + fi + echo "${INFO:-[INFO]} You entered: '$choice'" + case "$choice" in + 1) echo "< 1440p"; return ;; + 2) echo "≥ 1440p"; return ;; + *) echo "${ERROR} Invalid choice. Please enter 1 for < 1440p or 2 for ≥ 1440p." ;; + esac + done +} + +# Prompt for 12H clock; sets waybar/hyprlock/SDDM changes when accepted. +prompt_clock_12h() { + local log="$1" + while true; do + echo -e "${NOTE} ${SKY_BLUE} By default, KooL's Dots are configured in 24H clock format." + echo -n "$CAT Do you want to change to 12H (AM/PM) clock format? (y/n): " + read answer + answer=$(echo "$answer" | tr '[:upper:]' '[:lower:]') + if [[ "$answer" == "y" ]]; then + # waybar clocks + sed -i 's#^\(\s*\)//\("format": " {:%I:%M %p}",\) #\1\2 #g' config/waybar/Modules 2>&1 | tee -a "$log" + sed -i 's#^\(\s*\)\("format": " {:%H:%M:%S}",\) #\1//\2#g' config/waybar/Modules 2>&1 | tee -a "$log" + sed -i 's#^\(\s*\)\("format": " {:%H:%M}",\) #\1//\2#g' config/waybar/Modules 2>&1 | tee -a "$log" + sed -i 's#^\(\s*\)//\("format": "{:%I:%M %p - %d/%b}",\) #\1\2#g' config/waybar/Modules 2>&1 | tee -a "$log" + sed -i 's#^\(\s*\)\("format": "{:%H:%M - %d/%b}",\) #\1//\2#g' config/waybar/Modules 2>&1 | tee -a "$log" + sed -i 's#^\(\s*\)//\("format": "{:%B | %a %d, %Y | %I:%M %p}",\) #\1\2#g' config/waybar/Modules 2>&1 | tee -a "$log" + sed -i 's#^\(\s*\)\("format": "{:%B | %a %d, %Y | %H:%M}",\) #\1//\2#g' config/waybar/Modules 2>&1 | tee -a "$log" + sed -i 's#^\(\s*\)//\("format": "{:%A, %I:%M %P}",\) #\1\2#g' config/waybar/Modules 2>&1 | tee -a "$log" + sed -i 's#^\(\s*\)\("format": "{:%a %d | %H:%M}",\) #\1//\2#g' config/waybar/Modules 2>&1 | tee -a "$log" + + # hyprlock + local HYPRLOCK_FILE="config/hypr/hyprlock.conf" + if [ ! -f "$HYPRLOCK_FILE" ] && [ -f "config/hypr/hyprlock-1080p.conf" ]; then + HYPRLOCK_FILE="config/hypr/hyprlock-1080p.conf" + fi + if [ -f "$HYPRLOCK_FILE" ]; then + sed -i 's/^\s*text = cmd\[update:1000\] echo \"\$(date +\"%H\")\"/# &/' "$HYPRLOCK_FILE" 2>&1 | tee -a "$log" + sed -i 's/^\(\s*\)# *text = cmd\[update:1000\] echo \"\$(date +\"%I\")\" #AM\/PM/\1 text = cmd\[update:1000\] echo \"\$(date +\"%I\")\" #AM\/PM/' "$HYPRLOCK_FILE" 2>&1 | tee -a "$log" + sed -i 's/^\s*text = cmd\[update:1000\] echo \"\$(date +\"%S\")\"/# &/' "$HYPRLOCK_FILE" 2>&1 | tee -a "$log" + sed -i 's/^\(\s*\)# *text = cmd\[update:1000\] echo \"\$(date +\"%S %p\")\" #AM\/PM/\1 text = cmd\[update:1000\] echo \"\$(date +\"%S %p\")\" #AM\/PM/' "$HYPRLOCK_FILE" 2>&1 | tee -a "$log" + else + echo "${WARN} hyprlock template not found; skipping 12H lock format edits" 2>&1 | tee -a "$log" + fi + + if [ "${EXPRESS_MODE:-0}" -eq 0 ]; then + apply_sddm_12h_format "/usr/share/sddm/themes/simple-sddm" "$log" + apply_sddm_12h_format "/usr/share/sddm/themes/simple_sddm_2" "$log" + apply_sddm_12h_format_sequoia "/usr/share/sddm/themes/sequoia_2" "$log" + else + echo "${NOTE:-[NOTE]} Express mode: skipping SDDM 12H edits to avoid sudo prompts." 2>&1 | tee -a "$log" + fi + echo "${OK} 12H format set on waybar clocks succesfully." 2>&1 | tee -a "$log" + return + elif [[ "$answer" == "n" ]]; then + echo "${NOTE} You chose not to change to 12H format." 2>&1 | tee -a "$log" + return + else + echo "${ERROR} Invalid choice. Please enter y for yes or n for no." + fi + done +} + +apply_sddm_12h_format() { + local sddm_directory="$1" + local log="$2" + if [ -d "$sddm_directory" ]; then + echo "Editing ${SKY_BLUE}$sddm_directory${RESET} to 12H format" 2>&1 | tee -a "$log" + if ! sudo -n sed -i 's|^## HourFormat="hh:mm AP"|HourFormat="hh:mm AP"|' "$sddm_directory/theme.conf" 2>&1 | tee -a "$log"; then + echo "${WARN:-[WARN]} Skipping SDDM 12H edit (sudo password required)." 2>&1 | tee -a "$log" + return + fi + sudo -n sed -i 's|^HourFormat="HH:mm"|## HourFormat="HH:mm"|' "$sddm_directory/theme.conf" 2>&1 | tee -a "$log" || true + fi +} + +apply_sddm_12h_format_sequoia() { + local sddm_directory="$1" + local log="$2" + if [ -d "$sddm_directory" ]; then + echo "${YELLOW}sddm sequoia_2${RESET} theme exists. Editing to 12H format" 2>&1 | tee -a "$log" + if ! sudo -n sed -i 's|^clockFormat="HH:mm"|## clockFormat="HH:mm"|' "$sddm_directory/theme.conf" 2>&1 | tee -a "$log"; then + echo "${WARN:-[WARN]} Skipping sequoia SDDM 12H edit (sudo password required)." 2>&1 | tee -a "$log" + return + fi + if ! grep -q 'clockFormat="hh:mm AP"' "$sddm_directory/theme.conf"; then + sudo -n sed -i '/^clockFormat=/a clockFormat="hh:mm AP"' "$sddm_directory/theme.conf" 2>&1 | tee -a "$log" || true + fi + echo "${OK} 12H format set to SDDM successfully." 2>&1 | tee -a "$log" + fi +} + + +# Express upgrade confirmation; may set EXPRESS_MODE=1. +prompt_express_upgrade() { + local express_supported="$1" + local log="$2" + if [ "$EXPRESS_MODE" -eq 1 ] && [ "$express_supported" -eq 0 ]; then + echo "${NOTE} Express mode requires installed dotfiles v${MIN_EXPRESS_VERSION} or newer. Continuing with standard upgrade prompts." 2>&1 | tee -a "$log" + EXPRESS_MODE=0 + return + fi + if [ "$UPGRADE_MODE" -eq 1 ] && [ "$EXPRESS_MODE" -eq 0 ]; then + if [ "$express_supported" -eq 0 ]; then + echo "${NOTE} Express mode requires installed dotfiles v${MIN_EXPRESS_VERSION} or newer. Continuing with standard upgrade prompts." 2>&1 | tee -a "$log" + else + while true; do + echo "${NOTE} Express mode skips config restore prompts, SDDM/background questions, and trims old backups." + if ! read -r -p "${CAT} Do you want to continue with EXPRESS upgrade mode? (y/N): " express_choice </dev/tty; then + echo "${ERROR} Unable to read input for express choice; defaulting to standard prompts." 2>&1 | tee -a "$log" + break + fi + case "$express_choice" in + [Yy]) + EXPRESS_MODE=1 + echo "${INFO} Express mode enabled for this upgrade." 2>&1 | tee -a "$log" + break + ;; + [Nn] | "") + echo "${NOTE} Continuing with standard upgrade prompts." 2>&1 | tee -a "$log" + break + ;; + *) + echo "${WARN} Please answer y or n." + ;; + esac + done + fi + fi +} diff --git a/scripts/lib_update.sh b/scripts/lib_update.sh new file mode 100644 index 00000000..0a70dff0 --- /dev/null +++ b/scripts/lib_update.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash + +# run_repo_update +# Arguments: +# $1 - expected repository root (typically SCRIPT_DIR from copy.sh) +# Behavior: +# * Verifies the script is executed from Hyprland-Dots root. +# * Stashes local changes (including untracked), pulls latest changes. +# * Shows progress, reports errors, and summarizes results. +# * Waits for user input before returning control to caller. +run_repo_update() { + local repo_dir="${1:-$(pwd)}" + local expected_name="Hyprland-Dots" + local log_dir="$repo_dir/Copy-Logs" + local log_file="$log_dir/update-$(date +%d-%H%M%S)_git.log" + + mkdir -p "$log_dir" + + echo "${INFO} Starting repository update..." | tee -a "$log_file" + + if [ ! -d "$repo_dir" ] || [ "$(basename "$repo_dir")" != "$expected_name" ]; then + echo "${ERROR} This helper must be run from the $expected_name directory. Current: $(pwd)" | tee -a "$log_file" + read -n1 -s -r -p "Press any key to return to the menu..." + echo + return 1 + fi + + if [ "$PWD" != "$repo_dir" ]; then + echo "${INFO} Changing directory to $repo_dir" | tee -a "$log_file" + cd "$repo_dir" || { + echo "${ERROR} Failed to change directory to $repo_dir" | tee -a "$log_file" + read -n1 -s -r -p "Press any key to return to the menu..." + echo + return 1 + } + fi + + local head_before stash_msg pull_status=0 + head_before=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown") + + echo "${INFO} Checking working tree..." | tee -a "$log_file" + if git diff --quiet && git diff --cached --quiet; then + stash_msg="No local changes; no stash created." + echo "${NOTE} $stash_msg" | tee -a "$log_file" + else + echo "${INFO} Stashing local changes (tracked + untracked)..." | tee -a "$log_file" + if stash_output=$(git stash push -u 2>&1); then + stash_msg="Created stash: $(echo "$stash_output" | head -n1)" + echo "${OK} $stash_msg" | tee -a "$log_file" + else + echo "${ERROR} git stash failed. Details:" | tee -a "$log_file" + echo "$stash_output" | tee -a "$log_file" + read -n1 -s -r -p "Press any key to return to the menu..." + echo + return 1 + fi + fi + + echo "${INFO} Pulling latest changes..." | tee -a "$log_file" + if git pull --ff-only 2>&1 | tee -a "$log_file"; then + pull_status=0 + echo "${OK} Repository updated successfully." | tee -a "$log_file" + else + pull_status=$? + echo "${ERROR} git pull failed (exit $pull_status)." | tee -a "$log_file" + fi + + local head_after + head_after=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown") + + echo "----------------------------------------" | tee -a "$log_file" + echo "Summary:" | tee -a "$log_file" + echo " Repo : $repo_dir" | tee -a "$log_file" + echo " HEAD before : $head_before" | tee -a "$log_file" + echo " HEAD after : $head_after" | tee -a "$log_file" + echo " Stash : $stash_msg" | tee -a "$log_file" + echo " Pull status : $( [ $pull_status -eq 0 ] && echo success || echo failure )" | tee -a "$log_file" + echo "----------------------------------------" | tee -a "$log_file" + + read -n1 -s -r -p "Press any key to return to the main menu..." + echo + + return $pull_status +} |
