Amended
This commit is contained in:
@@ -1,261 +1,184 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -euo pipefail
|
set -e
|
||||||
|
|
||||||
# ====================== Flags ======================
|
# 🎯 Detect OS & package manager
|
||||||
WITH_TIDE=0
|
|
||||||
KEEP_PROMPT=0
|
|
||||||
REMOVE_TIDE=0
|
|
||||||
VERBOSE=0
|
|
||||||
NO_PLUGINS=0
|
|
||||||
UPDATE_MODE="prompt" # prompt | yes | no
|
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
|
||||||
case "$1" in
|
|
||||||
--with-tide) WITH_TIDE=1 ;;
|
|
||||||
--keep-prompt) KEEP_PROMPT=1 ;;
|
|
||||||
--remove-tide) REMOVE_TIDE=1 ;;
|
|
||||||
--verbose) VERBOSE=1 ;;
|
|
||||||
--no-plugins) NO_PLUGINS=1 ;;
|
|
||||||
--update-check) UPDATE_MODE="yes" ;;
|
|
||||||
--no-update-check) UPDATE_MODE="no" ;;
|
|
||||||
--) shift; break ;;
|
|
||||||
*) echo "Unknown option: $1"; exit 1 ;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
# Default plugin set (safe; no prompt change)
|
|
||||||
PLUGINS=("jethrokuan/z" "jorgebucaran/nvm.fish")
|
|
||||||
[[ $WITH_TIDE -eq 1 ]] && PLUGINS+=("ilancosman/tide")
|
|
||||||
|
|
||||||
# ====================== UI Helpers ======================
|
|
||||||
log() { echo -e "• $*"; }
|
|
||||||
ok() { echo -e "✓ $*"; }
|
|
||||||
warn(){ echo -e "! $*" >&2; }
|
|
||||||
|
|
||||||
padright() { printf "%-28s" "$1"; }
|
|
||||||
draw_bar() {
|
|
||||||
local cur="$1" total="$2" label="$3" width=40
|
|
||||||
(( total == 0 )) && total=1
|
|
||||||
local percent=$(( cur * 100 / total ))
|
|
||||||
(( percent > 100 )) && percent=100
|
|
||||||
local filled=$(( width * cur / total ))
|
|
||||||
(( filled > width )) && filled=$width
|
|
||||||
local empty=$(( width - filled ))
|
|
||||||
printf "\rInstalling: %s [%s%s] %3d%%" \
|
|
||||||
"$(padright "$label")" \
|
|
||||||
"$(printf '%0.s#' $(seq 1 $filled))" \
|
|
||||||
"$(printf '%0.s.' $(seq 1 $empty))" \
|
|
||||||
"$percent"
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt_yn_tty() {
|
|
||||||
local prompt="${1:-Proceed (y/N): }"
|
|
||||||
local d="${2:-N}" ans=""
|
|
||||||
if [ -r /dev/tty ] && [ -w /dev/tty ]; then
|
|
||||||
read -r -p "$prompt" ans </dev/tty || true
|
|
||||||
[[ ${ans:-$d} =~ ^[Yy]$ ]]
|
|
||||||
else
|
|
||||||
[[ "$d" =~ ^[Yy]$ ]]
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ====================== Platform Detect ======================
|
|
||||||
detect_platform() {
|
detect_platform() {
|
||||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
OS="macos"; PKG_MGR="brew"
|
OS="macos"; PKG_MGR="brew"
|
||||||
if ! command -v brew &>/dev/null; then
|
if ! command -v brew &>/dev/null; then
|
||||||
log "Installing Homebrew…"
|
echo "🍺 Installing Homebrew..."
|
||||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||||
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
|
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
|
||||||
eval "$(/opt/homebrew/bin/brew shellenv)"
|
eval "$(/opt/homebrew/bin/brew shellenv)"
|
||||||
ok "Homebrew installed"
|
|
||||||
fi
|
fi
|
||||||
elif command -v apt &>/dev/null; then OS="debian"; PKG_MGR="apt"
|
elif command -v apt &>/dev/null; then
|
||||||
elif command -v dnf &>/dev/null; then OS="rhel"; PKG_MGR="dnf"
|
OS="debian"; PKG_MGR="apt"
|
||||||
elif command -v yum &>/dev/null; then OS="rhel"; PKG_MGR="yum"
|
elif command -v dnf &>/dev/null; then
|
||||||
elif command -v pacman &>/dev/null; then OS="arch"; PKG_MGR="pacman"
|
OS="rhel"; PKG_MGR="dnf"
|
||||||
else warn "Unsupported platform"; exit 1; fi
|
elif command -v yum &>/dev/null; then
|
||||||
ok "Detected ${OS} (${PKG_MGR})"
|
OS="rhel"; PKG_MGR="yum"
|
||||||
|
elif command -v pacman &>/dev/null; then
|
||||||
|
OS="arch"; PKG_MGR="pacman"
|
||||||
|
else
|
||||||
|
echo "❌ Unsupported platform"; exit 1
|
||||||
|
fi
|
||||||
|
echo "✓ Detected $OS ($PKG_MGR)"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ====================== Debian repo/key ======================
|
# 🔐 Install gpg if needed (Debian/Ubuntu)
|
||||||
install_gpg_if_needed() {
|
install_gpg_if_needed() {
|
||||||
if [[ "${OS}" == "debian" ]] && ! command -v gpg &>/dev/null; then
|
if [[ "$OS" == "debian" ]] && ! command -v gpg &>/dev/null; then
|
||||||
log "Installing gnupg…"
|
echo "🔑 Installing gpg..."
|
||||||
sudo apt update && sudo apt install -y gnupg
|
sudo apt update && sudo apt install -y gnupg
|
||||||
ok "gnupg installed"
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 📦 Add Fish repo (Debian/Ubuntu)
|
||||||
add_fish_repo_debian() {
|
add_fish_repo_debian() {
|
||||||
log "Adding Fish repo (Debian)…"
|
echo "• Adding Fish repo..."
|
||||||
echo 'deb http://download.opensuse.org/repositories/shells:/fish:/release:/4/Debian_12/ /' \
|
echo 'deb http://download.opensuse.org/repositories/shells:/fish:/release:/4/Debian_12/ /' | \
|
||||||
| sudo tee /etc/apt/sources.list.d/fish.list >/dev/null
|
sudo tee /etc/apt/sources.list.d/fish.list >/dev/null
|
||||||
curl -fsSL https://download.opensuse.org/repositories/shells:fish:release:4/Debian_12/Release.key \
|
curl -fsSL https://download.opensuse.org/repositories/shells:fish:release:4/Debian_12/Release.key | \
|
||||||
| gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/fish.gpg >/dev/null
|
gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/fish.gpg > /dev/null
|
||||||
sudo apt update
|
sudo apt update
|
||||||
ok "Repo added"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# ====================== Fish install/update ======================
|
# 🐟 Install Fish shell
|
||||||
install_fish() {
|
install_fish() {
|
||||||
log "Installing Fish…"
|
echo "• Installing Fish..."
|
||||||
case "$OS" in
|
case "$OS" in
|
||||||
debian) add_fish_repo_debian; sudo apt install -y fish ;;
|
debian) add_fish_repo_debian; sudo apt install -y fish ;;
|
||||||
rhel) sudo "$PKG_MGR" install -y fish ;;
|
rhel) sudo $PKG_MGR install -y fish ;;
|
||||||
arch) sudo pacman -Sy --noconfirm fish ;;
|
arch) sudo pacman -Sy --noconfirm fish ;;
|
||||||
macos) brew install fish ;;
|
macos) brew install fish ;;
|
||||||
esac
|
esac
|
||||||
ok "Fish installed"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
should_check_updates() {
|
# 🔎 Detect if an update seems available (without doing a refresh)
|
||||||
case "$UPDATE_MODE" in
|
fish_update_available() {
|
||||||
yes) return 0 ;;
|
|
||||||
no) return 1 ;;
|
|
||||||
prompt)
|
|
||||||
prompt_yn_tty "Check for Fish updates (may run a repo refresh)? (y/N): " "N"
|
|
||||||
return $? ;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
update_fish_if_available() {
|
|
||||||
log "Checking Fish updates…"
|
|
||||||
case "$PKG_MGR" in
|
case "$PKG_MGR" in
|
||||||
apt)
|
apt)
|
||||||
if should_check_updates; then
|
# Compare Installed vs Candidate using current cache (no apt update)
|
||||||
sudo apt update
|
local installed candidate
|
||||||
apt list --upgradable 2>/dev/null | grep -q "^fish/" && sudo apt install -y fish || true
|
installed="$(dpkg-query -W -f='${Version}\n' fish 2>/dev/null || true)"
|
||||||
else
|
candidate="$(apt-cache policy fish 2>/dev/null | awk '/Candidate:/ {print $2}')"
|
||||||
ok "Skipped update check"
|
[[ -n "$installed" && -n "$candidate" && "$candidate" != "(none)" && "$candidate" != "$installed" ]]
|
||||||
fi
|
|
||||||
;;
|
;;
|
||||||
dnf|yum)
|
dnf|yum)
|
||||||
if should_check_updates; then
|
# Fast check; may contact repos briefly but doesn't install
|
||||||
sudo "$PKG_MGR" check-update || true
|
$PKG_MGR -q check-update fish >/dev/null 2>&1
|
||||||
"$PKG_MGR" list updates | grep -q "^fish" && sudo "$PKG_MGR" update -y fish || true
|
[[ $? -eq 100 ]] # 100 = updates available
|
||||||
else
|
|
||||||
ok "Skipped update check"
|
|
||||||
fi
|
|
||||||
;;
|
;;
|
||||||
pacman)
|
pacman)
|
||||||
if should_check_updates; then
|
# No refresh; just check queued upgrades
|
||||||
sudo pacman -Sy --noconfirm
|
pacman -Qu 2>/dev/null | grep -q '^fish '
|
||||||
pacman -Qu | grep -q "^fish" && sudo pacman -S --noconfirm fish || true
|
|
||||||
else
|
|
||||||
ok "Skipped update check"
|
|
||||||
fi
|
|
||||||
;;
|
;;
|
||||||
brew)
|
brew)
|
||||||
if should_check_updates; then
|
# No brew update; check current outdated list
|
||||||
brew update
|
brew outdated --quiet 2>/dev/null | grep -q '^fish$'
|
||||||
brew outdated | grep -q "^fish" && brew upgrade fish || true
|
|
||||||
else
|
|
||||||
ok "Skipped update check"
|
|
||||||
fi
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
ok "Fish up to date"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# ====================== Fisher & Plugins ======================
|
# ♻️ Update flow (only prompt if an update looks available)
|
||||||
ensure_fisher() {
|
maybe_update_fish() {
|
||||||
if fish -c 'functions -q fisher' </dev/null >/dev/null 2>&1; then
|
echo "• Checking Fish updates…"
|
||||||
ok "Fisher already present"
|
if fish_update_available; then
|
||||||
|
read -rp "A newer Fish appears available. Refresh repos and update now? (y/N): " confirm
|
||||||
|
if [[ "$confirm" =~ ^[Yy]$ ]]; then
|
||||||
|
case "$PKG_MGR" in
|
||||||
|
apt)
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y fish && echo "✓ Fish updated"
|
||||||
|
;;
|
||||||
|
dnf|yum)
|
||||||
|
sudo $PKG_MGR check-update || true
|
||||||
|
sudo $PKG_MGR update -y fish && echo "✓ Fish updated"
|
||||||
|
;;
|
||||||
|
pacman)
|
||||||
|
sudo pacman -Sy --noconfirm
|
||||||
|
sudo pacman -S --noconfirm fish && echo "✓ Fish updated"
|
||||||
|
;;
|
||||||
|
brew)
|
||||||
|
brew update
|
||||||
|
brew upgrade fish && echo "✓ Fish updated"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
else
|
else
|
||||||
log "Installing Fisher…"
|
echo "✓ Skipped update"
|
||||||
fish -c 'curl -sL https://git.io/fisher | source && fisher install jorgebucaran/fisher' </dev/null >/dev/null 2>&1
|
fi
|
||||||
ok "Fisher installed"
|
else
|
||||||
|
echo "✓ No update detected (based on current cache)"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
install_plugins() {
|
# 🎣 Install Fisher
|
||||||
[[ $NO_PLUGINS -eq 1 ]] && { ok "Skipping plugins (per flag)"; return; }
|
install_fisher() {
|
||||||
|
if fish -c "functions -q fisher" &>/dev/null; then
|
||||||
|
echo "✓ Fisher already present"
|
||||||
|
else
|
||||||
|
echo "• Installing Fisher..."
|
||||||
|
# isolate stdin so curl|bash doesn’t leak into fish
|
||||||
|
fish -c 'curl -sL https://git.io/fisher | source && fisher install jorgebucaran/fisher' </dev/null
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Current installed plugins
|
# 🧩 Install plugins (idempotent)
|
||||||
|
install_plugins() {
|
||||||
|
local plugins=( jethrokuan/z ilancosman/tide jorgebucaran/nvm.fish )
|
||||||
|
local install_list=()
|
||||||
local installed
|
local installed
|
||||||
installed="$(fish -c 'fisher list' </dev/null 2>/dev/null || true)"
|
installed="$(fish -c 'fisher list' </dev/null 2>/dev/null || true)"
|
||||||
|
for p in "${plugins[@]}"; do
|
||||||
# Determine missing plugins
|
|
||||||
local missing=()
|
|
||||||
for p in "${PLUGINS[@]}"; do
|
|
||||||
if ! grep -Fxq "$p" <<<"$installed"; then
|
if ! grep -Fxq "$p" <<<"$installed"; then
|
||||||
missing+=("$p")
|
install_list+=("$p")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
if [[ ${#install_list[@]} -gt 0 ]]; then
|
||||||
# Nothing to do?
|
echo "• Installing plugins: ${install_list[*]}"
|
||||||
if [[ ${#missing[@]} -eq 0 ]]; then
|
fish -c "fisher install ${install_list[*]}" </dev/null
|
||||||
ok "All requested plugins already installed"
|
echo "✓ Plugins installed"
|
||||||
else
|
else
|
||||||
if [[ $VERBOSE -eq 1 ]]; then
|
echo "✓ All requested plugins already installed"
|
||||||
for p in "${missing[@]}"; do
|
|
||||||
echo "Installing (verbose): $p"
|
|
||||||
fish -c "fisher install $p" </dev/null
|
|
||||||
done
|
|
||||||
ok "Plugins installed"
|
|
||||||
else
|
|
||||||
local total=${#missing[@]}
|
|
||||||
local i=0
|
|
||||||
for p in "${missing[@]}"; do
|
|
||||||
i=$((i+1))
|
|
||||||
draw_bar "$((i-1))" "$total" "$p"
|
|
||||||
( fish -c "fisher install $p" </dev/null >/dev/null 2>&1 ) &
|
|
||||||
pid=$!
|
|
||||||
while kill -0 "$pid" 2>/dev/null; do
|
|
||||||
draw_bar "$((i-1))" "$total" "$p"
|
|
||||||
sleep 0.08
|
|
||||||
done
|
|
||||||
draw_bar "$i" "$total" "$p"
|
|
||||||
done
|
|
||||||
echo
|
|
||||||
ok "Plugins installed"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If Tide installed but user wants to keep default prompt
|
|
||||||
if [[ $WITH_TIDE -eq 1 && $KEEP_PROMPT -eq 1 ]]; then
|
|
||||||
log "Keeping default Fish prompt (disabling Tide init)…"
|
|
||||||
rm -f ~/.config/fish/functions/fish_prompt.fish \
|
|
||||||
~/.config/fish/conf.d/_tide_init.fish 2>/dev/null || true
|
|
||||||
ok "Default prompt preserved"
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_tide_if_requested() {
|
# 🐚 Ask to set Fish as default shell (only if not already)
|
||||||
[[ $REMOVE_TIDE -eq 0 ]] && return
|
set_fish_default() {
|
||||||
|
local fish_path current_shell
|
||||||
|
fish_path="$(command -v fish)"
|
||||||
|
current_shell="$(getent passwd "$USER" | cut -d: -f7)"
|
||||||
|
|
||||||
ensure_fisher # make sure fisher exists so removal works
|
if [[ "$current_shell" == "$fish_path" ]]; then
|
||||||
|
echo "✓ Fish is already your default shell"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
# Only remove if installed
|
read -rp "Set Fish as your default shell? (y/N): " confirm
|
||||||
if fish -c 'fisher list' </dev/null 2>/dev/null | grep -Fxq "ilancosman/tide"; then
|
if [[ "$confirm" =~ ^[Yy]$ ]]; then
|
||||||
log "Removing Tide…"
|
if ! grep -q "^$fish_path$" /etc/shells; then
|
||||||
if [[ $VERBOSE -eq 1 ]]; then
|
echo "$fish_path" | sudo tee -a /etc/shells >/dev/null
|
||||||
fish -c "fisher remove ilancosman/tide" </dev/null
|
fi
|
||||||
|
chsh -s "$fish_path"
|
||||||
|
echo "✓ Fish set as default shell (applies to new sessions)"
|
||||||
else
|
else
|
||||||
fish -c "fisher remove ilancosman/tide" </dev/null >/dev/null 2>&1
|
echo "✓ Keeping current default shell"
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
|
|
||||||
# Clean any prompt overrides
|
|
||||||
rm -f ~/.config/fish/functions/fish_prompt.fish \
|
|
||||||
~/.config/fish/conf.d/_tide_init.fish 2>/dev/null || true
|
|
||||||
ok "Tide removed and default prompt restored"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# ====================== Main ======================
|
# 🚀 Main flow
|
||||||
detect_platform
|
detect_platform
|
||||||
|
install_gpg_if_needed
|
||||||
|
|
||||||
if command -v fish &>/dev/null; then
|
if command -v fish &>/dev/null; then
|
||||||
ok "Fish already installed"
|
echo "✓ Fish already installed"
|
||||||
update_fish_if_available
|
maybe_update_fish
|
||||||
else
|
else
|
||||||
install_gpg_if_needed
|
|
||||||
install_fish
|
install_fish
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ensure_fisher
|
install_fisher
|
||||||
install_plugins
|
install_plugins
|
||||||
remove_tide_if_requested
|
set_fish_default
|
||||||
|
|
||||||
ok "Fish + Fisher + plugins ready on ${OS}"
|
echo "✓ Fish + Fisher + plugins ready on $OS"
|
||||||
Reference in New Issue
Block a user