Files
easyzsh/install.sh
T
Frank Qing 76395923ce fix(install): make setup failures explicit and atomic
Installer pipelines masked dependency and download failures, causing
false success messages and unsafe config replacement.

Atomic updates and current upstream installers keep failures visible.
2026-07-15 00:16:33 +08:00

424 lines
15 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
# Save the original working directory
ORIGINAL_DIR="$(pwd)"
# Flags to determine if the arguments were passed
cp_hist_flag=false
noninteractive_flag=false
# Loop through all arguments
for arg in "$@"
do
case $arg in
--cp-hist|-c)
cp_hist_flag=true
;;
--non-interactive|-n)
noninteractive_flag=true
;;
*)
echo "Unknown option: $arg" >&2
exit 2
;;
esac
done
if [ -f ~/.gitconfig ]; then
cp ~/.gitconfig ~/.gitconfig.backup
echo "Backup of .gitconfig created."
else
echo ".gitconfig does not exist, no backup created."
fi
# Function to detect the operating system
detect_os() {
if [[ "$OSTYPE" == "darwin"* ]]; then
echo "macos"
elif [[ -f /etc/os-release ]]; then
. /etc/os-release
echo "$ID"
else
echo "unknown"
fi
}
run_as_root() {
if (( EUID == 0 )); then
"$@"
elif command -v sudo &> /dev/null; then
sudo "$@"
else
echo "Error: sudo is required to run: $*" >&2
return 1
fi
}
install_eza_binary() {
local archive
local binary
local target
case $(uname -m) in
x86_64|amd64)
target="x86_64-unknown-linux-gnu"
;;
aarch64|arm64)
target="aarch64-unknown-linux-gnu"
;;
armv7*)
target="arm-unknown-linux-gnueabihf"
;;
*)
echo "Error: no prebuilt eza binary is available for $(uname -m)." >&2
return 1
;;
esac
archive=$(mktemp)
binary=$(mktemp)
trap 'rm -f "${archive:-}" "${binary:-}"' EXIT
curl -fsSL "https://github.com/eza-community/eza/releases/latest/download/eza_${target}.tar.gz" -o "$archive"
tar -xOzf "$archive" ./eza > "$binary"
run_as_root install -m 755 "$binary" /usr/local/bin/eza
rm -f "$archive" "$binary"
trap - EXIT
}
# Install eza using package manager or fallback methods
install_eza() {
local os
os=$(detect_os)
echo "Installing eza..."
case $os in
macos)
if ! command -v brew &> /dev/null; then
echo "Homebrew is not installed. Installing Homebrew..."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
fi
brew install eza
;;
ubuntu|debian)
run_as_root apt-get update
if ! run_as_root apt-get install -y eza; then
echo "Package manager installation failed, attempting fallback..."
install_eza_binary
fi
;;
fedora)
if ! run_as_root dnf install -y eza; then
echo "Package manager installation failed, attempting fallback..."
install_eza_binary
fi
;;
centos|rhel)
if ! run_as_root yum install -y eza; then
echo "Package manager installation failed, attempting fallback..."
install_eza_binary
fi
;;
arch|manjaro)
if ! run_as_root pacman -Syu --noconfirm eza; then
echo "Package manager installation failed, attempting fallback..."
install_eza_binary
fi
;;
*)
echo "Unsupported operating system for package manager installation, using fallback..."
install_eza_binary
;;
esac
}
# Helper: install zoxide if the package manager couldn't provide it
install_zoxide_binary() {
curl -sSfL https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh |
sh -s -- --bin-dir /usr/local/bin --man-dir /usr/local/share/man
}
install_zoxide_fallback() {
local os
os=$(detect_os)
echo "Attempting fallback installation of zoxide..."
case $os in
macos)
brew install zoxide
;;
fedora)
run_as_root dnf install -y zoxide || install_zoxide_binary
;;
centos|rhel)
if command -v dnf &> /dev/null && run_as_root dnf install -y zoxide; then :; else
install_zoxide_binary
fi
;;
arch|manjaro)
run_as_root pacman -S --needed --noconfirm zoxide
;;
*)
install_zoxide_binary
;;
esac
}
# Core: install required packages via the preferred package manager, then fall back where needed
install_packages() {
local os
local base_packages=(zsh git wget fontconfig autoconf)
os=$(detect_os)
case $os in
macos)
if ! command -v brew &> /dev/null; then
echo "Homebrew is not installed. Installing Homebrew..."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
fi
brew install "${base_packages[@]}"
;;
ubuntu|debian)
run_as_root apt-get update
run_as_root apt-get install -y "${base_packages[@]}" curl tar
;;
fedora)
run_as_root dnf install -y "${base_packages[@]}" curl tar
;;
centos|rhel)
run_as_root yum install -y "${base_packages[@]}" curl tar
;;
arch|manjaro)
run_as_root pacman -Syu --noconfirm "${base_packages[@]}" curl tar
;;
*)
echo "Unsupported operating system. Please install ${base_packages[*]} manually."
;;
esac
# Fallbacks for any tools that are still missing after the package-manager step
command -v eza &> /dev/null || install_eza
command -v zoxide &> /dev/null || install_zoxide_fallback
local command_name
for command_name in zsh git wget curl tar fc-cache eza zoxide autoconf; do
if ! command -v "$command_name" &> /dev/null; then
echo "Error: required command was not installed: $command_name" >&2
return 1
fi
done
}
# Check if required packages are installed, if not, install them
if command -v zsh &> /dev/null && command -v git &> /dev/null && command -v wget &> /dev/null && command -v curl &> /dev/null && command -v tar &> /dev/null && command -v fc-cache &> /dev/null && command -v eza &> /dev/null && command -v zoxide &> /dev/null && command -v autoconf &> /dev/null; then
echo -e "ZSH, Git, wget, curl, tar, fontconfig, eza, zoxide, and autoconf are already installed\n"
else
echo "Installing required packages..."
install_packages
echo -e "zsh, wget, curl, tar, git, fontconfig, eza, zoxide, and autoconf installed\n"
fi
if [ -f ~/.gitconfig.backup ]; then
mv ~/.gitconfig.backup ~/.gitconfig
echo "Restored .gitconfig from backup."
else
echo "No .gitconfig backup found to restore."
fi
ZSH="$HOME/.oh-my-zsh"
zshrc_download=$(mktemp "$HOME/.zshrc.download.XXXXXX")
trap 'rm -f "$zshrc_download"' EXIT
wget -q "https://git.miomio.moe/mio/easyzsh/raw/branch/master/.zshrc" -O "$zshrc_download"
# echo -e "The setup will be installed in '~/.config/ezsh'\n"
# echo -e "Place your personal zshrc config files under '~/.config/ezsh/zshrc/'\n"
# mkdir -p ~/.config/ezsh/zshrc # PLACE YOUR ZSHRC CONFIGURATIONS OVER THERE
# if [ -d ~/.quickzsh ]; then
# echo -e "\n PREVIOUS SETUP FOUND AT '~/.quickzsh'. PLEASE MANUALLY MOVE ANY FILES YOU'D LIKE TO '~/.config/ezsh' \n"
# fi
echo -e "Installing oh-my-zsh\n"
if [ -d "$ZSH" ]; then
echo -e "oh-my-zsh is already installed\n"
git -C "$ZSH" remote set-url origin https://github.com/ohmyzsh/ohmyzsh.git
# elif [ -d ~/.oh-my-zsh ]; then
# echo -e "oh-my-zsh in already installed at '~/.oh-my-zsh'. Moving it to '$ZSH'"
# export ZSH="$HOME/.config/ezsh/oh-my-zsh"
# mv ~/.oh-my-zsh $ZSH
# git -C $ZSH remote set-url origin https://github.com/ohmyzsh/ohmyzsh.git
else
git clone --depth=1 https://github.com/ohmyzsh/ohmyzsh.git "$ZSH"
fi
if [ -e "$HOME/.zshrc" ] || [ -L "$HOME/.zshrc" ]; then
zshrc_backup=$(mktemp "$HOME/.zshrc.backup.$(date +"%Y%m%d-%H%M%S").XXXXXX")
cp -Pp "$HOME/.zshrc" "$zshrc_backup"
echo -e "Backed up the current .zshrc to $zshrc_backup\n"
fi
mv "$zshrc_download" "$HOME/.zshrc"
trap - EXIT
# cp -f ezshrc.zsh ~/.config/ezsh/
# cp -f p10k.zsh ~/.config/ezsh/
mkdir -p ~/.cache/zsh/ # this will be used to store .zcompdump zsh completion cache files which normally clutter $HOME
mkdir -p ~/.fonts # Create .fonts if doesn't exist
if [ -f ~/.zcompdump ]; then
mv ~/.zcompdump* ~/.cache/zsh/
fi
if [ -d "$ZSH/plugins/zsh-autosuggestions" ]; then
git -C "$ZSH/plugins/zsh-autosuggestions" pull
else
git clone --depth=1 https://github.com/zsh-users/zsh-autosuggestions "$ZSH/plugins/zsh-autosuggestions"
fi
if [ -d "$ZSH/custom/plugins/zsh-syntax-highlighting" ]; then
git -C "$ZSH/custom/plugins/zsh-syntax-highlighting" pull
else
git clone --depth=1 https://github.com/zsh-users/zsh-syntax-highlighting.git "$ZSH/custom/plugins/zsh-syntax-highlighting"
fi
if [ -d "$ZSH/custom/plugins/zsh-completions" ]; then
git -C "$ZSH/custom/plugins/zsh-completions" pull
else
git clone --depth=1 https://github.com/zsh-users/zsh-completions "$ZSH/custom/plugins/zsh-completions"
fi
if [ -d "$ZSH/custom/plugins/zsh-history-substring-search" ]; then
git -C "$ZSH/custom/plugins/zsh-history-substring-search" pull
else
git clone --depth=1 https://github.com/zsh-users/zsh-history-substring-search "$ZSH/custom/plugins/zsh-history-substring-search"
fi
# INSTALL FONTS
echo -e "Installing Nerd Fonts version of Hack, Roboto Mono, DejaVu Sans Mono\n"
# Check and download Hack font if it doesn't exist
if [ ! -f ~/.fonts/HackNerdFont-Regular.ttf ]; then
echo "Downloading Hack Nerd Font..."
wget -q --show-progress -N https://github.com/ryanoasis/nerd-fonts/raw/master/patched-fonts/Hack/Regular/HackNerdFont-Regular.ttf -P ~/.fonts/
else
echo "Hack Nerd Font already exists, skipping download."
fi
# Check and download Roboto Mono font if it doesn't exist
if [ ! -f ~/.fonts/RobotoMonoNerdFont-Regular.ttf ]; then
echo "Downloading Roboto Mono Nerd Font..."
wget -q --show-progress -N https://github.com/ryanoasis/nerd-fonts/raw/master/patched-fonts/RobotoMono/Regular/RobotoMonoNerdFont-Regular.ttf -P ~/.fonts/
else
echo "Roboto Mono Nerd Font already exists, skipping download."
fi
# Check and download DejaVu Sans Mono font if it doesn't exist
if [ ! -f ~/.fonts/DejaVuSansMNerdFont-Regular.ttf ]; then
echo "Downloading DejaVu Sans Mono Nerd Font..."
wget -q --show-progress -N https://github.com/ryanoasis/nerd-fonts/raw/master/patched-fonts/DejaVuSansMono/Regular/DejaVuSansMNerdFont-Regular.ttf -P ~/.fonts/
else
echo "DejaVu Sans Mono Nerd Font already exists, skipping download."
fi
fc-cache -fv ~/.fonts
if [ -d "$ZSH/custom/themes/powerlevel10k" ]; then
git -C "$ZSH/custom/themes/powerlevel10k" pull
else
git clone --depth=1 https://github.com/romkatv/powerlevel10k.git "$ZSH/custom/themes/powerlevel10k"
fi
curl -fsSL https://git.miomio.moe/mio/easyzsh/raw/branch/master/patch.sh | bash -s -- p10k
if [ -d ~/.fzf ]; then
git -C ~/.fzf pull
~/.fzf/install --all --key-bindings --completion --no-update-rc
else
git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install --all --key-bindings --completion --no-update-rc
fi
if [ -d "$ZSH/custom/plugins/k" ]; then
git -C "$ZSH/custom/plugins/k" pull
else
git clone --depth 1 https://github.com/supercrabtree/k "$ZSH/custom/plugins/k"
fi
if [ -d "$ZSH/custom/plugins/fzf-tab" ]; then
git -C "$ZSH/custom/plugins/fzf-tab" pull
else
git clone --depth 1 https://github.com/Aloxaf/fzf-tab "$ZSH/custom/plugins/fzf-tab"
fi
# Import existing z data into zoxide if available
if command -v zoxide &> /dev/null; then
for z_file in ~/.z ~/.local/share/z/data ~/.zlua ~/.fasd; do
if [ -f "$z_file" ]; then
echo -e "Importing existing z data from $z_file into zoxide\n"
zoxide import --from=z "$z_file" 2>/dev/null || echo "Failed to import $z_file, continuing..."
break
fi
done
fi
# if git clone --depth 1 https://github.com/todotxt/todo.txt-cli.git ~/.config/ezsh/todo; then :
# else
# cd ~/.config/ezsh/todo && git fetch --all && git reset --hard origin/master
# fi
# mkdir ~/.config/ezsh/todo/bin ; cp -f ~/.config/ezsh/todo/todo.sh ~/.config/ezsh/todo/bin/todo.sh # cp todo.sh to ./bin so only it is included in $PATH
# #touch ~/.todo/config # needs it, otherwise spits error , yeah a bug in todo
# ln -s ~/.config/ezsh/todo ~/.todo
# if [ ! -L ~/.config/ezsh/todo/bin/todo.sh ]; then
# echo -e "Installing todo.sh in ~/.config/ezsh/todo\n"
# mkdir -p ~/.config/ezsh/bin
# mkdir -p ~/.config/ezsh/todo
# wget -q --show-progress "https://github.com/todotxt/todo.txt-cli/releases/download/v2.12.0/todo.txt_cli-2.12.0.tar.gz" -P ~/.config/ezsh/
# tar xvf ~/.config/ezsh/todo.txt_cli-2.12.0.tar.gz -C ~/.config/ezsh/todo --strip 1 && rm ~/.config/ezsh/todo.txt_cli-2.12.0.tar.gz
# ln -s -f ~/.config/ezsh/todo/todo.sh ~/.config/ezsh/bin/todo.sh # so only .../bin is included in $PATH
# ln -s -f ~/.config/ezsh/todo/todo.cfg ~/.todo.cfg # it expects it there or ~/todo.cfg or ~/.todo/config
# else
# echo -e "todo.sh is already instlled in ~/.config/ezsh/todo/bin/\n"
# fi
if [ "$cp_hist_flag" = true ]; then
echo -e "\nCopying bash_history to zsh_history\n"
if command -v python &>/dev/null; then
wget -q --show-progress https://gist.githubusercontent.com/muendelezaji/c14722ab66b505a49861b8a74e52b274/raw/49f0fb7f661bdf794742257f58950d209dd6cb62/bash-to-zsh-hist.py
cat ~/.bash_history | python bash-to-zsh-hist.py >> ~/.zsh_history
else
if command -v python3 &>/dev/null; then
wget -q --show-progress https://gist.githubusercontent.com/muendelezaji/c14722ab66b505a49861b8a74e52b274/raw/49f0fb7f661bdf794742257f58950d209dd6cb62/bash-to-zsh-hist.py
cat ~/.bash_history | python3 bash-to-zsh-hist.py >> ~/.zsh_history
else
printf "Python is not installed, can't copy bash_history to zsh_history\n"
fi
fi
else
echo -e "\nNot copying bash_history to zsh_history, as --cp-hist or -c is not supplied\n"
fi
if [ "$noninteractive_flag" = true ]; then
echo -e "Installation complete, exit terminal and enter a new zsh session\n"
echo -e "Make sure to change zsh to default shell by running: chsh -s $(command -v zsh)"
echo -e "In a new zsh session manually run: build-fzf-tab-module"
else
# source ~/.zshrc
echo -e "\nAdministrator access is needed to change the default shell\n"
if run_as_root chsh -s "$(command -v zsh)" "${USER:-$(id -un)}" && /bin/zsh -i -c 'omz update'; then
echo -e "Installation complete, exit terminal and enter a new zsh session"
echo -e "In a new zsh session manually run: build-fzf-tab-module"
else
echo -e "Something is wrong"
exit 1
fi
fi
# Restore original working directory
cd "$ORIGINAL_DIR"
exit