diff options
| author | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2025-09-03 10:25:08 +0900 |
|---|---|---|
| committer | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2025-09-03 10:25:08 +0900 |
| commit | a92abe1eb0c6fb409e2d0f9b44695778988e7523 (patch) | |
| tree | 86c24d8731cf422f014b5384ff02ddfa985baed1 | |
| parent | 3ee425cbed2b673ac90c2c86f241d7f2442032de (diff) | |
modified static/thesiah-mac.sh
| -rwxr-xr-x | static/thesiah-mac.sh | 397 |
1 files changed, 322 insertions, 75 deletions
diff --git a/static/thesiah-mac.sh b/static/thesiah-mac.sh index d6cd90c..38f25ad 100755 --- a/static/thesiah-mac.sh +++ b/static/thesiah-mac.sh @@ -4,110 +4,253 @@ # Adaptation by: Soomin Im <si@thesiah.xyz> # License: GNU GPLv3 +set -e # Exit on error ### VARIABLES ### dotfilesrepo="https://github.com/TheSiahxyz/.dotfiles.git" progsfile="thesiah.xyz/macprogs.csv" repobranch="master" +### ENVIRONMENT VARIABLES FOR NON-INTERACTIVE BREW ### +export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 +export HOMEBREW_NO_INSTALL_CLEANUP=1 +export HOMEBREW_NO_AUTO_UPDATE=1 +export HOMEBREW_NO_ANALYTICS=1 +export HOMEBREW_NO_ENV_HINTS=1 ### FUNCTIONS ### +# Print formatted output +print_header() { + echo "==================================================" + echo "$1" + echo "==================================================" +} + +print_step() { + echo " -> $1" +} + +print_success() { + echo " [OK] $1" +} + +print_error() { + echo " [ERROR] $1" >&2 +} + +print_warning() { + echo " [WARNING] $1" +} + +# Check if command exists +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +# Check if directory exists and is writable +check_directory() { + if [ ! -d "$1" ]; then + print_step "Creating directory: $1" + mkdir -p "$1" || error "Failed to create directory: $1" + fi + + if [ ! -w "$1" ]; then + error "Directory is not writable: $1" + fi +} + # Notify function using macOS's notification system notify() { - osascript -e "display notification \"$2\" with title \"$1\"" + if command_exists osascript; then + osascript -e "display notification \"$2\" with title \"$1\"" 2>/dev/null || true + fi } # Error function for error handling and notification error() { - # Log to stderr and exit with failure. - printf "%s\n" "$1" >&2 - exit 1 + print_error "$1" + notify "Installation Failed" "$1" + exit 1 } # Install Homebrew brewinstall() { - if ! command -v brew &>/dev/null; then + print_step "Checking Homebrew installation..." + + if ! command_exists brew; then + print_step "Installing Homebrew..." /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + + # Add Homebrew to PATH for Apple Silicon Macs + if [ -f "/opt/homebrew/bin/brew" ]; then + eval "$(/opt/homebrew/bin/brew shellenv)" + elif [ -f "/usr/local/bin/brew" ]; then + eval "$(/usr/local/bin/brew shellenv)" + fi + + # Verify brew is now available + if ! command_exists brew; then + error "Homebrew installation failed - brew command not found" + fi + else + print_success "Homebrew already installed" fi - brew update >/dev/null 2>&1 - brew upgrade >/dev/null 2>&1 - eval "$(/opt/homebrew/bin/brew shellenv)" + + print_step "Updating Homebrew..." + brew update >/dev/null 2>&1 || print_warning "Homebrew update failed, continuing..." + brew upgrade >/dev/null 2>&1 || print_warning "Homebrew upgrade failed, continuing..." } -# Install Homebrew packages +# Install Homebrew packages with non-interactive flags installpkg() { - if brew list "$1" | grep "$1" &>/dev/null; then - echo "$1 is already installed." - else - echo "Installing $1..." - brew install "$1" || error "Failed to install $1." - fi + pkg="$1" + if brew list "$pkg" >/dev/null 2>&1; then + print_success "$pkg already installed" + else + print_step "Installing $pkg..." + # Use non-interactive flags to prevent user input prompts + brew install --force-bottle --no-quarantine "$pkg" 2>/dev/null || \ + brew install "$pkg" || error "Failed to install $pkg" + print_success "$pkg installed successfully" + fi } # Install Homebrew Cask applications caskinstall() { - if brew list --cask "$1" | grep "$1" &>/dev/null; then - echo "$1 is already installed." - else - echo "Installing $1..." - brew install --cask "$1" || error "Failed to install $1." - fi + pkg="$1" + if brew list --cask "$pkg" >/dev/null 2>&1; then + print_success "$pkg already installed" + else + print_step "Installing $pkg..." + # Use non-interactive flags for cask installations + brew install --cask --force --no-quarantine "$pkg" 2>/dev/null || \ + brew install --cask "$pkg" || error "Failed to install $pkg" + print_success "$pkg installed successfully" + fi } # Install Python packages with pip pipinstall() { - echo "Installing the Python package: $1" - if ! command -v pip3 &>/dev/null; then - installpkg python # This installs pip3 - fi - pip3 install "$1" || error "Failed to install Python package $1." + pkg="$1" + print_step "Installing Python package: $pkg" + + if ! command_exists pip3; then + print_step "Installing Python (includes pip3)..." + installpkg python + fi + + # Use --quiet flag to prevent pip from asking questions + pip3 install --quiet "$pkg" || error "Failed to install Python package $pkg" + print_success "$pkg installed successfully" } # Install Homebrew Tap packages tapinstall() { - # Extract the repository and package name repo=$(echo "$1" | cut -d'/' -f1) pkg=$(echo "$1" | cut -d'/' -f2) - echo "Adding tap: $repo" - brew tap "$repo" || error "Failed to tap $repo." + print_step "Adding tap: $repo" + brew tap "$repo" || error "Failed to tap $repo" - echo "Installing package: $pkg from tap" - brew install "$pkg" || error "Failed to install $pkg from tap." + print_step "Installing package: $pkg from tap" + # Use non-interactive flags for tap packages too + brew install --force-bottle --no-quarantine "$pkg" 2>/dev/null || \ + brew install "$pkg" || error "Failed to install $pkg from tap" + print_success "$pkg installed successfully" } # Install mac apps with mas masinstall() { - echo "Installing the mac app: $1" + app_name="$1" + print_step "Installing Mac App Store app: $app_name" - # Search for the app by name and get its ID using awk to parse the output - id=$(mas search "$1" | awk -v appName="$1" '{if ($0 ~ appName) {print $1; exit}}') - # Check if the id variable is set and not empty + # Search for the app by name and get its ID + id=$(mas search "$app_name" | awk -v appName="$app_name" ' + tolower($0) ~ tolower(appName) { + print $1; + exit + } + ') + if [ -z "$id" ]; then - error "Failed to find an ID for the app named $1." + error "Failed to find an ID for the app named $app_name" else - mas install "$id" || error "Failed to install the app $1." + print_step "Installing app ID: $id" + mas install "$id" || error "Failed to install the app $app_name" + print_success "$app_name installed successfully" fi } # Clone and setup dotfiles putgitrepo() { - notify "Installation" "Downloading and installing config files..." - dir=$(mktemp -d) - if [ ! -d "$2" ]; then - mkdir -p "$2" - fi - git clone --depth 1 --single-branch --no-tags -q --recursive -b "${3:-$repobranch}" "$1" "$dir" || error "Failed to clone $1." - cp -Rf "$dir/"* "$2" - rm -rf "$dir" # Clean up + repo="$1" + target="$2" + branch="${3:-$repobranch}" + + print_step "Downloading and installing config files..." + notify "Installation" "Downloading and installing config files..." + + # Check if git is available + if ! command_exists git; then + error "Git is not installed. Please install git first." + fi + + # Check target directory + check_directory "$target" + + dir=$(mktemp -d) + + print_step "Cloning repository: $repo" + git clone --depth 1 --single-branch --no-tags -q --recursive -b "$branch" "$repo" "$dir" || \ + error "Failed to clone $repo" + + print_step "Copying files to target directory" + cp -Rf "$dir/"* "$target" 2>/dev/null || true + rm -rf "$dir" + print_success "Config files installed successfully" } # Installation loop for processing the programs.csv file installationloop() { - ([ -f "$progsfile" ] && cp "$progsfile" /tmp/programs.csv) || curl -Ls "$progsfile" | sed '/^#/d' >/tmp/programs.csv || error "Failed to download programs file." + csv_file="/tmp/programs.csv" + + print_header "Installing Software Packages" + + # Download programs file + if [ -f "$progsfile" ]; then + print_step "Using local programs file: $progsfile" + cp "$progsfile" "$csv_file" + else + print_step "Downloading programs list from: $progsfile" + if ! curl -Ls "$progsfile" | sed '/^#/d' > "$csv_file"; then + error "Failed to download programs file from $progsfile" + fi + + # Verify file was downloaded and has content + if [ ! -s "$csv_file" ]; then + error "Downloaded programs file is empty" + fi + fi + + # Count total packages for progress + total_packages=$(wc -l < "$csv_file" 2>/dev/null | tr -d ' ' || echo "0") + if [ "$total_packages" -eq 0 ]; then + error "No packages found in programs file" + fi + + current=0 + while IFS=, read -r tag program comment; do - echo "$comment" + # Skip empty lines + if [ -z "$tag" ] || [ -z "$program" ]; then + continue + fi + + current=$((current + 1)) + echo "" + print_step "[$current/$total_packages] $comment" + case "$tag" in "P") pipinstall "$program" ;; "C") caskinstall "$program" ;; @@ -115,39 +258,143 @@ installationloop() { "T") tapinstall "$program" ;; *) installpkg "$program" ;; esac - done </tmp/programs.csv + done < "$csv_file" + + rm -f "$csv_file" + print_success "All packages installed successfully" } -finalize() { - # Farewell - echo "All done!" - echo "Congrats! Provided there were no hidden errors, the script completed successfully and all the programs and configuration files should be in place.\\n\\n.t Soomin" +# Setup shell configuration +setup_shell() { + print_header "Setting up Shell Configuration" + + print_step "Creating symbolic links for shell configs..." + + # Create backup of existing configs + timestamp=$(date +%Y%m%d_%H%M%S) + + [ -f ~/.zprofile ] && mv ~/.zprofile ~/.zprofile.backup.$timestamp + [ -f ~/.bash_profile ] && mv ~/.bash_profile ~/.bash_profile.backup.$timestamp + [ -f ~/.bashrc ] && mv ~/.bashrc ~/.bashrc.backup.$timestamp + + # Create new symbolic links + ln -sf ~/.dotfiles/mac/.config/shell/profile ~/.zprofile + ln -sf ~/.dotfiles/mac/.config/bash/bash_profile ~/.bash_profile + ln -sf ~/.dotfiles/mac/.config/bash/bashrc ~/.bashrc + + print_success "Shell configuration setup completed" } -### Main Installation Process ### -# Ensure Homebrew is installed -brewinstall || error "Failed to install Homebrew." - -# Ensure mas is installed -installpkg "mas" - -# Start the installation loop for software from the CSV -installationloop - -# Setup Dotfiles -putgitrepo "$dotfilesrepo" "$HOME" "$repobranch" && mv "$HOME/$(echo "$dotfilesrepo" | sed 's|.*/\([^/]*\)\.git|\1|')" "$HOME/.dotfiles" - -# Sync profile -ln -sf ~/.dotfiles/mac/.config/shell/profile ~/.zprofile -ln -sf ~/.dotfiles/mac/.config/bash/bash_profile ~/.bash_profile -ln -sf ~/.dotfiles/mac/.config/bash/bashrc ~/.bashrc +# Setup dotfiles with stow +setup_dotfiles() { + print_header "Setting up Dotfiles" + + print_step "Installing dotfiles with stow..." + + # Check if stow is available + if ! command_exists stow; then + print_warning "Stow not found, installing..." + installpkg stow + fi + + # Check if dotfiles directory exists + if [ ! -d ~/.dotfiles ]; then + error "Dotfiles directory not found: ~/.dotfiles" + fi + + cd ~/.dotfiles || error "Failed to change to dotfiles directory" + + # Install global configurations + print_step "Installing global configurations..." + if [ -d "global" ]; then + stow --no-folding -S global || error "Failed to stow global configs" + else + print_warning "Global configs directory not found, skipping..." + fi + + # Install macOS specific configurations + print_step "Installing macOS specific configurations..." + if [ -d "mac" ]; then + stow --no-folding -S mac || error "Failed to stow mac configs" + else + print_warning "Mac configs directory not found, skipping..." + fi + + cd - > /dev/null + print_success "Dotfiles setup completed" +} -cd ~/.dotfiles +# Finalize installation +finalize() { + print_header "Installation Complete!" + echo "" + echo "Congratulations! The installation has completed successfully." + echo "" + echo "What was installed:" + echo " - Homebrew and essential packages" + echo " - Mac App Store applications" + echo " - Python packages" + echo " - Shell configuration files" + echo " - Dotfiles and system configurations" + echo "" + echo "Next steps:" + echo " - Restart your terminal or run: source ~/.zprofile" + echo " - Customize your configuration files in ~/.dotfiles" + echo " - Install additional packages as needed" + echo "" + echo "For support, visit: https://github.com/TheSiahxyz/.dotfiles" + echo "" + echo "Best regards," + echo "Soomin" + echo "" + + notify "Installation Complete" "THESIAH setup finished successfully!" +} -stow --no-folding -S global -stow --no-folding -S mac +### MAIN INSTALLATION PROCESS ### -cd - +main() { + print_header "THESIAH - macOS Auto Setup Script" + echo "Starting installation process..." + echo "" + + # Check if running on macOS + if [ "$(uname)" != "Darwin" ]; then + error "This script is designed for macOS only" + fi + + # Check if running as root + if [ "$(id -u)" -eq 0 ]; then + error "This script should not be run as root" + fi + + # Ensure Homebrew is installed + brewinstall || error "Failed to install Homebrew" + + # Ensure mas is installed + installpkg "mas" + + # Start the installation loop for software from the CSV + installationloop + + # Setup Dotfiles + putgitrepo "$dotfilesrepo" "$HOME" "$repobranch" + + # Move dotfiles to proper location + dotfiles_dir=$(basename "$dotfilesrepo" .git) + if [ -d "$HOME/$dotfiles_dir" ]; then + mv "$HOME/$dotfiles_dir" "$HOME/.dotfiles" + fi + + # Setup shell configuration + setup_shell + + # Setup dotfiles with stow + setup_dotfiles + + # Finalize + finalize +} -# Final -finalize +# Run main function +main "$@" |
