#!/usr/bin/env bash # Enhanced Awesome Package Manager Installer # Version: 0.2.0 set -e # Exit on error # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' BOLD='\033[1m' RESET='\033[0m' awesome_dir="$HOME/.local/share/awesome" bin_dir="$HOME/.local/share/bin" log_dir="$HOME/.local/share/awesome/logs" config_dir="$HOME/.config/awesome" # ============================================================================ # UTILITY FUNCTIONS # ============================================================================ print_color() { local color=$1 shift printf "${color}%s${RESET}\n" "$*" } print_success() { print_color "$GREEN" "✓ $*" } print_error() { print_color "$RED" "✗ $*" >&2 } print_info() { print_color "$CYAN" "→ $*" } print_warning() { print_color "$YELLOW" "! $*" } print_header() { echo print_color "$BOLD$CYAN" "=== $* ===" echo } fn_confirm() { local item=$1 read -rp "Do you want to remove $item? (yes/y or no/n): " PANS ans=$(echo "$PANS" | cut -c 1-1 | tr "[:lower:]" "[:upper:]") echo "$ans" } # Check if a directory exists, create if not fn_checkDir() { if [[ -z $1 ]]; then print_error "Specify the directory." exit 1 fi if [[ ! -d $1 ]]; then mkdir -p "$1" print_success "$1 directory created." fi } # Check if a command exists fn_check_cmd() { if ! command -v "$1" &>/dev/null; then print_error "Please install $1" print_info "On macOS: brew install $1" print_info "On Ubuntu/Debian: sudo apt-get install $1" exit 1 fi } # Check network connectivity check_network() { print_info "Checking network connectivity..." # Use curl with timeout to verify actual HTTPS connectivity to GitHub if curl -sf --connect-timeout 5 --max-time 10 "https://github.com" -o /dev/null 2>/dev/null; then print_success "Network connection OK" return 0 else print_error "Cannot reach GitHub. Please check your internet connection." return 1 fi } # Create a symlink in bin directory fn_create_symlink() { local repo_name=$1 ln -sf "${awesome_dir}/${repo_name}/${repo_name}" "${bin_dir}/${repo_name}" } # Find all symlinks in bin directory fn_find_symlinks() { find "$bin_dir" -type l 2>/dev/null } # Verify installation verify_installation() { print_header "Verifying Installation" local issues=0 # Check awesome directory if [[ -d "$awesome_dir/awesome" ]]; then print_success "Awesome repository cloned" else print_error "Awesome repository not found" ((++issues)) fi # Check for main script if [[ -f "$awesome_dir/awesome/awesome" ]]; then print_success "Main awesome script found" else print_error "Main awesome script not found" ((++issues)) fi # Check for utility libraries if [[ -f "$awesome_dir/awesome/utils/lib-enhanced" ]]; then print_success "Enhanced library found" else print_warning "Enhanced library not found" fi # Check symlink if [[ -L "$bin_dir/awesome" ]]; then print_success "Awesome symlink created" else print_error "Awesome symlink not found" ((++issues)) fi # Check if awesome is in PATH if command -v awesome &>/dev/null; then print_success "Awesome is accessible in PATH" else print_warning "Awesome not found in PATH (you may need to update your shell config)" # Don't increment issues - this is expected until user updates their shell config fi echo if [ "$issues" -eq 0 ]; then print_success "All checks passed!" return 0 else print_warning "Found $issues issue(s) that may need attention" return 1 fi } # Run post-install diagnostics run_diagnostics() { print_header "Running Diagnostics" if command -v awesome &>/dev/null; then awesome --version echo awesome doctor else print_warning "Cannot run diagnostics - awesome not in PATH yet" print_info "After adding awesome to PATH, run: awesome doctor" fi } # ============================================================================ # INSTALL FUNCTION # ============================================================================ fn_install() { local force_install="${FORCE_INSTALL:-false}" print_header "Installing Awesome Package Manager" # Check prerequisites print_info "Checking prerequisites..." fn_check_cmd git fn_check_cmd curl # Check network if ! check_network; then exit 1 fi # Create directories print_info "Setting up directories..." fn_checkDir "$awesome_dir" fn_checkDir "$bin_dir" fn_checkDir "$log_dir" fn_checkDir "$config_dir" # Clone repository cd "$awesome_dir" || exit local url="https://github.com/shinokada/awesome.git" local repo="awesome" print_info "Cloning awesome repository..." if [[ -d "$awesome_dir/$repo" ]]; then print_warning "Awesome directory already exists" # Check if stdin is a terminal if [[ ! -t 0 ]] && [[ "$force_install" != "true" ]]; then print_error "Cannot run install interactively through pipe when directory exists" print_info "Please download and run the installer directly:" print_color "$YELLOW" " curl -O https://raw.githubusercontent.com/shinokada/awesome/main/install" print_color "$YELLOW" " bash install install" echo print_info "Or use force mode (overwrites existing installation):" print_color "$YELLOW" " curl -s https://raw.githubusercontent.com/shinokada/awesome/main/install | FORCE_INSTALL=true bash -s install" exit 1 fi if [[ "$force_install" == "true" ]]; then print_warning "Force mode: Removing existing installation" rm -rf "$awesome_dir/$repo" print_info "Removed existing installation" else read -rp "Do you want to remove it and reinstall? (yes/y or no/n): " response # Convert to lowercase for Bash 3.2 compatibility response=$(echo "$response" | tr '[:upper:]' '[:lower:]') if [[ "$response" =~ ^(yes|y)$ ]]; then rm -rf "$awesome_dir/$repo" print_info "Removed existing installation" else print_info "Keeping existing installation" fn_create_symlink "$repo" print_success "Symlink updated" exit 0 fi fi fi local max_retries=${MAX_RETRIES:-3} local retry_count=0 while :; do if git clone --depth 1 "$url"; then print_success "Repository cloned successfully" break fi ((++retry_count)) # Cleanup partial clone so retries can succeed if [[ -d "$awesome_dir/$repo" ]]; then rm -rf "$awesome_dir/$repo" fi if ((retry_count >= max_retries)); then print_error "Failed to clone repository after $max_retries attempts" exit 1 fi print_warning "Clone failed, retrying ($retry_count/$max_retries)..." sleep 2 done # Create symlink print_info "Creating symlink..." fn_create_symlink "${repo}" print_success "Symlink created" # Verify installation if ! verify_installation; then print_warning "Installation completed with warnings. Review the messages above." fi # Print usage instructions print_header "Installation Complete!" echo "To use awesome, ensure your PATH includes the bin directory:" print_color "$YELLOW" " export PATH=\$HOME/.local/share/bin:\$PATH" echo echo "Add this line to your shell configuration file:" print_info " For zsh: ~/.zshrc" print_info " For bash: ~/.bashrc or ~/.bash_profile" echo echo "Then reload your shell or run:" print_color "$YELLOW" " source ~/.zshrc # or source ~/.bashrc" echo echo "Getting started:" print_info " awesome --help # Show help" print_info " awesome doctor # Run health check" print_info " awesome ls # List installed packages" print_info " awesome install # Install a package" echo print_success "Visit https://awesome.codewithshin.com for documentation" # Ask if user wants to run diagnostics echo if [[ ! -t 0 ]]; then # Non-interactive mode - skip diagnostics print_info "Run 'awesome doctor' later to verify your installation" else read -rp "Would you like to run diagnostics now? (yes/y or no/n): " run_diag # Convert to lowercase for Bash 3.2 compatibility run_diag=$(echo "$run_diag" | tr '[:upper:]' '[:lower:]') if [[ "$run_diag" =~ ^(yes|y)$ ]]; then # Temporarily add to PATH for this session export PATH="$bin_dir:$PATH" run_diagnostics fi fi } # ============================================================================ # UNINSTALL FUNCTION # ============================================================================ fn_uninstall() { local force_uninstall="${FORCE_UNINSTALL:-false}" print_header "Uninstalling Awesome Package Manager" # Show what will be removed echo "This will remove:" print_info " - All installed packages in $awesome_dir" print_info " - All symlinks in $bin_dir" print_info " - Configuration files in $config_dir" print_info " - Log files in $log_dir" echo print_warning "This action cannot be undone!" echo # Check if stdin is a terminal if [[ ! -t 0 ]] && [[ "$force_uninstall" != "true" ]]; then print_error "Cannot run uninstall interactively through pipe" print_info "Please download and run the installer directly:" print_color "$YELLOW" " curl -O https://raw.githubusercontent.com/shinokada/awesome/main/install" print_color "$YELLOW" " bash install uninstall" echo print_info "Or use force mode (skips all confirmations):" print_color "$YELLOW" " curl -s https://raw.githubusercontent.com/shinokada/awesome/main/install | FORCE_UNINSTALL=true bash -s uninstall" exit 1 fi if [[ "$force_uninstall" != "true" ]]; then if [[ $(fn_confirm "awesome and all its data") != "Y" ]]; then print_info "Uninstall cancelled" exit 0 fi else print_warning "Force mode: Skipping confirmations" fi # Ask about directory removal first local remove_awesome_dir="N" if [[ -d "$awesome_dir" ]]; then if [[ "$force_uninstall" == "true" ]]; then remove_awesome_dir="Y" else remove_awesome_dir=$(fn_confirm "awesome directory") fi fi # Only remove symlinks if user confirmed directory removal if [[ "$remove_awesome_dir" == "Y" ]]; then print_info "Finding and removing symlinks..." if [[ -d "$bin_dir" ]]; then links=() while IFS= read -r link; do links+=("$link") done < <(fn_find_symlinks 2>/dev/null) if [ ${#links[@]} -gt 0 ]; then for link in "${links[@]}"; do if [[ -L "$link" ]]; then local link_name=$(basename "$link") unlink "$link" print_success "Removed symlink: $link_name" fi done else print_info "No symlinks found" fi fi fi if [[ "$remove_awesome_dir" == "Y" ]]; then if [[ -d "$awesome_dir" ]]; then print_info "Removing awesome directory..." rm -rf "$awesome_dir" print_success "Removed $awesome_dir" fi fi # Remove config directory if [[ -d "$config_dir" ]]; then if [[ "$force_uninstall" == "true" ]]; then rm -rf "$config_dir" print_success "Removed $config_dir" else read -rp "Remove configuration directory? (yes/y or no/n): " remove_config # Convert to lowercase for Bash 3.2 compatibility remove_config=$(echo "$remove_config" | tr '[:upper:]' '[:lower:]') if [[ "$remove_config" =~ ^(yes|y)$ ]]; then rm -rf "$config_dir" print_success "Removed $config_dir" fi fi fi # Remove bin directory if empty if [[ -d "$bin_dir" ]] && [[ -z "$(ls -A "$bin_dir")" ]]; then if [[ "$force_uninstall" == "true" ]]; then rm -rf "$bin_dir" print_success "Removed empty bin directory" else read -rp "Bin directory is empty. Remove it? (yes/y or no/n): " remove_bin # Convert to lowercase for Bash 3.2 compatibility remove_bin=$(echo "$remove_bin" | tr '[:upper:]' '[:lower:]') if [[ "$remove_bin" =~ ^(yes|y)$ ]]; then rm -rf "$bin_dir" print_success "Removed empty bin directory" fi fi fi print_header "Uninstallation Complete" print_info "Remember to remove the PATH export from your shell config file:" print_color "$YELLOW" " export PATH=\$HOME/.local/share/bin:\$PATH" } # ============================================================================ # UPDATE FUNCTION # ============================================================================ fn_update() { print_header "Updating Awesome Package Manager" if [[ ! -d "$awesome_dir/awesome" ]]; then print_error "Awesome is not installed" print_info "Run: ./install install" exit 1 fi # Check network connectivity if ! check_network; then exit 1 fi cd "$awesome_dir/awesome" || exit print_info "Fetching updates..." local max_retries=${MAX_RETRIES:-3} local retry_count=0 while :; do if git pull; then print_success "Awesome updated successfully" print_info "New version: $(git describe --tags 2>/dev/null || git rev-parse --short HEAD)" return 0 fi ((++retry_count)) if ((retry_count >= max_retries)); then print_error "Update failed after $max_retries attempts" exit 1 fi print_warning "Update failed, retrying ($retry_count/$max_retries)..." sleep 2 done } # ============================================================================ # MAIN FUNCTION # ============================================================================ fn_main() { if [ $# -eq 0 ]; then print_error "No command specified" echo echo "Usage: $0 {install|uninstall|update}" echo echo "Commands:" echo " install - Install awesome package manager" echo " uninstall - Remove awesome and all data" echo " update - Update awesome to latest version" exit 1 fi case $1 in install) fn_install ;; uninstall) fn_uninstall ;; update) fn_update ;; *) print_error "Unknown command: $1" echo "Valid commands: install, uninstall, update" exit 1 ;; esac } fn_main "$@"