#!/bin/sh set -e DOTFILES="$(dirname "$(realpath "$0")")" TARGET="$HOME" [ -f "$DOTFILES/config.local" ] && . "$DOTFILES/config.local" GIT_USER="${GIT_USER:-Fernando Schauenburg}" GIT_EMAIL="${GIT_EMAIL:-fernando@schauenburg.me}" main() { IS_DRY_RUN=yes while getopts 'fht:' opt; do case "$opt" in f) unset IS_DRY_RUN;; t) TARGET="$OPTARG";; h) usage; exit 0;; *) usage; exit 1;; esac done greeting deploy_packages bin_extras git_extras nvim_extras } greeting() { dry_run && { warn "Performing dry run (use -f to actually make changes)." warn } info "Deploying dotfiles:" info " Source: $cyan$DOTFILES$rst" info " Taget: $cyan$TARGET$rst" info " Git user: $cyan$GIT_USER <$GIT_EMAIL>$rst" [ -t 0 ] && { info info "Press ENTER to continue (CTRL-C to cancel)..." read k } } deploy_packages() { for item in "$DOTFILES"/*; do [ ! -d "$item" ] && continue heading "${item#$DOTFILES/}" deploy "$item" done } bin_extras() { heading "stale $TARGET/.local/bin commands" for cmd in $TARGET/.local/bin/*; do prune_cmd "$cmd"; done; } git_extras() { heading 'git user configuration' temp_git="$(mktemp)" cat >"$temp_git" </dev/null 2>&1; then dry_run || { warn "installing neovim plugins..." nvim --headless -c 'autocmd User PackerComplete quitall' -c 'PackerSync' } else error "neovim is not installed; skipping plugin installation..." fi } ############################################################################### # Helper Functions ############################################################################### if [ -t 1 ]; then rst=$(tput sgr0) red=$(tput setaf 1) green=$(tput setaf 2) yellow=$(tput setaf 3) blue=$(tput setaf 4) magenta=$(tput setaf 5) cyan=$(tput setaf 6) else rst='' red='' green='' yellow='' blue='' magenta='' cyan='' fi dry_run() { [ -n "$IS_DRY_RUN" ]; } usage() { echo "Usage: $(basename $0) [-h] [-f] [-t ]" echo "" echo " -h print this help and exit" echo " -f modify filesystem rather than dry run" echo " -t set directory (default: \$HOME)" } heading() { printf '%s\n' "${blue}===== $1 ==========${rst}"; } info() { printf '%s ' "$@"; printf '\n'; } warn() { printf '%s ' "${yellow}$@${rst}"; printf '\n'; } error() { printf '%s ' "${red}$@${rst}"; printf '\n'; } log() { case "$1" in OK|ok) color=$green;; *) color=$yellow;; esac printf '%s%-*s%s %s\n' "$color" 6 "$1:" "$rst" "$2" } # Make sure directory $1 exists. ensure_directory() { [ -d "$1" ] && return log MKDIR "$1/" dry_run || mkdir -p "$1/" } # Remove $1 if it's a broken link to a dotfile script. prune_cmd() { if [ -h "$1" ]; then # it's a symbolic link... target="$(readlink "$1")" case "$target" in "$DOTFILES/bin/"*) # ... pointing into dotfiles bin if [ ! -e "$1" ]; then # target of the link missing log UNLINK "(stale) $1 -> $target" dry_run || rm -f "$1" fi ;; esac fi } # Make sure $1 is a (relative) symlink to $2. link() { target="$(python -c "import os; print os.path.relpath('$2','$(dirname $1)')")" if [ "$(readlink "$1")" = "$target" ]; then log OK "$1 -> $target" else log LINK "$1 -> $target" dry_run || ln -sf "$target" "$1" fi } # Ensure $1 and $2 contents are equal, updating $1 if needed. equal_content() { if diff "$1" "$2" >/dev/null 2>&1; then log OK "$1" else log OVERWRITE "$1 with $2:" echo "$cyan" cat "$2" echo "$rst" dry_run || cp -f "$2" "$1" fi } # Deploy package by creating subdirs and symlinks to dotfiles. deploy() { package="$1" find "$package" -type f | while read dotfile; do link="$TARGET/${dotfile##"$package"/}" ensure_directory "$(dirname "$link")" [ "$(basename "$dotfile")" = '.keep' ] || link "$link" "$dotfile" done } main "$@"