#!/usr/bin/env bash set -Eeuo pipefail readonly APP_NAME='dotfiles' readonly REPO_URI="${REPO_URI:-https://github.com/Donaldttt/dotfiles.git}" readonly REPO_BRANCH="${REPO_BRANCH:-main}" SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" 2>/dev/null && pwd -P || true)" if [[ -n "$SCRIPT_DIR" && -e "$SCRIPT_DIR/.git" ]]; then APP_PATH="${APP_PATH:-$SCRIPT_DIR}" else APP_PATH="${APP_PATH:-$HOME/.$APP_NAME}" fi readonly APP_PATH readonly VIM_PATH="${VIM_PATH:-$HOME/.vim}" readonly MANAGED_START='# >>> dotfiles >>>' readonly MANAGED_END='# <<< dotfiles <<<' info() { printf '\033[34m[•]\033[0m %s\n' "$*"; } success() { printf '\033[32m[✔]\033[0m %s\n' "$*"; } warn() { printf '\033[33m[!]\033[0m %s\n' "$*" >&2; } die() { printf '\033[31m[✘]\033[0m %s\n' "$*" >&2; exit 1; } usage() { cat <<'EOF' Usage: ./install.sh [all|vim|shell|tmux]... With no arguments, installs every component. Components can be combined, for example: ./install.sh vim tmux EOF } require() { command -v "$1" >/dev/null 2>&1 || die "Required program not found: $1" } ensure_repo() { require git if [[ -d "$APP_PATH/.git" ]]; then return fi [[ ! -e "$APP_PATH" ]] || die "$APP_PATH exists but is not a Git repository." info "Cloning $REPO_URI into $APP_PATH" git clone --branch "$REPO_BRANCH" --depth 1 "$REPO_URI" "$APP_PATH" } backup_path() { local target=$1 if [[ -e "$target" || -L "$target" ]]; then local backup="${target}.backup.$(date +%Y%m%d_%H%M%S)" suffix=0 while [[ -e "$backup" || -L "$backup" ]]; do ((suffix += 1)) backup="${target}.backup.$(date +%Y%m%d_%H%M%S).$suffix" done mv -- "$target" "$backup" warn "Backed up $target to $backup" fi } link_config() { local source=$1 target=$2 current='' if [[ -L "$target" ]]; then current=$(readlink "$target" || true) [[ "$current" == "$source" ]] && return fi backup_path "$target" ln -s "$source" "$target" success "Linked $target -> $source" } remove_managed_block() { local file=$1 temp [[ -f "$file" ]] || return temp=$(mktemp) awk -v start="$MANAGED_START" -v end="$MANAGED_END" ' $0 == start { managed = 1; next } $0 == end { managed = 0; next } !managed { print } ' "$file" > "$temp" cat "$temp" > "$file" rm -f "$temp" } install_vim() { require vim require curl if ! vim --clean -Nu NONE -n -es \ '+if v:version < 900 | cquit | endif' '+qall'; then die 'Vim 9 or newer is required.' fi link_config "$APP_PATH/.vimrc" "$HOME/.vimrc" mkdir -p "$VIM_PATH/autoload" local temp temp=$(mktemp) curl --fail --location --silent --show-error \ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim \ --output "$temp" mv "$temp" "$VIM_PATH/autoload/plug.vim" vim -Nu "$APP_PATH/.vimrc" -n -es \ '+set nomore' '+PlugInstall --sync' '+qall' success 'Installed Vim configuration and plugins.' } install_shell() { local bashrc="$HOME/.bashrc" quoted_source touch "$bashrc" remove_managed_block "$bashrc" printf -v quoted_source '%q' "$APP_PATH/bash_tools.sh" { printf '\n%s\n' "$MANAGED_START" printf '[ -r %s ] && source %s\n' "$quoted_source" "$quoted_source" printf 'command -v zoxide >/dev/null 2>&1 && eval "$(zoxide init bash)"\n' printf '%s\n' "$MANAGED_END" } >> "$bashrc" success "Installed managed Bash block in $bashrc" } install_tmux() { require tmux link_config "$APP_PATH/.tmux.conf" "$HOME/.tmux.conf" local tpm="$HOME/.tmux/plugins/tpm" if [[ ! -d "$tpm/.git" ]]; then [[ ! -e "$tpm" ]] || die "$tpm exists but is not a Git repository." mkdir -p "$(dirname "$tpm")" git clone --depth 1 https://github.com/tmux-plugins/tpm "$tpm" fi success 'Installed tmux configuration and TPM.' } main() { case "${1:-}" in -h|--help) usage; return ;; esac ensure_repo local components=("$@") component ((${#components[@]})) || components=(all) for component in "${components[@]}"; do case "$component" in all) install_vim install_shell install_tmux ;; vim) install_vim ;; shell) install_shell ;; tmux) install_tmux ;; *) usage; die "Unknown component: $component" ;; esac done } main "$@"