252 lines
6.2 KiB
Bash
252 lines
6.2 KiB
Bash
#!/usr/bin/env bash
|
|
# Script Standards and Common Functions
|
|
# Source this file in your scripts for consistent behavior
|
|
|
|
# ==============================================================================
|
|
# STANDARD SCRIPT HEADER
|
|
# ==============================================================================
|
|
# All scripts should start with:
|
|
# #!/usr/bin/env bash
|
|
# # NAME: Brief description of what the script does
|
|
# # REQUIRES: sudo interactive (if needed)
|
|
#
|
|
# set -euo pipefail
|
|
#
|
|
# # Source common functions (adjust path as needed)
|
|
# source "$(dirname "$0")/common.sh" 2>/dev/null || true
|
|
|
|
# ==============================================================================
|
|
# CONFIGURATION
|
|
# ==============================================================================
|
|
readonly SCRIPT_NAME="${0##*/}"
|
|
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
# Colors for output (only if terminal supports it)
|
|
if [[ -t 1 ]] && command -v tput >/dev/null 2>&1; then
|
|
readonly RED=$(tput setaf 1)
|
|
readonly GREEN=$(tput setaf 2)
|
|
readonly YELLOW=$(tput setaf 3)
|
|
readonly BLUE=$(tput setaf 4)
|
|
readonly PURPLE=$(tput setaf 5)
|
|
readonly CYAN=$(tput setaf 6)
|
|
readonly WHITE=$(tput setaf 7)
|
|
readonly BOLD=$(tput bold)
|
|
readonly RESET=$(tput sgr0)
|
|
else
|
|
readonly RED="" GREEN="" YELLOW="" BLUE="" PURPLE="" CYAN="" WHITE="" BOLD="" RESET=""
|
|
fi
|
|
|
|
# ==============================================================================
|
|
# LOGGING FUNCTIONS
|
|
# ==============================================================================
|
|
log_info() {
|
|
printf "${GREEN}[INFO]${RESET} %s\n" "$*"
|
|
}
|
|
|
|
log_warn() {
|
|
printf "${YELLOW}[WARN]${RESET} %s\n" "$*" >&2
|
|
}
|
|
|
|
log_error() {
|
|
printf "${RED}[ERROR]${RESET} %s\n" "$*" >&2
|
|
}
|
|
|
|
log_success() {
|
|
printf "${GREEN}[SUCCESS]${RESET} %s\n" "$*"
|
|
}
|
|
|
|
log_debug() {
|
|
if [[ "${DEBUG:-0}" == "1" ]]; then
|
|
printf "${CYAN}[DEBUG]${RESET} %s\n" "$*" >&2
|
|
fi
|
|
}
|
|
|
|
log_sudo() {
|
|
printf "${PURPLE}[SUDO]${RESET} %s\n" "$*"
|
|
}
|
|
|
|
# ==============================================================================
|
|
# UTILITY FUNCTIONS
|
|
# ==============================================================================
|
|
command_exists() {
|
|
command -v "$1" >/dev/null 2>&1
|
|
}
|
|
|
|
is_root() {
|
|
[[ $EUID -eq 0 ]]
|
|
}
|
|
|
|
require_root() {
|
|
if ! is_root; then
|
|
log_error "This script requires root privileges"
|
|
log_info "Run with: sudo $0 $*"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Check if running in interactive terminal
|
|
is_interactive() {
|
|
[[ -t 0 && -t 1 ]]
|
|
}
|
|
|
|
# ==============================================================================
|
|
# CONFIRMATION FUNCTIONS
|
|
# ==============================================================================
|
|
confirm() {
|
|
local prompt="${1:-Continue?}"
|
|
local default="${2:-n}"
|
|
local response
|
|
|
|
if ! is_interactive; then
|
|
log_warn "Non-interactive mode, using default: $default"
|
|
[[ "$default" == "y" ]]
|
|
return $?
|
|
fi
|
|
|
|
while true; do
|
|
if [[ "$default" == "y" ]]; then
|
|
printf "%s [Y/n]: " "$prompt"
|
|
else
|
|
printf "%s [y/N]: " "$prompt"
|
|
fi
|
|
|
|
read -r response
|
|
response=${response,,} # Convert to lowercase
|
|
|
|
case "$response" in
|
|
"")
|
|
[[ "$default" == "y" ]]
|
|
return $?
|
|
;;
|
|
y | yes)
|
|
return 0
|
|
;;
|
|
n | no)
|
|
return 1
|
|
;;
|
|
*)
|
|
log_warn "Please answer 'y' or 'n'"
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# Show changes and confirm
|
|
confirm_change() {
|
|
local file="$1"
|
|
local description="$2"
|
|
local temp_file="$3"
|
|
|
|
log_info "Proposed changes for $description:"
|
|
log_info "File: $file"
|
|
echo
|
|
|
|
if command_exists colordiff; then
|
|
colordiff -u "$file" "$temp_file" 2>/dev/null || diff -u "$file" "$temp_file" 2>/dev/null || true
|
|
else
|
|
diff -u "$file" "$temp_file" 2>/dev/null || true
|
|
fi
|
|
|
|
echo
|
|
confirm "Apply these changes?"
|
|
}
|
|
|
|
# ==============================================================================
|
|
# FILE OPERATIONS
|
|
# ==============================================================================
|
|
backup_file() {
|
|
local file="$1"
|
|
local backup_suffix="${2:-$(date +%Y%m%d_%H%M%S)}"
|
|
local backup_file="${file}.backup.${backup_suffix}"
|
|
|
|
if [[ -f "$file" ]]; then
|
|
cp "$file" "$backup_file"
|
|
log_info "Created backup: $backup_file"
|
|
echo "$backup_file"
|
|
fi
|
|
}
|
|
|
|
create_temp_file() {
|
|
local template="${1:-tmp.XXXXXX}"
|
|
mktemp "/tmp/${template}"
|
|
}
|
|
|
|
# ==============================================================================
|
|
# PACKAGE MANAGEMENT
|
|
# ==============================================================================
|
|
install_packages() {
|
|
local packages=("$@")
|
|
|
|
if [[ ${#packages[@]} -eq 0 ]]; then
|
|
log_warn "No packages specified"
|
|
return 1
|
|
fi
|
|
|
|
log_info "Installing packages: ${packages[*]}"
|
|
|
|
if command_exists paru; then
|
|
paru -S --needed --noconfirm "${packages[@]}"
|
|
elif command_exists pacman; then
|
|
sudo pacman -S --needed --noconfirm "${packages[@]}"
|
|
else
|
|
log_error "No supported package manager found"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ==============================================================================
|
|
# SERVICE MANAGEMENT
|
|
# ==============================================================================
|
|
enable_service() {
|
|
local service="$1"
|
|
local user_service="${2:-false}"
|
|
|
|
if [[ "$user_service" == "true" ]]; then
|
|
log_info "Enabling user service: $service"
|
|
systemctl --user enable --now "$service"
|
|
else
|
|
log_info "Enabling system service: $service"
|
|
sudo systemctl enable --now "$service"
|
|
fi
|
|
}
|
|
|
|
# ==============================================================================
|
|
# ERROR HANDLING
|
|
# ==============================================================================
|
|
cleanup() {
|
|
local exit_code=$?
|
|
# Add any cleanup operations here
|
|
exit $exit_code
|
|
}
|
|
|
|
# Set up trap for cleanup
|
|
trap cleanup EXIT INT TERM
|
|
|
|
# ==============================================================================
|
|
# SCRIPT INITIALIZATION
|
|
# ==============================================================================
|
|
init_script() {
|
|
log_info "Starting $SCRIPT_NAME"
|
|
|
|
# Set DEBUG mode if requested
|
|
if [[ "${1:-}" == "--debug" ]]; then
|
|
export DEBUG=1
|
|
log_debug "Debug mode enabled"
|
|
shift
|
|
fi
|
|
}
|
|
|
|
# ==============================================================================
|
|
# SCRIPT COMPLETION
|
|
# ==============================================================================
|
|
finish_script() {
|
|
local exit_code="${1:-0}"
|
|
|
|
if [[ "$exit_code" -eq 0 ]]; then
|
|
log_success "$SCRIPT_NAME completed successfully"
|
|
else
|
|
log_error "$SCRIPT_NAME failed with exit code $exit_code"
|
|
fi
|
|
|
|
exit "$exit_code"
|
|
}
|