nvim/install.sh
2026-01-21 16:59:34 -05:00

266 lines
6.8 KiB
Bash
Executable file

#!/usr/bin/env bash
set -euo pipefail
if [[ -t 1 ]]; then
RED=$'\033[0;31m'
GREEN=$'\033[0;32m'
YELLOW=$'\033[1;33m'
NC=$'\033[0m'
else
RED=""
GREEN=""
YELLOW=""
NC=""
fi
log() { printf "%s%s%s\n" "${GREEN}" "$*" "${NC}"; }
warn() { printf "%s%s%s\n" "${YELLOW}" "$*" "${NC}" >&2; }
die() { printf "%sError:%s %s\n" "${RED}" "${NC}" "$*" >&2; exit 1; }
have() { command -v "$1" >/dev/null 2>&1; }
REPO_URL="https://github.com/harivansh-afk/nvim.git"
SKIP_NVIM=0
SKIP_CONFIG=0
NO_PATH=0
FULL_INSTALL=0
while [[ $# -gt 0 ]]; do
case "$1" in
--repo)
[[ $# -ge 2 ]] || die "--repo requires a value"
REPO_URL="$2"
shift 2
;;
--skip-nvim) SKIP_NVIM=1; shift ;;
--skip-config) SKIP_CONFIG=1; shift ;;
--no-path) NO_PATH=1; shift ;;
--bells-and-whistles) FULL_INSTALL=1; shift ;;
-h|--help)
cat <<'EOF'
Usage: install.sh [options]
Options:
--repo URL Clone config from URL (default: repo in this script)
--skip-nvim Do not install Neovim
--skip-config Do not install config
--no-path Do not modify shell rc files
--bells-and-whistles Full install with LSP and AI completion (default: minimal)
EOF
exit 0
;;
*)
die "Unknown argument: $1 (use --help)"
;;
esac
done
OS="$(uname -s)"
ARCH_RAW="$(uname -m)"
case "$OS" in
Darwin) OS="macos" ;;
Linux) OS="linux" ;;
*) die "Unsupported OS: $OS" ;;
esac
case "$ARCH_RAW" in
x86_64|amd64) ARCH="x86_64" ;;
arm64|aarch64) ARCH="arm64" ;;
*) die "Unsupported architecture: $ARCH_RAW" ;;
esac
LOCAL_BIN="${HOME}/.local/bin"
LOCAL_NVIM_DIR="${HOME}/.local/nvim"
CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/nvim"
BACKUP_DIR="${CONFIG_DIR}.backup.$(date +%Y%m%d_%H%M%S)"
require_downloader() {
if have curl; then
DOWNLOADER="curl"
elif have wget; then
DOWNLOADER="wget"
else
die "Missing downloader: install curl or wget"
fi
}
download() {
local url="$1"
local dest="$2"
if [[ "${DOWNLOADER}" == "curl" ]]; then
curl -fsSL "$url" -o "$dest"
else
wget -q --show-progress -O "$dest" "$url"
fi
}
ensure_local_bin_path() {
export PATH="${LOCAL_BIN}:${PATH}"
[[ "$NO_PATH" -eq 1 ]] && return 0
local export_line='export PATH="$HOME/.local/bin:$PATH"'
local rc_candidates=()
case "${SHELL:-}" in
*/zsh) rc_candidates+=("$HOME/.zshrc") ;;
*/bash) rc_candidates+=("$HOME/.bashrc") ;;
*) rc_candidates+=("$HOME/.zshrc" "$HOME/.bashrc") ;;
esac
for rc in "${rc_candidates[@]}"; do
[[ -f "$rc" ]] || continue
if ! grep -qF "$export_line" "$rc" 2>/dev/null; then
printf "\n%s\n" "$export_line" >> "$rc"
log "Added ~/.local/bin to PATH in $(basename "$rc")"
fi
done
}
install_nvim_macos_tarball() {
require_downloader
mkdir -p "$LOCAL_BIN"
local tmp
tmp="$(mktemp -d 2>/dev/null || mktemp -d -t nvim-install)"
trap 'rm -rf "$tmp"' EXIT
local tarball="${tmp}/nvim.tar.gz"
local urls=(
"https://github.com/neovim/neovim/releases/download/stable/nvim-macos-${ARCH}.tar.gz"
"https://github.com/neovim/neovim/releases/download/stable/nvim-macos.tar.gz"
)
local downloaded=0
for url in "${urls[@]}"; do
if download "$url" "$tarball" 2>/dev/null; then
downloaded=1
break
fi
done
[[ "$downloaded" -eq 1 ]] || die "Failed to download Neovim for macOS (${ARCH})"
tar -xzf "$tarball" -C "$tmp"
local extracted_dir
extracted_dir="$(find "$tmp" -maxdepth 2 -type f -path '*/bin/nvim' -print -quit | xargs -I{} dirname "{}" | xargs -I{} dirname "{}")"
[[ -n "$extracted_dir" ]] || die "Downloaded archive did not contain bin/nvim"
rm -rf "$LOCAL_NVIM_DIR"
mv "$extracted_dir" "$LOCAL_NVIM_DIR"
ln -sf "${LOCAL_NVIM_DIR}/bin/nvim" "${LOCAL_BIN}/nvim"
}
install_nvim_linux_appimage() {
require_downloader
mkdir -p "$LOCAL_BIN"
local tmp
tmp="$(mktemp -d 2>/dev/null || mktemp -d -t nvim-install)"
trap 'rm -rf "$tmp"' EXIT
local appimage="${tmp}/nvim.appimage"
local url="https://github.com/neovim/neovim/releases/download/stable/nvim-linux-${ARCH}.appimage"
download "$url" "$appimage" || die "Failed to download Neovim AppImage for Linux (${ARCH})"
chmod u+x "$appimage"
(cd "$tmp" && ./nvim.appimage --appimage-extract >/dev/null 2>&1) || true
if [[ -d "${tmp}/squashfs-root" ]]; then
rm -rf "$LOCAL_NVIM_DIR"
mv "${tmp}/squashfs-root" "$LOCAL_NVIM_DIR"
ln -sf "${LOCAL_NVIM_DIR}/AppRun" "${LOCAL_BIN}/nvim"
else
mv "$appimage" "${LOCAL_BIN}/nvim"
chmod u+x "${LOCAL_BIN}/nvim"
fi
}
install_nvim() {
if have nvim; then
log "Neovim already installed: $(nvim --version | head -1)"
return 0
fi
log "Neovim not found; installing (no sudo)..."
ensure_local_bin_path
if [[ "$OS" == "macos" ]]; then
if have brew; then
if brew list neovim >/dev/null 2>&1; then
brew upgrade neovim >/dev/null || true
else
brew install neovim
fi
else
install_nvim_macos_tarball
fi
else
install_nvim_linux_appimage
fi
ensure_local_bin_path
have nvim || die "Neovim install completed but 'nvim' is not on PATH"
log "Neovim version: $(nvim --version | head -1)"
}
install_config() {
have git || die "git is required (install git first)"
mkdir -p "$(dirname "$CONFIG_DIR")"
if [[ -d "$CONFIG_DIR" ]]; then
if [[ -d "${CONFIG_DIR}/.git" ]]; then
local remote
remote="$(git -C "$CONFIG_DIR" remote get-url origin 2>/dev/null || true)"
if [[ "$remote" == "$REPO_URL" ]]; then
if git -C "$CONFIG_DIR" diff --quiet && git -C "$CONFIG_DIR" diff --cached --quiet; then
log "Updating existing config in ${CONFIG_DIR}..."
git -C "$CONFIG_DIR" pull --ff-only || true
mkdir -p "${CONFIG_DIR}/undodir"
return 0
fi
fi
fi
warn "Backing up existing config to ${BACKUP_DIR}"
mv "$CONFIG_DIR" "$BACKUP_DIR"
fi
log "Cloning config into ${CONFIG_DIR}..."
git clone "$REPO_URL" "$CONFIG_DIR"
mkdir -p "${CONFIG_DIR}/undodir"
}
strip_heavy_plugins() {
local plugins_dir="${CONFIG_DIR}/lua/plugins"
local heavy_plugins=(
"supermaven.lua"
"lsp.lua"
)
for plugin in "${heavy_plugins[@]}"; do
if [[ -f "${plugins_dir}/${plugin}" ]]; then
rm "${plugins_dir}/${plugin}"
log "Removed ${plugin} (minimal install)"
fi
done
}
log "Installing nvim config from ${REPO_URL} (${OS}/${ARCH})"
ensure_local_bin_path
if [[ "$SKIP_NVIM" -eq 0 ]]; then
install_nvim
fi
if [[ "$SKIP_CONFIG" -eq 0 ]]; then
install_config
if [[ "$FULL_INSTALL" -eq 0 ]]; then
strip_heavy_plugins
fi
fi
if have nvim; then
# Basic sanity check: confirm Neovim can start headlessly.
nvim --headless "+qall" >/dev/null 2>&1 || warn "Neovim failed to start headlessly; run 'nvim' to see errors."
fi
log "Done."
if [[ "$NO_PATH" -eq 0 ]]; then
warn "Open a new terminal (or 'source' your shell rc) if 'nvim' isn't found."
fi