2025-04-16 16:47:59 +02:00

305 lines
8.6 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
# Constants
readonly DEFAULT_DIR="$HOME/.local/share/gnome-terminal-autotheme"
readonly DESKTOP_FILE_NAME="gnome-terminal-autotheme.desktop"
readonly AUTOSTART_DIR="$HOME/.config/autostart"
readonly AUTOSTART_TARGET="$AUTOSTART_DIR/$DESKTOP_FILE_NAME"
readonly SCRIPT_NAME="sync-terminal-theme.sh"
readonly VERSION="1.1.0"
# Colors for output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[0;33m'
readonly BLUE='\033[0;34m'
readonly NC='\033[0m' # No Color
# Utility functions
error() {
echo -e "${RED}[ERROR]${NC} $1" >&2
}
warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
check_installed() {
command -v "$1" >/dev/null 2>&1
}
stop_existing_processes() {
local pids
pids=$(pgrep -f "$SCRIPT_NAME" || true)
if [[ -n "$pids" ]]; then
info "Stopping existing theme sync processes..."
for pid in $pids; do
if kill -0 "$pid" 2>/dev/null; then
kill "$pid" && info "Stopped process $pid" || warning "Failed to stop process $pid"
fi
done
# Wait a moment for processes to terminate
sleep 0.5
fi
}
validate_path() {
local path="$1"
[[ -z "$path" ]] && { error "Path cannot be empty"; return 1; }
[[ "$path" == "/" ]] && { error "Cannot use root directory"; return 1; }
[[ "$path" =~ ^/etc ]] && { error "System directories not allowed"; return 1; }
return 0
}
is_gnome() {
[[ "$XDG_CURRENT_DESKTOP" =~ GNOME|gnome ]] || check_installed gnome-session
}
start_sync() {
info "Running sync script now in the background..."
nohup bash "$INSTALL_DIR/$SCRIPT_NAME" >/dev/null 2>&1 &
disown
success "Theme sync script running in background (PID: $!)"
}
check_integrity() {
local install_dir="${1:-$DEFAULT_DIR}"
info "Checking installation integrity in: $install_dir"
local desktop_file="$AUTOSTART_TARGET"
local target_exec
if [[ ! -f "$desktop_file" ]]; then
error "Autostart desktop entry not found: $desktop_file"
return 1
fi
# Extract Exec path from .desktop file
target_exec=$(grep -oP '^Exec=\K.+' "$desktop_file" | head -n1)
if [[ -z "$target_exec" ]]; then
error "No Exec entry found in desktop file"
return 1
fi
if [[ ! -f "$target_exec" ]]; then
error "Script file referenced in .desktop does not exist: $target_exec"
return 1
fi
if [[ ! -x "$target_exec" ]]; then
error "Script file is not executable: $target_exec"
return 1
fi
if ! head -n 1 "$target_exec" | grep -qE '^#!.*/bash'; then
error "Script does not start with a valid bash shebang: $target_exec"
return 1
fi
success "All basic integrity checks passed"
}
uninstall() {
local install_dir="$1"
local uninstall_choice="${2:-autostart}"
local theme_choice="${3:-d}"
info "Starting uninstallation..."
# Kill running process
stop_existing_processes
# Remove autostart entry
if [[ -f "$AUTOSTART_TARGET" ]]; then
rm -f -- "$AUTOSTART_TARGET" && success "Autostart entry removed" || error "Failed to remove autostart entry"
fi
# Apply final theme
case "${theme_choice,,}" in
l|light)
bash "$install_dir/$SCRIPT_NAME" light && success "Applied light theme" || error "Failed to apply light theme"
;;
d|dark)
bash "$install_dir/$SCRIPT_NAME" dark && success "Applied dark theme" || error "Failed to apply dark theme"
;;
*)
warning "No theme change applied"
;;
esac
# Remove installation directory if requested
if [[ "$uninstall_choice" =~ ^whole$ ]]; then
if [[ -d "$install_dir" ]]; then
rm -rf -- "$install_dir" && success "Installation directory removed" || error "Failed to remove directory"
else
warning "Installation directory not found"
fi
fi
success "Uninstallation complete"
}
install() {
local reinstall="${1:-false}"
local provided_path="${2:-}"
info "Starting installation..."
stop_existing_processes
# Check for gnome-terminal
if ! check_installed gnome-terminal; then
warning "gnome-terminal not found"
read -rp "Continue anyway? [y/N]: " proceed_terminal
[[ "${proceed_terminal,,}" =~ ^y ]] || { error "Aborted"; exit 1; }
fi
# Check for GNOME
if ! is_gnome; then
warning "GNOME not detected"
read -rp "Continue anyway? [y/N]: " proceed_gnome
[[ "${proceed_gnome,,}" =~ ^y ]] || { error "Aborted"; exit 1; }
fi
# Use provided path or prompt user
if [[ -n "$provided_path" ]]; then
INSTALL_DIR=$(realpath -m "$provided_path")
else
read -rp "Enter install path [default: $DEFAULT_DIR]: " install_dir
INSTALL_DIR=$(realpath -m "${install_dir:-$DEFAULT_DIR}")
fi
if ! validate_path "$INSTALL_DIR"; then
error "Invalid install path: $INSTALL_DIR"
exit 1
fi
# Create directory
mkdir -p -- "$INSTALL_DIR" || { error "Failed to create directory"; exit 1; }
# Verify source files exist
if [[ ! -f "$SCRIPT_NAME" || ! -f "$DESKTOP_FILE_NAME" ]]; then
error "Missing required files: $SCRIPT_NAME and $DESKTOP_FILE_NAME must be in current directory"
exit 1
fi
# Copy files
cp -- "$SCRIPT_NAME" "$INSTALL_DIR/" || { error "Failed to copy script"; exit 1; }
cp -- "$DESKTOP_FILE_NAME" "$INSTALL_DIR/" || { error "Failed to copy desktop file"; exit 1; }
# Make script executable
chmod +x "$INSTALL_DIR/$SCRIPT_NAME" || { error "Failed to make script executable"; exit 1; }
# Create autostart entry
mkdir -p -- "$AUTOSTART_DIR" || { error "Failed to create autostart directory"; exit 1; }
# Escape path for sed
local escaped_path=$(printf '%q' "$INSTALL_DIR/$SCRIPT_NAME")
sed "s|Exec=.*|Exec=$escaped_path|" "$INSTALL_DIR/$DESKTOP_FILE_NAME" > "$AUTOSTART_TARGET" || {
error "Failed to create autostart entry"
exit 1
}
success "Installation complete"
success "Script installed to: $INSTALL_DIR"
success "Autostart entry created: $AUTOSTART_TARGET"
# Start sync process
start_sync
}
main() {
echo -e "${BLUE}=== GNOME Terminal Auto Theme Sync v$VERSION ===${NC}"
# Check if installed
if [[ -f "$AUTOSTART_TARGET" ]]; then
local old_install_dir=$(grep -oP 'Exec=\K.+' "$AUTOSTART_TARGET" | sed "s|/$SCRIPT_NAME||")
if [[ -z "$old_install_dir" || ! -d "$old_install_dir" ]]; then
warning "Could not determine valid install path from autostart entry (you may want to look into it)"
info "Fallback to default installation directory"
old_install_dir="$DEFAULT_DIR"
if [[ ! -d "$old_install_dir" ]]; then
error "Default install directory does not exist: $old_install_dir"
exit 1
fi
fi
info "Theme watcher already installed at: $old_install_dir"
read -rp "Do you want to (u)ninstall, (r)einstall or check (i)ntegrity? [u/r/i/N]: " choice
case "${choice,,}" in
u)
# Loop until a valid choice is entered for uninstall
while true; do
read -rp "Uninstall (w)hole installation or (a)utostart entry? [w/a]: " uninstall_choice
case "${uninstall_choice,,}" in
a) uninstall_choice="autostart"; break ;;
w) uninstall_choice="whole"; break ;;
esac
echo "Invalid choice, please enter 'w' for whole or 'a' for autostart."
done
# Loop until a valid choice is entered for theme selection
while true; do
read -rp "Set terminal theme to (l)ight or (d)ark after uninstall? [l/d]: " theme_choice
case "${theme_choice,,}" in
l) theme_choice="l"; break ;;
d) theme_choice="d"; break ;;
esac
echo "Invalid choice, please enter 'l' for light or 'd' for dark."
done
uninstall "$old_install_dir" "$uninstall_choice" "$theme_choice"
exit 0
;;
r)
read -rp "Enter new install path [default: $DEFAULT_DIR]: " install_dir
INSTALL_DIR="${install_dir:-$DEFAULT_DIR}"
INSTALL_DIR=$(realpath -m "$INSTALL_DIR")
if [[ "$INSTALL_DIR" != "$old_install_dir" ]]; then
read -rp "The new install path is different from the old one ($old_install_dir). Remove old directory? [y/N]: " remove_old
if [[ "${remove_old,,}" =~ ^y ]]; then
if [[ -d "$old_install_dir" ]]; then
rm -rf -- "$old_install_dir" && success "Old installation directory removed" || error "Failed to remove old directory"
else
warning "Old installation directory not found"
fi
fi
fi
install true "$INSTALL_DIR"
;;
i)
check_integrity "$old_install_dir"
exit $?
;;
*)
info "No changes made"
exit 0
;;
esac
else
install
fi
}
main