diff --git a/README.md b/README.md index 43a44eb..dbedb98 100644 --- a/README.md +++ b/README.md @@ -4,5 +4,5 @@ ## use it now ```bash -bash <(curl -s https://raw.githubusercontent.com/harivansh-afk/nvim/main/install.sh) +curl -fsSL https://raw.githubusercontent.com/harivansh-afk/nvim/main/install.sh | bash ``` diff --git a/install.sh b/install.sh index 458a78b..8d96d0d 100755 --- a/install.sh +++ b/install.sh @@ -1,104 +1,246 @@ -#!/bin/bash +#!/usr/bin/env bash +set -euo pipefail -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color +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" -NVIM_CONFIG_DIR="$HOME/.config/nvim" -BACKUP_DIR="$HOME/.config/nvim.backup.$(date +%Y%m%d_%H%M%S)" +SKIP_NVIM=0 +SKIP_CONFIG=0 +NO_PATH=0 -echo -e "${GREEN}Installing harivansh-afk/nvim configuration...${NC}" +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 ;; + -h|--help) + cat <<'EOF' +Usage: install.sh [options] -# Check if git is installed -if ! command -v git &> /dev/null; then - echo -e "${RED}Error: git is not installed. Please install git first.${NC}" - exit 1 -fi +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 +EOF + exit 0 + ;; + *) + die "Unknown argument: $1 (use --help)" + ;; + esac +done -# Check if nvim is installed -if ! command -v nvim &> /dev/null; then - echo -e "${YELLOW}Neovim not found. Installing via appimage (no sudo required)...${NC}" +OS="$(uname -s)" +ARCH_RAW="$(uname -m)" +case "$OS" in + Darwin) OS="macos" ;; + Linux) OS="linux" ;; + *) die "Unsupported OS: $OS" ;; +esac - cd /tmp - rm -f nvim.appimage +case "$ARCH_RAW" in + x86_64|amd64) ARCH="x86_64" ;; + arm64|aarch64) ARCH="arm64" ;; + *) die "Unsupported architecture: $ARCH_RAW" ;; +esac - # Use stable release URL (more reliable than 'latest' redirect) - NVIM_URL="https://github.com/neovim/neovim/releases/download/stable/nvim-linux-x86_64.appimage" +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)" - echo "Downloading from $NVIM_URL..." - if command -v wget &> /dev/null; then - wget -q --show-progress -O nvim.appimage "$NVIM_URL" - else - curl -L -o nvim.appimage "$NVIM_URL" +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 +} - chmod u+x nvim.appimage +install_nvim_macos_tarball() { + require_downloader + mkdir -p "$LOCAL_BIN" - # Try to extract (works on most systems) - ./nvim.appimage --appimage-extract > /dev/null 2>&1 + local tmp + tmp="$(mktemp -d 2>/dev/null || mktemp -d -t nvim-install)" + trap 'rm -rf "$tmp"' EXIT - mkdir -p "$HOME/.local/bin" + 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" + ) - if [ -d "squashfs-root" ]; then - # Use extracted version - rm -rf "$HOME/.local/nvim" - mv squashfs-root "$HOME/.local/nvim" - ln -sf "$HOME/.local/nvim/AppRun" "$HOME/.local/bin/nvim" - rm -f nvim.appimage - else - # Use appimage directly - mv nvim.appimage "$HOME/.local/bin/nvim" + 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})" - # Add to PATH - export PATH="$HOME/.local/bin:$PATH" + 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" - # Add to shell rc files if not already there - for rc in "$HOME/.bashrc" "$HOME/.zshrc"; do - if [ -f "$rc" ]; then - if ! grep -q '.local/bin' "$rc" 2>/dev/null; then - echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$rc" - echo -e "${GREEN}Added ~/.local/bin to PATH in $(basename $rc)${NC}" - fi + 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 - done + fi + fi - cd - > /dev/null + 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" +} + +log "Installing nvim config from ${REPO_URL} (${OS}/${ARCH})" +ensure_local_bin_path + +if [[ "$SKIP_NVIM" -eq 0 ]]; then + install_nvim fi -# Verify nvim installation -if ! command -v nvim &> /dev/null && [ ! -x "$HOME/.local/bin/nvim" ]; then - echo -e "${RED}Failed to install neovim.${NC}" - exit 1 +if [[ "$SKIP_CONFIG" -eq 0 ]]; then + install_config fi -# Ensure PATH includes local bin -export PATH="$HOME/.local/bin:$PATH" - -echo -e "${GREEN}Neovim version: $(nvim --version | head -1)${NC}" - -# Backup existing config if it exists -if [ -d "$NVIM_CONFIG_DIR" ]; then - echo -e "${YELLOW}Backing up existing nvim config to $BACKUP_DIR${NC}" - mv "$NVIM_CONFIG_DIR" "$BACKUP_DIR" +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 -# Ensure .config directory exists -mkdir -p "$HOME/.config" - -# Clone the repository -echo -e "${GREEN}Cloning nvim config from $REPO_URL...${NC}" -git clone "$REPO_URL" "$NVIM_CONFIG_DIR" - -# Create undodir -mkdir -p "$NVIM_CONFIG_DIR/undodir" - -echo -e "${GREEN}Installation complete!${NC}" -echo "" -echo -e "${YELLOW}IMPORTANT: Run this to use nvim now:${NC}" -echo -e " source ~/.bashrc && nvim" -echo "" -echo "Or start a new terminal session and run: nvim" +log "Done." +if [[ "$NO_PATH" -eq 0 ]]; then + warn "Open a new terminal (or 'source' your shell rc) if 'nvim' isn't found." +fi