common.sh 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #!/usr/bin/env bash
  2. # Script Standards and Common Functions
  3. # Source this file in your scripts for consistent behavior
  4. # ==============================================================================
  5. # STANDARD SCRIPT HEADER
  6. # ==============================================================================
  7. # All scripts should start with:
  8. # #!/usr/bin/env bash
  9. # # NAME: Brief description of what the script does
  10. # # REQUIRES: sudo interactive (if needed)
  11. #
  12. # set -euo pipefail
  13. #
  14. # # Source common functions (adjust path as needed)
  15. # source "$(dirname "$0")/common.sh" 2>/dev/null || true
  16. # ==============================================================================
  17. # CONFIGURATION
  18. # ==============================================================================
  19. readonly SCRIPT_NAME="${0##*/}"
  20. readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  21. # Colors for output (only if terminal supports it)
  22. if [[ -t 1 ]] && command -v tput >/dev/null 2>&1; then
  23. readonly RED=$(tput setaf 1)
  24. readonly GREEN=$(tput setaf 2)
  25. readonly YELLOW=$(tput setaf 3)
  26. readonly BLUE=$(tput setaf 4)
  27. readonly PURPLE=$(tput setaf 5)
  28. readonly CYAN=$(tput setaf 6)
  29. readonly WHITE=$(tput setaf 7)
  30. readonly BOLD=$(tput bold)
  31. readonly RESET=$(tput sgr0)
  32. else
  33. readonly RED="" GREEN="" YELLOW="" BLUE="" PURPLE="" CYAN="" WHITE="" BOLD="" RESET=""
  34. fi
  35. # ==============================================================================
  36. # LOGGING FUNCTIONS
  37. # ==============================================================================
  38. log_info() {
  39. printf "${GREEN}[INFO]${RESET} %s\n" "$*"
  40. }
  41. log_warn() {
  42. printf "${YELLOW}[WARN]${RESET} %s\n" "$*" >&2
  43. }
  44. log_error() {
  45. printf "${RED}[ERROR]${RESET} %s\n" "$*" >&2
  46. }
  47. log_success() {
  48. printf "${GREEN}[SUCCESS]${RESET} %s\n" "$*"
  49. }
  50. log_debug() {
  51. if [[ "${DEBUG:-0}" == "1" ]]; then
  52. printf "${CYAN}[DEBUG]${RESET} %s\n" "$*" >&2
  53. fi
  54. }
  55. log_sudo() {
  56. printf "${PURPLE}[SUDO]${RESET} %s\n" "$*"
  57. }
  58. # ==============================================================================
  59. # UTILITY FUNCTIONS
  60. # ==============================================================================
  61. command_exists() {
  62. command -v "$1" >/dev/null 2>&1
  63. }
  64. is_root() {
  65. [[ $EUID -eq 0 ]]
  66. }
  67. require_root() {
  68. if ! is_root; then
  69. log_error "This script requires root privileges"
  70. log_info "Run with: sudo $0 $*"
  71. exit 1
  72. fi
  73. }
  74. # Check if running in interactive terminal
  75. is_interactive() {
  76. [[ -t 0 && -t 1 ]]
  77. }
  78. # ==============================================================================
  79. # CONFIRMATION FUNCTIONS
  80. # ==============================================================================
  81. confirm() {
  82. local prompt="${1:-Continue?}"
  83. local default="${2:-n}"
  84. local response
  85. if ! is_interactive; then
  86. log_warn "Non-interactive mode, using default: $default"
  87. [[ "$default" == "y" ]]
  88. return $?
  89. fi
  90. while true; do
  91. if [[ "$default" == "y" ]]; then
  92. printf "%s [Y/n]: " "$prompt"
  93. else
  94. printf "%s [y/N]: " "$prompt"
  95. fi
  96. read -r response
  97. response=${response,,} # Convert to lowercase
  98. case "$response" in
  99. "")
  100. [[ "$default" == "y" ]]
  101. return $?
  102. ;;
  103. y | yes)
  104. return 0
  105. ;;
  106. n | no)
  107. return 1
  108. ;;
  109. *)
  110. log_warn "Please answer 'y' or 'n'"
  111. ;;
  112. esac
  113. done
  114. }
  115. # Show changes and confirm
  116. confirm_change() {
  117. local file="$1"
  118. local description="$2"
  119. local temp_file="$3"
  120. log_info "Proposed changes for $description:"
  121. log_info "File: $file"
  122. echo
  123. if command_exists colordiff; then
  124. colordiff -u "$file" "$temp_file" 2>/dev/null || diff -u "$file" "$temp_file" 2>/dev/null || true
  125. else
  126. diff -u "$file" "$temp_file" 2>/dev/null || true
  127. fi
  128. echo
  129. confirm "Apply these changes?"
  130. }
  131. # ==============================================================================
  132. # FILE OPERATIONS
  133. # ==============================================================================
  134. backup_file() {
  135. local file="$1"
  136. local backup_suffix="${2:-$(date +%Y%m%d_%H%M%S)}"
  137. local backup_file="${file}.backup.${backup_suffix}"
  138. if [[ -f "$file" ]]; then
  139. cp "$file" "$backup_file"
  140. log_info "Created backup: $backup_file"
  141. echo "$backup_file"
  142. fi
  143. }
  144. create_temp_file() {
  145. local template="${1:-tmp.XXXXXX}"
  146. mktemp "/tmp/${template}"
  147. }
  148. # ==============================================================================
  149. # PACKAGE MANAGEMENT
  150. # ==============================================================================
  151. install_packages() {
  152. local packages=("$@")
  153. if [[ ${#packages[@]} -eq 0 ]]; then
  154. log_warn "No packages specified"
  155. return 1
  156. fi
  157. log_info "Installing packages: ${packages[*]}"
  158. if command_exists paru; then
  159. paru -S --needed --noconfirm "${packages[@]}"
  160. elif command_exists pacman; then
  161. sudo pacman -S --needed --noconfirm "${packages[@]}"
  162. else
  163. log_error "No supported package manager found"
  164. return 1
  165. fi
  166. }
  167. # ==============================================================================
  168. # SERVICE MANAGEMENT
  169. # ==============================================================================
  170. enable_service() {
  171. local service="$1"
  172. local user_service="${2:-false}"
  173. if [[ "$user_service" == "true" ]]; then
  174. log_info "Enabling user service: $service"
  175. systemctl --user enable --now "$service"
  176. else
  177. log_info "Enabling system service: $service"
  178. sudo systemctl enable --now "$service"
  179. fi
  180. }
  181. # ==============================================================================
  182. # ERROR HANDLING
  183. # ==============================================================================
  184. cleanup() {
  185. local exit_code=$?
  186. # Add any cleanup operations here
  187. exit $exit_code
  188. }
  189. # Set up trap for cleanup
  190. trap cleanup EXIT INT TERM
  191. # ==============================================================================
  192. # SCRIPT INITIALIZATION
  193. # ==============================================================================
  194. init_script() {
  195. log_info "Starting $SCRIPT_NAME"
  196. # Set DEBUG mode if requested
  197. if [[ "${1:-}" == "--debug" ]]; then
  198. export DEBUG=1
  199. log_debug "Debug mode enabled"
  200. shift
  201. fi
  202. }
  203. # ==============================================================================
  204. # SCRIPT COMPLETION
  205. # ==============================================================================
  206. finish_script() {
  207. local exit_code="${1:-0}"
  208. if [[ "$exit_code" -eq 0 ]]; then
  209. log_success "$SCRIPT_NAME completed successfully"
  210. else
  211. log_error "$SCRIPT_NAME failed with exit code $exit_code"
  212. fi
  213. exit "$exit_code"
  214. }