Last active
March 3, 2025 18:03
-
-
Save ZviBaratz/368984cd1df8050ec84f01c5a22cebbd to your computer and use it in GitHub Desktop.
Terminal setup
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# Cross-Platform Terminal Environment Setup Script | |
# | |
# This script automates the setup of the terminal environment with: | |
# - ZSH shell with Oh My Zsh framework | |
# - Powerlevel10k theme with optimized configuration | |
# - MesloLGS NF fonts for proper rendering | |
# - Useful plugins for productivity (autosuggestions, syntax highlighting, fzf) | |
# | |
# Supported platforms: | |
# - Ubuntu/Debian-based distributions | |
# - macOS | |
# - Amazon Linux 2 | |
# | |
# Usage: | |
# Direct execution: | |
# 1. Download the script. | |
# 2. Make it executable: | |
# chmod +x setup-terminal.sh | |
# 3. Run the script: | |
# ./setup-terminal.sh | |
# | |
# Remote execution: | |
# 1. Get the raw content URL from the GitHub gist (click 'Raw' button) | |
# 2. Execute with `curl`: | |
# bash -c "$(curl -fsSL <RAW_GIST_URL>)" | |
# or with `wget`: | |
# bash -c "$(wget -qO- <RAW_GIST_URL>)" | |
# | |
# Author: Zvi Baratz | |
# License: MIT | |
set -uo pipefail | |
# Colors | |
GREEN="\033[0;32m" | |
BLUE="\033[0;34m" | |
YELLOW="\033[0;33m" | |
GRAY="\e[38;5;248m" | |
BOLD="\033[1m" | |
RED="\033[0;31m" | |
CLEAR="\033[0m" | |
# Symbols | |
SUCCESS="${GREEN}✔${CLEAR}" | |
NO_ACTION="${GRAY}●${CLEAR}" | |
ERROR="${RED}✗${CLEAR}" | |
WARNING="${YELLOW}!${CLEAR}" | |
# Track if any changes were made | |
CHANGES_MADE=0 | |
# Backup directory | |
BACKUP_DIR="$HOME/.terminal_setup_backup_$(date +%Y%m%d_%H%M%S)" | |
# Detected OS variables | |
OS_TYPE="" | |
OS_NAME="" | |
OS_VERSION="" | |
PKG_MANAGER="" | |
################################################################################ | |
# Handles errors that occur during script execution. | |
# | |
# Args: | |
# line_number: The line number where the error occurred. | |
# command: The command that caused the error. | |
# | |
# Returns: | |
# None | |
# | |
# Exits: | |
# With the original error code. | |
################################################################################ | |
handle_error() | |
{ | |
local exit_code=$? | |
local line_number=$1 | |
local command=$2 | |
echo -e "\r${ERROR} Error occurred in line ${line_number}: ${command}" | |
exit $exit_code | |
} | |
################################################################################ | |
# Displays a progress message with optional status. | |
# | |
# Args: | |
# message: The message to display. | |
# status: The status symbol to use (SUCCESS, NO_ACTION, ERROR). | |
# | |
# Returns: | |
# None | |
################################################################################ | |
display_msg() | |
{ | |
local message="$1" | |
local status="${2:-}" | |
if [ -n "$status" ]; then | |
echo -e "\r${status} ${message}" | |
else | |
echo -ne "\r${message}..." | |
fi | |
} | |
################################################################################ | |
# Checks if the script is run with sudo and warns against it. | |
# | |
# Args: | |
# None | |
# | |
# Returns: | |
# None | |
# | |
# Exits: | |
# With code 1 if run with sudo. | |
################################################################################ | |
check_sudo() | |
{ | |
if [ "$(id -u)" -eq 0 ]; then | |
display_msg "This script should not be run directly with sudo." "$ERROR" | |
echo " The script will use sudo for specific commands as needed." | |
echo " Please run the script as your regular user." | |
exit 1 | |
fi | |
} | |
################################################################################ | |
# Creates a backup of a file if it exists. | |
# | |
# Args: | |
# file_path: The path to the file to back up. | |
# | |
# Returns: | |
# None | |
################################################################################ | |
backup_file() | |
{ | |
local file="$1" | |
if [ -f "$file" ]; then | |
mkdir -p "$BACKUP_DIR" | |
cp "$file" "$BACKUP_DIR/$(basename "$file").bak" | |
return 0 | |
fi | |
return 1 | |
} | |
################################################################################ | |
# Detects the operating system and sets OS_TYPE, OS_NAME, and PKG_MANAGER. | |
# | |
# Args: | |
# None | |
# | |
# Returns: | |
# 0 if the OS is detected and supported. | |
# 1 if the OS is not supported. | |
################################################################################ | |
detect_os() | |
{ | |
# Check for macOS | |
if [ "$(uname -s)" = "Darwin" ]; then | |
OS_TYPE="macos" | |
OS_NAME="macOS" | |
OS_VERSION=$(sw_vers -productVersion) | |
PKG_MANAGER="brew" | |
return 0 | |
fi | |
# Check for Linux distributions | |
if [ -f /etc/os-release ]; then | |
. /etc/os-release | |
# Check for Amazon Linux 2 | |
if [ -f /etc/system-release ] && grep -q "Amazon Linux" /etc/system-release; then | |
OS_TYPE="amazon" | |
OS_NAME="Amazon Linux" | |
OS_VERSION=$(cat /etc/system-release | grep -oE '[0-9]+\.[0-9]+' | head -1) | |
PKG_MANAGER="yum" | |
return 0 | |
# Check for Debian-based distributions | |
elif [[ "$ID" =~ ^(ubuntu|debian|elementary|linuxmint|pop|zorin)$ ]]; then | |
OS_TYPE="debian" | |
OS_NAME="$PRETTY_NAME" | |
OS_VERSION="$VERSION_ID" | |
PKG_MANAGER="apt" | |
return 0 | |
fi | |
fi | |
# Unknown or unsupported OS | |
OS_TYPE="unknown" | |
OS_NAME="Unknown OS" | |
display_msg "Your operating system could not be detected or is not officially supported." "$WARNING" | |
echo " This script officially supports Ubuntu/Debian-based systems, macOS, and Amazon Linux 2." | |
read -p " Do you want to continue anyway? [y/N] " -n 1 -r | |
echo | |
if [[ ! $REPLY =~ ^[Yy]$ ]]; then | |
display_msg "Installation aborted by user." "$ERROR" | |
exit 1 | |
fi | |
# Try to determine package manager for unknown Linux | |
if command -v apt >/dev/null 2>&1; then | |
PKG_MANAGER="apt" | |
elif command -v yum >/dev/null 2>&1; then | |
PKG_MANAGER="yum" | |
elif command -v dnf >/dev/null 2>&1; then | |
PKG_MANAGER="dnf" | |
else | |
display_msg "Could not determine package manager for your system" "$ERROR" | |
exit 1 | |
fi | |
return 0 | |
} | |
################################################################################ | |
# Installs Homebrew on macOS if it's not already installed. | |
# | |
# Args: | |
# None | |
# | |
# Returns: | |
# 0 if Homebrew is installed successfully or already present. | |
# 1 if installation fails. | |
################################################################################ | |
install_homebrew() | |
{ | |
if [ "$OS_TYPE" != "macos" ]; then | |
return 0 | |
fi | |
if command -v brew >/dev/null 2>&1; then | |
display_msg "Homebrew is already installed" "$NO_ACTION" | |
return 0 | |
fi | |
display_msg "Installing Homebrew" | |
local install_output | |
install_output=$(/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 2>&1) | |
local install_status=$? | |
if [ $install_status -ne 0 ]; then | |
display_msg "Failed to install Homebrew" "$ERROR" | |
echo " Error output: $install_output" | |
return 1 | |
fi | |
# Add Homebrew to PATH (this will only take effect in new terminal sessions) | |
if [ -d "/opt/homebrew/bin" ]; then | |
# For Apple Silicon Macs | |
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >>"$HOME/.zprofile" | |
eval "$(/opt/homebrew/bin/brew shellenv)" | |
elif [ -d "/usr/local/bin" ]; then | |
# For Intel Macs | |
echo 'eval "$(/usr/local/bin/brew shellenv)"' >>"$HOME/.zprofile" | |
eval "$(/usr/local/bin/brew shellenv)" | |
fi | |
display_msg "Homebrew installed successfully" "$SUCCESS" | |
CHANGES_MADE=1 | |
return 0 | |
} | |
################################################################################ | |
# Updates the package list and calculates if updates were available. | |
# | |
# Args: | |
# None | |
# | |
# Returns: | |
# 0 if successful. | |
# 1 if update fails. | |
################################################################################ | |
update_packages() | |
{ | |
display_msg "Updating package list" | |
local update_output="" | |
local update_status=0 | |
case "$PKG_MANAGER" in | |
apt) | |
update_output=$(sudo apt update 2>&1) | |
update_status=$? | |
;; | |
yum) | |
update_output=$(sudo yum check-update -y 2>&1) || true | |
# yum check-update returns 100 if updates are available, which is not an error | |
if [ $? -ne 0 ] && [ $? -ne 100 ]; then | |
update_status=1 | |
fi | |
;; | |
brew) | |
update_output=$(brew update 2>&1) | |
update_status=$? | |
;; | |
*) | |
display_msg "Unknown package manager: $PKG_MANAGER" "$ERROR" | |
return 1 | |
;; | |
esac | |
if [ $update_status -ne 0 ]; then | |
display_msg "Failed to update package list" "$ERROR" | |
echo " Error output: $update_output" | |
return 1 | |
fi | |
display_msg "Package list refreshed" "$SUCCESS" | |
return 0 | |
} | |
################################################################################ | |
# Installs a package if it is not already installed. | |
# | |
# Args: | |
# cmd: The command to check for existence. | |
# package_name: The name of the package to install (defaults to cmd if not provided). | |
# alt_pkg_names: Optional alternative package names for different package managers. | |
# | |
# Returns: | |
# 0 if installation successful or package already installed. | |
# 1 if installation fails. | |
################################################################################ | |
install_package() | |
{ | |
local cmd="${1}" | |
local default_pkg="${2:-$1}" | |
# Define platform-specific package names | |
local debian_pkg="${3:-$default_pkg}" | |
local amazon_pkg="${4:-$default_pkg}" | |
local macos_pkg="${5:-$default_pkg}" | |
# Select the appropriate package name based on OS | |
local package_name="" | |
case "$OS_TYPE" in | |
debian) | |
package_name="$debian_pkg" | |
;; | |
amazon) | |
package_name="$amazon_pkg" | |
;; | |
macos) | |
package_name="$macos_pkg" | |
;; | |
*) | |
package_name="$default_pkg" | |
;; | |
esac | |
if command -v "${cmd}" >/dev/null 2>&1; then | |
display_msg "${cmd} is already installed" "$NO_ACTION" | |
return 0 | |
fi | |
display_msg "Installing ${cmd}" | |
local install_output="" | |
local install_status=0 | |
case "$PKG_MANAGER" in | |
apt) | |
install_output=$(sudo apt install -y "${package_name}" 2>&1) | |
install_status=$? | |
;; | |
yum) | |
install_output=$(sudo yum install -y "${package_name}" 2>&1) | |
install_status=$? | |
;; | |
brew) | |
install_output=$(brew install "${package_name}" 2>&1) | |
install_status=$? | |
;; | |
*) | |
display_msg "Unknown package manager: $PKG_MANAGER" "$ERROR" | |
return 1 | |
;; | |
esac | |
if [ $install_status -ne 0 ]; then | |
display_msg "Failed to install ${cmd}" "$ERROR" | |
echo " Error output: $install_output" | |
return 1 | |
fi | |
if ! command -v "${cmd}" >/dev/null 2>&1; then | |
display_msg "Package ${package_name} was installed but ${cmd} command is not available" "$ERROR" | |
return 1 | |
fi | |
display_msg "${cmd} installed successfully" "$SUCCESS" | |
CHANGES_MADE=1 | |
return 0 | |
} | |
################################################################################ | |
# Installs Oh My Zsh if not already installed. | |
# | |
# Args: | |
# None | |
# | |
# Returns: | |
# 0 if installation successful or already installed. | |
# 1 if installation fails. | |
################################################################################ | |
install_oh_my_zsh() | |
{ | |
if [ -d "$HOME/.oh-my-zsh" ]; then | |
display_msg "Oh My Zsh is already installed" "$NO_ACTION" | |
return 0 | |
fi | |
display_msg "Installing Oh My Zsh" | |
# Backup .zshrc if it exists | |
backup_file "$HOME/.zshrc" | |
# Using curl with error handling | |
local install_output | |
install_output=$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh 2>&1) | |
if [ $? -ne 0 ]; then | |
display_msg "Failed to download Oh My Zsh installation script" "$ERROR" | |
echo " Error output: $install_output" | |
return 1 | |
fi | |
# Install Oh My Zsh | |
local omz_install_output | |
omz_install_output=$(RUNZSH=no KEEP_ZSHRC=yes sh -c "$install_output" 2>&1) | |
local omz_status=$? | |
if [ $omz_status -ne 0 ]; then | |
display_msg "Failed to install Oh My Zsh" "$ERROR" | |
echo " Error output: $omz_install_output" | |
return 1 | |
fi | |
display_msg "Oh My Zsh installed successfully" "$SUCCESS" | |
CHANGES_MADE=1 | |
return 0 | |
} | |
################################################################################ | |
# Installs Powerlevel10k theme for Oh My Zsh. | |
# | |
# Args: | |
# None | |
# | |
# Returns: | |
# 0 if installation successful or already installed. | |
# 1 if installation fails. | |
################################################################################ | |
install_powerlevel10k() | |
{ | |
local p10k_dir="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k" | |
if [ -d "$p10k_dir" ] && [ -n "$(ls -A "$p10k_dir" 2>/dev/null)" ]; then | |
display_msg "Powerlevel10k is already installed" "$NO_ACTION" | |
else | |
display_msg "Installing Powerlevel10k" | |
local clone_output | |
clone_output=$(git clone --depth=1 https://github.com/romkatv/powerlevel10k.git "$p10k_dir" 2>&1) | |
local clone_status=$? | |
if [ $clone_status -ne 0 ]; then | |
display_msg "Failed to install Powerlevel10k" "$ERROR" | |
echo " Error output: $clone_output" | |
return 1 | |
fi | |
# Check if the p10k.zsh source line already exists in .zshrc to avoid duplicates | |
if ! grep -q "source.*p10k.zsh" "$HOME/.zshrc"; then | |
echo '[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh' >>"$HOME/.zshrc" | |
fi | |
display_msg "Powerlevel10k installed successfully" "$SUCCESS" | |
CHANGES_MADE=1 | |
fi | |
return 0 | |
} | |
################################################################################ | |
# Downloads and installs the p10k configuration file. | |
# | |
# Args: | |
# None | |
# | |
# Returns: | |
# 0 if configuration is installed successfully or already present. | |
# 1 if installation fails. | |
################################################################################ | |
install_p10k_config() | |
{ | |
if [ -f "$HOME/.p10k.zsh" ]; then | |
display_msg "Existing Powerlevel10k configuration file found" "$NO_ACTION" | |
return 0 | |
fi | |
display_msg "Downloading p10k configuration" | |
local p10k_config_url="https://gist.githubusercontent.com/ZviBaratz/9529600d91695315c224263808b13ded/raw/64238295b34e3643ece0819f7a6a5f319a0e9b43/.p10k.zsh" | |
local download_output | |
download_output=$(wget -q -O "$HOME/.p10k.zsh" "$p10k_config_url" 2>&1) | |
local download_status=$? | |
if [ $download_status -ne 0 ]; then | |
display_msg "Failed to download p10k configuration" "$ERROR" | |
echo " Error output: $download_output" | |
return 1 | |
fi | |
if [ ! -s "$HOME/.p10k.zsh" ]; then | |
display_msg "Downloaded p10k configuration file is empty" "$ERROR" | |
return 1 | |
fi | |
display_msg "p10k configuration downloaded successfully" "$SUCCESS" | |
CHANGES_MADE=1 | |
return 0 | |
} | |
################################################################################ | |
# Sets the ZSH theme to Powerlevel10k in the .zshrc file. | |
# | |
# Args: | |
# None | |
# | |
# Returns: | |
# 0 if theme is set successfully or already set. | |
# 1 if setting the theme fails. | |
################################################################################ | |
set_zsh_theme() | |
{ | |
# First check if the .zshrc file exists | |
if [ ! -f "$HOME/.zshrc" ]; then | |
display_msg "No .zshrc file found, cannot set theme" "$ERROR" | |
return 1 | |
fi | |
# Use direct approach - look for any line that: | |
# 1. Isn't commented (doesn't start with #) | |
# 2. Contains ZSH_THEME= | |
# 3. Contains powerlevel10k | |
if grep -v "^[[:space:]]*#" "$HOME/.zshrc" | grep -q "ZSH_THEME.*powerlevel10k"; then | |
display_msg "Zsh theme already set to Powerlevel10k" "$NO_ACTION" | |
return 0 | |
fi | |
display_msg "Setting Zsh theme to Powerlevel10k" | |
# Backup .zshrc before modifying | |
backup_file "$HOME/.zshrc" | |
# Handle macOS sed differences | |
if [ "$OS_TYPE" = "macos" ]; then | |
sed -i '' 's/^ZSH_THEME=.*$/ZSH_THEME="powerlevel10k\/powerlevel10k"/' "$HOME/.zshrc" | |
else | |
sed -i 's/^ZSH_THEME=.*$/ZSH_THEME="powerlevel10k\/powerlevel10k"/' "$HOME/.zshrc" | |
fi | |
# Check if the sed command actually made a change | |
if ! grep -q 'ZSH_THEME="powerlevel10k/powerlevel10k"' "$HOME/.zshrc"; then | |
# If sed didn't work (perhaps because there was no ZSH_THEME line), | |
# append the theme to the end of the file | |
echo 'ZSH_THEME="powerlevel10k/powerlevel10k"' >>"$HOME/.zshrc" | |
fi | |
display_msg "Zsh theme set to Powerlevel10k" "$SUCCESS" | |
CHANGES_MADE=1 | |
return 0 | |
} | |
################################################################################ | |
# Returns the appropriate font directory for the current OS. | |
# | |
# Args: | |
# None | |
# | |
# Returns: | |
# The path to the font directory. | |
################################################################################ | |
get_font_dir() | |
{ | |
case "$OS_TYPE" in | |
macos) | |
echo "$HOME/Library/Fonts" | |
;; | |
*) | |
echo "$HOME/.fonts" | |
;; | |
esac | |
} | |
################################################################################ | |
# Downloads and installs the MesloLGS NF fonts for Powerlevel10k. | |
# | |
# Args: | |
# None | |
# | |
# Returns: | |
# 0 if fonts are installed successfully or already present. | |
# 1 if any step of the installation fails. | |
################################################################################ | |
install_p10k_fonts() | |
{ | |
local fonts_dir=$(get_font_dir) | |
local fonts_downloaded=0 | |
local p10k_media_url="https://github.com/romkatv/powerlevel10k-media/raw/master" | |
# Create fonts directory if it doesn't exist | |
if ! mkdir -p "$fonts_dir"; then | |
display_msg "Failed to create fonts directory" "$ERROR" | |
return 1 | |
fi | |
# Download each font variant | |
for font in "Regular" "Bold" "Italic" "Bold Italic"; do | |
local font_file="MesloLGS NF ${font}.ttf" | |
local font_path="${fonts_dir}/${font_file}" | |
local font_url="${font// /%20}" | |
if [ -f "$font_path" ]; then | |
display_msg "Font ${font_file} already exists" "$NO_ACTION" | |
continue | |
fi | |
display_msg "Downloading ${font_file}" | |
local download_output | |
download_output=$(wget --no-clobber --quiet \ | |
-O "$font_path" \ | |
"${p10k_media_url}/MesloLGS%20NF%20${font_url}.ttf" 2>&1) | |
local download_status=$? | |
if [ $download_status -ne 0 ]; then | |
display_msg "Failed to download ${font_file}" "$ERROR" | |
echo " Error output: $download_output" | |
return 1 | |
fi | |
display_msg "${font_file} downloaded successfully" "$SUCCESS" | |
fonts_downloaded=1 | |
CHANGES_MADE=1 | |
done | |
# Update font cache if new fonts were downloaded and OS is not macOS | |
if [ "$fonts_downloaded" -eq 1 ] && [ "$OS_TYPE" != "macos" ]; then | |
display_msg "Updating font cache" | |
local fc_output | |
fc_output=$(fc-cache -f -v 2>&1) | |
local fc_status=$? | |
if [ $fc_status -ne 0 ]; then | |
display_msg "Failed to update font cache" "$ERROR" | |
echo " Error output: $fc_output" | |
return 1 | |
fi | |
display_msg "Font cache updated successfully" "$SUCCESS" | |
else | |
display_msg "All MesloLGS NF fonts are already installed" "$NO_ACTION" | |
fi | |
return 0 | |
} | |
################################################################################ | |
# Installs a Zsh plugin and adds it to the .zshrc configuration. | |
# | |
# Args: | |
# plugin_name: The name of the plugin to install. | |
# repo_url: The Git repository URL of the plugin. | |
# | |
# Returns: | |
# 0 if plugin is installed successfully or already present. | |
# 1 if installation or configuration fails. | |
################################################################################ | |
install_zsh_plugin() | |
{ | |
local plugin_name="$1" | |
local repo_url="$2" | |
local plugin_dir="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins/${plugin_name}" | |
if [ -d "$plugin_dir" ] && [ -n "$(ls -A "$plugin_dir" 2>/dev/null)" ]; then | |
display_msg "${plugin_name} plugin is already installed" "$NO_ACTION" | |
return 0 | |
fi | |
display_msg "Installing ${plugin_name} plugin" | |
local clone_output | |
clone_output=$(git clone "$repo_url" "$plugin_dir" 2>&1) | |
local clone_status=$? | |
if [ $clone_status -ne 0 ]; then | |
display_msg "Failed to install ${plugin_name} plugin" "$ERROR" | |
echo " Error output: $clone_output" | |
return 1 | |
fi | |
# Add plugin to .zshrc plugins list if not already present | |
if ! grep -q "${plugin_name}" "$HOME/.zshrc"; then | |
# Backup .zshrc before modifying | |
backup_file "$HOME/.zshrc" | |
# More robust plugin addition using awk | |
awk -v plugin="$plugin_name" ' | |
/^plugins=\(/ { | |
# Check if line ends with closing parenthesis | |
if ($0 ~ /\)$/) { | |
# Remove trailing parenthesis, add plugin and closing parenthesis | |
sub(/\)$/, " " plugin ")") | |
} else { | |
# This is a multi-line plugin declaration, just add to the first line | |
sub(/$/, " " plugin) | |
} | |
} | |
{ print } | |
' "$HOME/.zshrc" >"$HOME/.zshrc.tmp" | |
if [ $? -ne 0 ]; then | |
display_msg "Failed to modify plugins in .zshrc" "$ERROR" | |
rm -f "$HOME/.zshrc.tmp" | |
return 1 | |
fi | |
mv "$HOME/.zshrc.tmp" "$HOME/.zshrc" | |
fi | |
display_msg "${plugin_name} plugin installed successfully" "$SUCCESS" | |
CHANGES_MADE=1 | |
return 0 | |
} | |
################################################################################ | |
# Sets the default shell to Zsh if it isn't already. | |
# | |
# Args: | |
# None | |
# | |
# Returns: | |
# 0 if shell is set successfully or already set to Zsh. | |
# 1 if setting the shell fails. | |
################################################################################ | |
set_default_shell() | |
{ | |
local zsh_path | |
zsh_path=$(which zsh) | |
# Check if zsh is already the default shell | |
if [[ "$SHELL" == *zsh* ]] || grep -q "^${USER}.*zsh$" /etc/passwd 2>/dev/null; then | |
display_msg "Default shell is already Zsh" "$NO_ACTION" | |
return 0 | |
fi | |
display_msg "Setting default shell to Zsh" | |
local chsh_output | |
local chsh_status | |
# Different commands for different OS types | |
case "$OS_TYPE" in | |
macos) | |
# macOS doesn't need sudo for chsh | |
chsh_output=$(chsh -s "$zsh_path" 2>&1) | |
chsh_status=$? | |
;; | |
amazon) | |
# For Amazon Linux, check if zsh is in /etc/shells | |
if ! grep -q "^$zsh_path$" /etc/shells; then | |
echo "$zsh_path" | sudo tee -a /etc/shells >/dev/null | |
fi | |
# Try to change the shell using chsh first (this might not work in all EC2 environments) | |
chsh_output=$(sudo chsh -s "$zsh_path" "$USER" 2>&1) || true | |
chsh_status=$? | |
# Track if we actually made changes | |
local files_changed=0 | |
# Make sure to add to all possible startup files for EC2 instances | |
for file in ~/.bash_profile ~/.bashrc ~/.profile; do | |
if [ -f "$file" ] && ! grep -q "# Start zsh as default shell (added by terminal setup script)" "$file"; then | |
# Back up the file | |
cp "$file" "${file}.bak" | |
# Add the exec zsh command but allow for a way to bypass it if needed | |
echo "" >>"$file" | |
echo "# Start zsh as default shell (added by terminal setup script)" >>"$file" | |
echo "if [ -x $(which zsh) ] && [[ \"\$ZSH_VERSION\" == \"\" ]] && [[ \"\$TERM_PROGRAM\" != \"vscode\" ]] && [[ -z \"\$BASH_ONLY\" ]]; then" >>"$file" | |
echo " export SHELL=$(which zsh)" >>"$file" | |
echo " exec zsh" >>"$file" | |
echo "fi" >>"$file" | |
files_changed=1 | |
fi | |
done | |
if [ $files_changed -eq 1 ]; then | |
display_msg "Configured login files to start Zsh" "$SUCCESS" | |
CHANGES_MADE=1 | |
else | |
display_msg "Login files already configured for Zsh" "$NO_ACTION" | |
fi | |
;; | |
*) | |
# Other Linux systems | |
chsh_output=$(sudo chsh -s "$zsh_path" "$USER" 2>&1) | |
chsh_status=$? | |
;; | |
esac | |
if [ $chsh_status -ne 0 ] && [ "$OS_TYPE" != "amazon" ]; then | |
display_msg "Failed to change default shell to Zsh" "$WARNING" | |
echo " This is a common issue on some systems." | |
echo " You can manually change your shell by running: chsh -s $(which zsh)" | |
echo " Alternatively, you can add 'exec zsh' to your .bashrc to automatically start zsh" | |
# Add exec zsh to bashrc if it doesn't exist | |
if [ ! -f "$HOME/.bashrc" ] || ! grep -q "exec zsh" "$HOME/.bashrc"; then | |
echo "# Auto-start zsh (added by terminal setup script)" >>"$HOME/.bashrc" | |
echo "[ -f $(which zsh) ] && exec zsh" >>"$HOME/.bashrc" | |
display_msg "Added 'exec zsh' to .bashrc as a fallback" "$SUCCESS" | |
CHANGES_MADE=1 | |
fi | |
elif [ "$OS_TYPE" != "amazon" ]; then | |
display_msg "Default shell set to Zsh" "$SUCCESS" | |
CHANGES_MADE=1 | |
fi | |
return 0 | |
} | |
################################################################################ | |
# Main function that runs the script. | |
# | |
# Args: | |
# None | |
# | |
# Returns: | |
# 0 if all steps complete successfully. | |
# Various error codes if any step fails. | |
################################################################################ | |
main() | |
{ | |
# Set up error handling | |
trap 'handle_error ${LINENO} "$BASH_COMMAND"' ERR | |
echo -e "${BOLD}Cross-Platform Terminal Environment Setup${CLEAR}" | |
echo "This script will set up the terminal environment with Zsh, Oh My Zsh, and Powerlevel10k." | |
# Preliminary checks | |
check_sudo | |
detect_os | |
echo -e "${BLUE}${BOLD}Detected OS:${CLEAR} $OS_NAME" | |
# Create a section separator for visual organization | |
echo -e "\n${BLUE}${BOLD}System Preparation${CLEAR}" | |
# Install Homebrew for macOS | |
if [ "$OS_TYPE" = "macos" ]; then | |
install_homebrew | |
fi | |
# Update package repositories | |
update_packages | |
# Install required packages | |
echo -e "\n${BLUE}${BOLD}Installing Required Packages${CLEAR}" | |
# Install fontconfig (not needed on macOS) | |
if [ "$OS_TYPE" != "macos" ]; then | |
install_package "fc-cache" "fontconfig" "fontconfig" "fontconfig" | |
fi | |
# Install other required packages with platform-specific names | |
install_package "zsh" "zsh" "zsh" "zsh" "zsh" | |
install_package "git" "git" "git" "git" "git" | |
install_package "wget" "wget" "wget" "wget" "wget" | |
# Handle fzf installation differently based on OS | |
if [ "$OS_TYPE" = "amazon" ]; then | |
display_msg "Installing fzf on Amazon Linux 2" | |
# Check if fzf is already installed | |
if command -v fzf >/dev/null 2>&1; then | |
display_msg "fzf is already installed" "$NO_ACTION" | |
else | |
# Install fzf from git for Amazon Linux | |
if [ ! -d "$HOME/.fzf" ]; then | |
# Suppress detailed output | |
git clone --depth 1 https://github.com/junegunn/fzf.git "$HOME/.fzf" >/dev/null 2>&1 | |
"$HOME/.fzf/install" --all >/dev/null 2>&1 | |
display_msg "fzf installed successfully" "$SUCCESS" | |
CHANGES_MADE=1 | |
else | |
display_msg "fzf directory exists but command not found, running installer" | |
"$HOME/.fzf/install" --all >/dev/null 2>&1 | |
display_msg "fzf installed successfully" "$SUCCESS" | |
CHANGES_MADE=1 | |
fi | |
fi | |
else | |
# For other systems, use package manager | |
install_package "fzf" "fzf" "fzf" "fzf" "fzf" | |
fi | |
install_package "curl" "curl" "curl" "curl" "curl" | |
# Install Oh My Zsh and configure it | |
echo -e "\n${BLUE}${BOLD}Setting Up Zsh Environment${CLEAR}" | |
install_oh_my_zsh | |
install_powerlevel10k | |
install_p10k_config | |
set_zsh_theme | |
# Install fonts | |
echo -e "\n${BLUE}${BOLD}Installing Custom Fonts${CLEAR}" | |
install_p10k_fonts | |
# Install plugins | |
echo -e "\n${BLUE}${BOLD}Installing Zsh Plugins${CLEAR}" | |
install_zsh_plugin "zsh-autosuggestions" "https://github.com/zsh-users/zsh-autosuggestions" | |
install_zsh_plugin "zsh-fzf-history-search" "https://github.com/joshskidmore/zsh-fzf-history-search" | |
install_zsh_plugin "zsh-syntax-highlighting" "https://github.com/zsh-users/zsh-syntax-highlighting.git" | |
# Set default shell | |
echo -e "\n${BLUE}${BOLD}Finalizing Setup${CLEAR}" | |
set_default_shell | |
# Display completion message | |
echo | |
if [ "$CHANGES_MADE" -eq 1 ]; then | |
echo -e "${GREEN}${BOLD}✨ Terminal setup completed successfully! ✨${CLEAR}" | |
# Check if backup was created | |
if [ -d "$BACKUP_DIR" ]; then | |
echo -e "${YELLOW}Backup of original configuration files created at:${CLEAR}" | |
echo " $BACKUP_DIR" | |
fi | |
echo -e "\n${BOLD}Next steps:${CLEAR}" | |
echo "1. Close this terminal and open a new one to activate Zsh" | |
echo "2. If the Powerlevel10k configuration wizard starts, follow the prompts to customize your theme" | |
echo "3. Alternatively, your terminal should already be configured with the downloaded settings" | |
echo | |
echo -e "${YELLOW}Note:${CLEAR} To see all the new features, make sure your terminal app is using one of the MesloLGS NF fonts" | |
# Platform-specific instructions | |
case "$OS_TYPE" in | |
macos) | |
echo -e "\n${BOLD}macOS-Specific Instructions:${CLEAR}" | |
echo "1. Open Terminal.app preferences" | |
echo "2. Go to 'Profiles' > Select your profile > 'Text'" | |
echo "3. Click 'Change...' next to the font and select 'MesloLGS NF'" | |
;; | |
amazon) | |
echo -e "\n${BOLD}Amazon Linux-Specific Instructions:${CLEAR}" | |
echo "1. If using EC2 with SSH, configure your local terminal to use MesloLGS NF font" | |
echo "2. For AWS CloudShell, you may need to adjust font settings in your browser" | |
;; | |
esac | |
else | |
echo -e "${BOLD}Terminal setup completed (no changes needed)${CLEAR}" | |
echo "Your system is already configured with all the requested components." | |
fi | |
return 0 | |
} | |
main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment