#!/usr/bin/env bash # My (idempotent) dotfile deployment script. # # You pass the directories of "topics" to it to install them. It will: # 1. symlink $topic/bin/* to ~/.bin/ # 2. symlink $topic/.* => ~/ and $topic/.*/* => ~/.$1/ # 3. run $topic/install the first time # 4. run $topic/update on consecutive runs # # NOTE: Be sure to add ~/.bin to PATH # # Options: # -i Don't run install scripts # -u Don't run update scripts # -l Don't symlink dotfiles or bin scripts # -d Do a dry run (only output commands, no changes) # -y Overwrite conflicts (no prompts) # -p Prompt on file conflict (to overwrite) # -r Refresh; the same as -ui set -e source "$(cd $(dirname $0) && pwd -P)/shell/common.sh" shopt -s nullglob # blue="\033[34m" magenta="\033[35m" green="\033[32m" orange="\033[33m" grey="\033[36m" red="\033[31m" nc="\033[0m" # _run() { local cmd="${@//$DOTFILES\//}" cmd="${cmd//$HOME/~}" local clr="" case $cmd in *~/.bin/) clr="${magenta}" ;; ln*) clr="" ;; mkdir*) clr="${blue}" ;; *) clr="${orange}" esac echo -e " $PREFIX$clr$cmd$nc" [[ $DRYRUN ]] || { $@ | sed 's/^/ => /'; } } _link() { if [[ -f "$2" || ( -d "$2" && -f "$2/${1##*/}" ) ]]; then if [[ $yes ]]; then [[ $yes = 1 ]] && choice=y || choice=n else read -n 1 -p "? ${1##*/} exists in $2 -- overwrite? [y/n] " choice echo fi case $choice in y|Y) _run rm -rf "$2" PREFIX="${red}OVERWRITE:${nc} " _run ln -svf "$1" "$2" ;; *) PREFIX="${red}ABORTED:${nc} " DRYRUN=1 _run ln -svf "$1" "$2" ;; esac else _run ln -svf "$1" "$2" fi } ## install=1 link=1 update=1 yes=0 while getopts iuldypr opt; do case $opt in d) DRYRUN=1 ;; i) install= ;; l) link= ;; p) yes= ;; u) update= ;; y) yes=1 ;; r) update= install= ;; *) >&2 echo "Aborted." exit 1 ;; esac done shift $((OPTIND-1)) # [[ -d ~/.bin ]] || _run mkdir -p ~/.bin PATH="$DOTFILES/.bin:$PATH" for topic in ${@:-$(find "$ENABLED_DIR" -maxdepth 1 -type l)}; do [[ -d $topic ]] || continue tpath=$(realpath "$topic") tname="$(basename $(dirname $tpath))_${tpath##*/}" echo -e "${green}${tname//_//}${nc}" # Link binaries for bin in $tpath/bin/*; do [[ "$link" && -x "$bin" ]] && _link "$bin" "$HOME/.bin/" done # Link topic to .enabled.d if [[ -h "$ENABLED_DIR/$tname" ]]; then [[ "$update" && -x $tpath/update ]] && _run "$tpath/update" else # Register topic as 'enabled' _run ln -svf "$tpath" "$ENABLED_DIR/$tname" # Run install script [[ "$install" && -x $tpath/install ]] && _run "$tpath/install" fi # Symlink dotfiles from topic directories (2 levels deep) [[ "$link" ]] || continue for file in "$tpath"/.[a-zA-Z]*; do if [[ -d $file ]]; then for subfile in "$file"/*; do base="$HOME/${file##*/}/" [[ -d $base ]] || _run mkdir -p "$base" _link "$subfile" "$base" done elif [[ -f $file ]]; then _link "$file" "$HOME/" fi done done # vim:set ft=sh: