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.
This commit is contained in:
Frank Qing
2026-07-15 00:16:33 +08:00
parent 9641876d43
commit 76395923ce
14 changed files with 350 additions and 432 deletions
+136 -84
View File
@@ -1,5 +1,7 @@
#!/bin/bash
set -euo pipefail
# Save the original working directory
ORIGINAL_DIR="$(pwd)"
@@ -18,7 +20,8 @@ do
noninteractive_flag=true
;;
*)
# Handle any other arguments or provide an error message
echo "Unknown option: $arg" >&2
exit 2
;;
esac
done
@@ -42,9 +45,52 @@ detect_os() {
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=$(detect_os)
local os
os=$(detect_os)
echo "Installing eza..."
case $os in
@@ -56,80 +102,73 @@ install_eza() {
brew install eza
;;
ubuntu|debian)
sudo apt-get update
if ! sudo apt-get install -y eza; then
run_as_root apt-get update
if ! run_as_root apt-get install -y eza; then
echo "Package manager installation failed, attempting fallback..."
# Install cargo if it doesn't exist
if ! command -v cargo &> /dev/null; then
echo "Installing Rust and cargo..."
sudo apt-get update
sudo apt-get install -y curl build-essential
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source ~/.cargo/env
fi
cargo install eza
install_eza_binary
fi
;;
fedora)
if ! sudo dnf install -y eza; then
if ! run_as_root dnf install -y eza; then
echo "Package manager installation failed, attempting fallback..."
if command -v cargo &> /dev/null; then
cargo install eza
else
wget -qO- https://github.com/eza-community/eza/releases/latest/download/eza_x86_64-unknown-linux-gnu.tar.gz | tar xz
sudo install -m 755 eza /usr/local/bin/eza
fi
install_eza_binary
fi
;;
centos|rhel)
if ! sudo yum install -y eza; then
if ! run_as_root yum install -y eza; then
echo "Package manager installation failed, attempting fallback..."
if command -v cargo &> /dev/null; then
cargo install eza
else
wget -qO- https://github.com/eza-community/eza/releases/latest/download/eza_x86_64-unknown-linux-gnu.tar.gz | tar xz
sudo install -m 755 eza /usr/local/bin/eza
fi
install_eza_binary
fi
;;
arch|manjaro)
if ! sudo pacman -Syu --noconfirm eza; then
if ! run_as_root pacman -Syu --noconfirm eza; then
echo "Package manager installation failed, attempting fallback..."
wget -qO- https://github.com/eza-community/eza/releases/latest/download/eza_x86_64-unknown-linux-gnu.tar.gz | tar xz
sudo install -m 755 eza /usr/local/bin/eza
install_eza_binary
fi
;;
*)
echo "Unsupported operating system for package manager installation, using fallback..."
wget -qO- https://github.com/eza-community/eza/releases/latest/download/eza_x86_64-unknown-linux-gnu.tar.gz | tar xz
sudo install -m 755 eza /usr/local/bin/eza
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=$(detect_os)
local os
os=$(detect_os)
echo "Attempting fallback installation of zoxide..."
case $os in
macos)
brew install zoxide
;;
fedora)
sudo dnf install -y zoxide || curl -sSfL https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | sh
run_as_root dnf install -y zoxide || install_zoxide_binary
;;
centos|rhel)
if command -v dnf &> /dev/null && sudo dnf install -y zoxide; then :; else
curl -sSfL https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | sh
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
;;
*)
curl -sSfL https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | sh
install_zoxide_binary
;;
esac
}
# Core: install required packages via the preferred package manager, then fall back where needed
install_packages() {
local os=$(detect_os)
local base_packages="zsh git wget fontconfig zoxide autoconf"
local os
local base_packages=(zsh git wget fontconfig autoconf)
os=$(detect_os)
case $os in
macos)
@@ -137,38 +176,46 @@ install_packages() {
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}
brew install "${base_packages[@]}"
;;
ubuntu|debian)
sudo apt-get update
sudo apt-get install -y ${base_packages}
run_as_root apt-get update
run_as_root apt-get install -y "${base_packages[@]}" curl tar
;;
fedora)
sudo dnf install -y ${base_packages}
run_as_root dnf install -y "${base_packages[@]}" curl tar
;;
centos|rhel)
sudo yum install -y ${base_packages}
run_as_root yum install -y "${base_packages[@]}" curl tar
;;
arch|manjaro)
sudo pacman -Syu --noconfirm ${base_packages}
run_as_root pacman -Syu --noconfirm "${base_packages[@]}" curl tar
;;
*)
echo "Unsupported operating system. Please install ${base_packages} manually."
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 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, fontconfig, eza, zoxide, and autoconf are already installed\n"
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, git, fontconfig, eza, zoxide, and autoconf Installed\n"
echo -e "zsh, wget, curl, tar, git, fontconfig, eza, zoxide, and autoconf installed\n"
fi
if [ -f ~/.gitconfig.backup ]; then
@@ -181,9 +228,9 @@ fi
ZSH="$HOME/.oh-my-zsh"
if mv -n ~/.zshrc ~/.zshrc-backup-$(date +"%Y-%m-%d"); then # backup .zshrc
echo -e "Backed up the current .zshrc to .zshrc-backup-date\n"
fi
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"
@@ -194,20 +241,25 @@ fi
# fi
echo -e "Installing oh-my-zsh\n"
if [ -d $ZSH ]; then
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
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
git clone --depth=1 https://github.com/ohmyzsh/ohmyzsh.git "$ZSH"
fi
# cp -f .zshrc ~/
wget -q "https://git.miomio.moe/mio/easyzsh/raw/branch/master/.zshrc" -O ~/.zshrc
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/
@@ -219,28 +271,28 @@ if [ -f ~/.zcompdump ]; then
mv ~/.zcompdump* ~/.cache/zsh/
fi
if [ -d $ZSH/plugins/zsh-autosuggestions ]; then
git -C $ZSH/plugins/zsh-autosuggestions pull
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
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
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
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
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
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
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
git clone --depth=1 https://github.com/zsh-users/zsh-history-substring-search "$ZSH/custom/plugins/zsh-history-substring-search"
fi
@@ -274,12 +326,12 @@ fi
fc-cache -fv ~/.fonts
if [ -d $ZSH/custom/themes/powerlevel10k ]; then
git -C $ZSH/custom/themes/powerlevel10k pull
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
git clone --depth=1 https://github.com/romkatv/powerlevel10k.git "$ZSH/custom/themes/powerlevel10k"
fi
curl -s https://git.miomio.moe/mio/easyzsh/raw/branch/master/patch.sh | bash -s p10k
curl -fsSL https://git.miomio.moe/mio/easyzsh/raw/branch/master/patch.sh | bash -s -- p10k
if [ -d ~/.fzf ]; then
git -C ~/.fzf pull
@@ -289,16 +341,16 @@ else
~/.fzf/install --all --key-bindings --completion --no-update-rc
fi
if [ -d $ZSH/custom/plugins/k ]; then
git -C $ZSH/custom/plugins/k pull
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
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
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
git clone --depth 1 https://github.com/Aloxaf/fzf-tab "$ZSH/custom/plugins/fzf-tab"
fi
# Import existing z data into zoxide if available
@@ -342,7 +394,7 @@ if [ "$cp_hist_flag" = true ]; 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
echo "Python is not installed, can't copy bash_history to zsh_history\n"
printf "Python is not installed, can't copy bash_history to zsh_history\n"
fi
fi
else
@@ -351,18 +403,18 @@ 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 $(which zsh)"
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 "\nSudo access is needed to change default shell\n"
echo -e "\nAdministrator access is needed to change the default shell\n"
if sudo chsh -s $(which zsh) $USER && /bin/zsh -i -c 'omz update'; then
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