348 lines
12 KiB
Bash
Executable File
348 lines
12 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# ============================================================================
|
||
# ADLee Theme System-wide Deployment Script
|
||
# ============================================================================
|
||
# Deploys the zsh theme system-wide via symlinks
|
||
#
|
||
# Usage:
|
||
# sudo ./deploy-zshtheme-systemwide.sh # Interactive mode
|
||
# sudo ./deploy-zshtheme-systemwide.sh --all # All users with oh-my-zsh
|
||
# sudo ./deploy-zshtheme-systemwide.sh --current # Current user + root only
|
||
# sudo ./deploy-zshtheme-systemwide.sh --status # Show deployment status
|
||
# sudo ./deploy-zshtheme-systemwide.sh --force # Force replace existing links
|
||
|
||
set -euo pipefail
|
||
|
||
# ============================================================================
|
||
# Load Configuration
|
||
# ============================================================================
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
DOTFILES_CONF="${SCRIPT_DIR}/../dotfiles.conf"
|
||
[[ -f "$DOTFILES_CONF" ]] || DOTFILES_CONF="$HOME/.dotfiles/dotfiles.conf"
|
||
|
||
if [[ -f "$DOTFILES_CONF" ]]; then
|
||
source "$DOTFILES_CONF"
|
||
else
|
||
DOTFILES_DIR="$HOME/.dotfiles"
|
||
ZSH_THEME_NAME="adlee"
|
||
fi
|
||
|
||
# ============================================================================
|
||
# Configuration
|
||
# ============================================================================
|
||
|
||
MASTER_THEME_DIR="/usr/local/share/zsh/themes"
|
||
THEME_FILE="${ZSH_THEME_NAME}.zsh-theme"
|
||
MASTER_THEME_PATH="${MASTER_THEME_DIR}/${THEME_FILE}"
|
||
SOURCE_THEME="${DOTFILES_DIR}/zsh/themes/${THEME_FILE}"
|
||
FORCE_REPLACE=false
|
||
|
||
# Colors
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
RED='\033[0;31m'
|
||
BLUE='\033[0;34m'
|
||
CYAN='\033[0;36m'
|
||
NC='\033[0m'
|
||
|
||
# ============================================================================
|
||
# Helper Functions
|
||
# ============================================================================
|
||
|
||
print_success() { echo -e "${GREEN}✓${NC} $1"; }
|
||
print_warning() { echo -e "${YELLOW}⚠${NC} $1"; }
|
||
print_error() { echo -e "${RED}✗${NC} $1"; }
|
||
print_info() { echo -e "${CYAN}ℹ${NC} $1"; }
|
||
print_step() { echo -e "\n${GREEN}==>${NC} $1"; }
|
||
|
||
print_header() {
|
||
echo -e "\n${BLUE}╔════════════════════════════════════════════════════════════╗${NC}"
|
||
echo -e "${BLUE}║${NC} ${ZSH_THEME_NAME} Theme System-wide Deployment ${BLUE}║${NC}"
|
||
echo -e "${BLUE}╚════════════════════════════════════════════════════════════╝${NC}\n"
|
||
}
|
||
|
||
check_root() {
|
||
if [[ $EUID -ne 0 ]]; then
|
||
print_error "This script must be run as root (use sudo)"
|
||
echo "Usage: sudo $0 [--all|--current|--status]"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
ask_yes_no() {
|
||
local prompt="$1"
|
||
local default="${2:-y}"
|
||
|
||
if [[ "$default" == "y" ]]; then
|
||
prompt="$prompt [Y/n]: "
|
||
else
|
||
prompt="$prompt [y/N]: "
|
||
fi
|
||
|
||
read -p "$prompt" response
|
||
response=${response:-$default}
|
||
[[ "$response" =~ ^[Yy]$ ]]
|
||
}
|
||
|
||
is_system_symlink() {
|
||
local path="$1"
|
||
[[ -L "$path" ]] && [[ "$(readlink -f "$path")" == "$MASTER_THEME_PATH" ]]
|
||
}
|
||
|
||
is_local_symlink() {
|
||
local path="$1"
|
||
if [[ -L "$path" ]]; then
|
||
local target=$(readlink -f "$path")
|
||
[[ "$target" == *"/.dotfiles/"* ]]
|
||
else
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
get_link_status() {
|
||
local path="$1"
|
||
|
||
if [[ ! -e "$path" ]] && [[ ! -L "$path" ]]; then
|
||
echo "not_present"
|
||
elif is_system_symlink "$path"; then
|
||
echo "system_link"
|
||
elif is_local_symlink "$path"; then
|
||
echo "local_link"
|
||
elif [[ -f "$path" ]] && [[ ! -L "$path" ]]; then
|
||
echo "regular_file"
|
||
elif [[ -L "$path" ]]; then
|
||
echo "other_link"
|
||
else
|
||
echo "unknown"
|
||
fi
|
||
}
|
||
|
||
# ============================================================================
|
||
# Setup Functions
|
||
# ============================================================================
|
||
|
||
setup_master_location() {
|
||
print_step "Setting up master theme location"
|
||
|
||
[[ ! -d "$MASTER_THEME_DIR" ]] && mkdir -p "$MASTER_THEME_DIR" && print_success "Created: $MASTER_THEME_DIR"
|
||
|
||
local source_file=""
|
||
[[ -f "$SOURCE_THEME" ]] && source_file="$SOURCE_THEME"
|
||
[[ -z "$source_file" && -f "$THEME_FILE" ]] && source_file="$THEME_FILE"
|
||
[[ -z "$source_file" && -f "$HOME/.oh-my-zsh/themes/$THEME_FILE" ]] && source_file="$HOME/.oh-my-zsh/themes/$THEME_FILE"
|
||
|
||
if [[ -f "$MASTER_THEME_PATH" ]]; then
|
||
print_info "Master theme exists: $MASTER_THEME_PATH"
|
||
if [[ -n "$source_file" ]] && ! diff -q "$source_file" "$MASTER_THEME_PATH" &>/dev/null; then
|
||
print_warning "Source differs from master"
|
||
if ask_yes_no "Update master theme?"; then
|
||
cp "$source_file" "$MASTER_THEME_PATH"
|
||
chmod 644 "$MASTER_THEME_PATH"
|
||
print_success "Master theme updated"
|
||
fi
|
||
fi
|
||
else
|
||
if [[ -z "$source_file" ]]; then
|
||
print_error "Theme file not found. Check these locations:"
|
||
echo " - $SOURCE_THEME"
|
||
echo " - ./$THEME_FILE"
|
||
exit 1
|
||
fi
|
||
cp "$source_file" "$MASTER_THEME_PATH"
|
||
chmod 644 "$MASTER_THEME_PATH"
|
||
print_success "Copied theme to master location"
|
||
fi
|
||
}
|
||
|
||
get_users_with_tty() {
|
||
local users=()
|
||
[[ -d "/root/.oh-my-zsh" ]] && users+=("root")
|
||
|
||
while IFS=: read -r username _ uid _ _ home_dir _; do
|
||
[[ $uid -lt 1000 ]] && continue
|
||
[[ -d "$home_dir/.oh-my-zsh" ]] && users+=("$username")
|
||
done < /etc/passwd
|
||
|
||
echo "${users[@]}"
|
||
}
|
||
|
||
get_current_user() {
|
||
if [[ -n "${SUDO_USER:-}" ]]; then
|
||
echo "$SUDO_USER"
|
||
else
|
||
while IFS=: read -r username _ uid _ _ home_dir _; do
|
||
if [[ $uid -ge 1000 ]] && [[ -d "$home_dir/.oh-my-zsh" ]]; then
|
||
echo "$username"
|
||
return
|
||
fi
|
||
done < /etc/passwd
|
||
fi
|
||
}
|
||
|
||
select_users() {
|
||
local mode="$1"
|
||
local users=()
|
||
|
||
case "$mode" in
|
||
all)
|
||
users=($(get_users_with_tty))
|
||
;;
|
||
current)
|
||
local current_user=$(get_current_user)
|
||
[[ -n "$current_user" ]] && users=("$current_user")
|
||
[[ -d "/root/.oh-my-zsh" ]] && users+=("root")
|
||
;;
|
||
interactive)
|
||
local all_users=($(get_users_with_tty))
|
||
[[ ${#all_users[@]} -eq 0 ]] && { print_error "No users with oh-my-zsh found"; exit 1; }
|
||
|
||
echo "Users with oh-my-zsh:"
|
||
for i in "${!all_users[@]}"; do
|
||
echo " $((i+1)). ${all_users[$i]}"
|
||
done
|
||
echo
|
||
|
||
if ask_yes_no "Deploy to all users?"; then
|
||
users=("${all_users[@]}")
|
||
else
|
||
local current_user=$(get_current_user)
|
||
[[ -n "$current_user" ]] && users=("$current_user") && print_info "Selected: $current_user"
|
||
[[ -d "/root/.oh-my-zsh" ]] && ask_yes_no "Include root?" && users+=("root")
|
||
fi
|
||
;;
|
||
esac
|
||
|
||
echo "${users[@]}"
|
||
}
|
||
|
||
deploy_for_user() {
|
||
local username="$1"
|
||
local home_dir=$([[ "$username" == "root" ]] && echo "/root" || eval echo "~$username")
|
||
|
||
[[ ! -d "$home_dir" ]] && { print_warning "Home not found: $username"; return 1; }
|
||
[[ ! -d "$home_dir/.oh-my-zsh" ]] && { print_warning "oh-my-zsh not installed: $username"; return 1; }
|
||
|
||
local theme_link="$home_dir/.oh-my-zsh/themes/$THEME_FILE"
|
||
local status=$(get_link_status "$theme_link")
|
||
|
||
case "$status" in
|
||
system_link)
|
||
[[ "$FORCE_REPLACE" != true ]] && { print_success "Already system-wide: $username"; return 0; }
|
||
print_info "Recreating link: $username"
|
||
;;
|
||
local_link) print_info "Converting local → system: $username" ;;
|
||
regular_file) print_warning "Replacing file → system: $username" ;;
|
||
*) print_info "Creating link: $username" ;;
|
||
esac
|
||
|
||
mkdir -p "$home_dir/.oh-my-zsh/themes"
|
||
rm -f "$theme_link"
|
||
ln -sf "$MASTER_THEME_PATH" "$theme_link"
|
||
|
||
[[ "$username" != "root" ]] && chown -h "$username:$(id -gn "$username" 2>/dev/null || echo "$username")" "$theme_link" 2>/dev/null || true
|
||
|
||
is_system_symlink "$theme_link" && print_success "Deployed: $username" || { print_error "Failed: $username"; return 1; }
|
||
}
|
||
|
||
show_deployment_status() {
|
||
print_step "Deployment status"
|
||
|
||
local users=($(get_users_with_tty))
|
||
[[ ${#users[@]} -eq 0 ]] && { print_warning "No users with oh-my-zsh"; return; }
|
||
|
||
echo
|
||
printf "%-20s %-20s %s\n" "User" "Status" "Details"
|
||
printf "%-20s %-20s %s\n" "----" "------" "-------"
|
||
|
||
for user in "${users[@]}"; do
|
||
local home_dir=$([[ "$user" == "root" ]] && echo "/root" || eval echo "~$user")
|
||
local theme_path="$home_dir/.oh-my-zsh/themes/$THEME_FILE"
|
||
local status=$(get_link_status "$theme_path")
|
||
|
||
case "$status" in
|
||
system_link) printf "%-20s ${GREEN}%-20s${NC} %s\n" "$user" "System-wide ✓" "→ $MASTER_THEME_PATH" ;;
|
||
local_link) printf "%-20s ${YELLOW}%-20s${NC} %s\n" "$user" "Local symlink" "→ $(readlink "$theme_path")" ;;
|
||
regular_file) printf "%-20s ${CYAN}%-20s${NC} %s\n" "$user" "Regular file" "(standalone)" ;;
|
||
not_present) printf "%-20s ${RED}%-20s${NC}\n" "$user" "Not installed" ;;
|
||
*) printf "%-20s ${RED}%-20s${NC}\n" "$user" "Unknown" ;;
|
||
esac
|
||
done
|
||
echo
|
||
}
|
||
|
||
show_summary() {
|
||
echo
|
||
echo "============================================================================"
|
||
echo "Master theme: $MASTER_THEME_PATH"
|
||
echo
|
||
echo "To update theme globally:"
|
||
echo " sudo cp $SOURCE_THEME $MASTER_THEME_PATH"
|
||
echo
|
||
echo "Check status: sudo $0 --status"
|
||
echo "============================================================================"
|
||
}
|
||
|
||
# ============================================================================
|
||
# Main
|
||
# ============================================================================
|
||
|
||
main() {
|
||
local mode="interactive"
|
||
local status_only=false
|
||
|
||
for arg in "$@"; do
|
||
case "$arg" in
|
||
--all) mode="all" ;;
|
||
--current) mode="current" ;;
|
||
--force) FORCE_REPLACE=true ;;
|
||
--status|-s) status_only=true ;;
|
||
--help|-h)
|
||
echo "Usage: sudo $0 [OPTIONS]"
|
||
echo
|
||
echo "Options:"
|
||
echo " --all Deploy to all users with oh-my-zsh"
|
||
echo " --current Deploy to current user and root"
|
||
echo " --force Force replacement of existing links"
|
||
echo " --status Show current deployment status"
|
||
echo " --help Show this help"
|
||
exit 0
|
||
;;
|
||
*) print_error "Unknown option: $arg"; exit 1 ;;
|
||
esac
|
||
done
|
||
|
||
print_header
|
||
check_root
|
||
|
||
if [[ "$status_only" == true ]]; then
|
||
show_deployment_status
|
||
exit 0
|
||
fi
|
||
|
||
show_deployment_status
|
||
setup_master_location
|
||
|
||
local users=($(select_users "$mode"))
|
||
[[ ${#users[@]} -eq 0 ]] && { print_error "No users selected"; exit 1; }
|
||
|
||
print_step "Deploying to ${#users[@]} user(s): ${users[*]}"
|
||
[[ "$FORCE_REPLACE" == true ]] && print_warning "Force mode enabled"
|
||
|
||
ask_yes_no "Proceed?" || { print_warning "Cancelled"; exit 0; }
|
||
|
||
echo
|
||
local ok=0 fail=0
|
||
for user in "${users[@]}"; do
|
||
deploy_for_user "$user" && ((ok++)) || ((fail++))
|
||
done
|
||
|
||
echo
|
||
[[ $fail -eq 0 ]] && print_success "Complete! ($ok/${#users[@]})" || print_warning "Done with errors ($ok ok, $fail failed)"
|
||
|
||
show_deployment_status
|
||
show_summary
|
||
}
|
||
|
||
main "$@"
|