helm/deploy
2018-05-07 17:04:01 +02:00

200 lines
4.9 KiB
Bash
Executable file

#!/usr/bin/env zsh
# Dotfile manager
#
# By Henrik Lissner <henrik@lissner.net>
# https://github.com/hlissner/dotfiles
#
# Requires: git, zsh
source "${0:A:h}/env"
SEP='.'
# helpers
function echo-debug { echo "$@" | sed -e "s%$HOME%~%g" -e "s%$DOTFILES%.%g"; }
function echo-info { printf "\r\033[2K\033[0;34m[ .. ]\033[0m %s\n" "$*"; }
function echo-skip { printf "\r\033[2K\033[38;05;240m[SKIP]\033[0m %s\n" "$*"; }
function echo-ok { printf "\r\033[2K\033[0;32m[ OK ]\033[0m %s\n" "$*"; }
function echo-fail { printf "\r\033[2K\033[0;31m[FAIL]\033[0m %s\n" "$*"; }
function _exec { ${DRYRUN:+echo-debug} $@; return ${DRYRUN:+1}; }
function _topic-do {
if [[ -e "$1/_init" ]]; then
if [[ ! -x "$1/_init" ]]; then
echo-fail "$1's init script isn't executable"
return 1
fi
export TOPIC=$1
pushd -q $1
_exec ./_init $2
popd -q
return $?
fi
}
function _topics {
local topics; topics=( "$DOTFILES_DATA"/*.topic(N) )
echo ${${${topics[@]#$DOTFILES_DATA/}%.topic}/$SEP//}
}
# For _init scripts
function topic-enabled-p { [[ -L "$(topic-path $1)" ]] }
function topic-path { echo "$DOTFILES_DATA/${1/\//$SEP}.topic" }
function mklink {
if [[ $1 == '-s' ]]; then
local dosudo=1
shift
fi
local flags="-svf"
local dest=${@[-1]}
local srcs; srcs=( ${@:1:-1} )
if [[ $dest == */ && ! -d $dest ]]; then
mkdir -p $dest
elif [[ ! -d ${dest%/*} ]]; then
mkdir -p ${dest%/*}
fi
for lk in ${srcs[@]}; do
local src
case $lk in
/*) src=$lk ;;
.) src="$(topic-path $TOPIC)" ;;
*) src="$(topic-path $TOPIC)/$lk" ;;
esac
if [[ -d $src ]]; then
if [[ $dest != */ && -d $dest && -L $dest ]]; then
${dosudo:+sudo} rm -fv $dest
fi
fi
_exec ${dosudo:+sudo} ln $flags $src $dest
done
}
function init {
declare -f $1 >/dev/null || return 1
echo-info "$TOPIC.init.$1: start"
$1
local retval=$?
echo-ok "$TOPIC.init.$1: done"
return $retval
}
# deploy functions
function topic-init {
for topic in $@; do
if [[ ! -d $topic ]]; then
echo-fail "$topic doesn't exist!"
continue
fi
echo-ok "$topic"
local tpath="$DOTFILES/$topic"
local tsympath="$(topic-path $topic)"
if [[ -d "$topic/bin" ]]; then
export path; path=( $tpath/bin $path )
echo-ok "$topic: added $topic/bin to PATH, temporarily"
fi
if [[ -n $LINK ]] || ! topic-enabled-p $topic; then
local op=install
mklink $tpath $tsympath
_topic-do $topic link
fi
if [[ -z $INHIBIT_INIT ]]; then
_topic-do $topic ${FORCE_OP:-${op:-update}}
if [[ $op == install && $? != 0 ]]; then
topic-remove $topic
fi
fi
done
}
function topic-remove {
for topic in $@; do
if [[ ! -d $topic ]]; then
echo-fail "$topic doesn't exist"
continue
elif ! topic-enabled-p $topic; then
echo-fail "$topic isn't enabled"
continue
fi
[[ -z $INHIBIT_INIT ]] && _topic-do $topic clean
_exec rm -f "$(topic-path $topic)" && \
echo-ok "Removed $topic" || \
echo-fail "Couldn't remove $topic"
done
}
function clean {
local paths; paths=( $XDG_CACHE_HOME $XDG_CONFIG_HOME $XDG_DATA_HOME $XDG_BIN_HOME $DOTFILES_DATA )
local links; links=( ~/.*(-@DN) ${^paths}/**(-@DN))
if [[ ${#links[@]} > 1 ]]; then
echo-info "Removing dead dotfile symlinks..."
for link in "${links[@]}"; do
[[ -e $link ]] || _exec rm -fv "$link"
done
else
echo-ok "No dead symlinks in HOME"
fi
# Remove empty dotfile folders
local dirs; dirs=( ${^paths}/**(N/^F) ~/.*(N/^F) )
if [[ ${#dirs[@]} > 1 ]]; then
echo-info "Removing empty dotfile directories..."
for dir in "${dirs[@]}"; do
_exec rmdir "$dir" && echo-ok "Deleted ${dir/$HOME/~}"
done
else
echo-ok "No empty directories in HOME"
fi
}
## Bootstrap
if [[ $ZSH_EVAL_CONTEXT != *:file ]]; then
while getopts acdehlLit opt; do
case $opt in
a) TARGETS="$(_topics)" ;;
c) CLEAN=1 ;;
d) REMOVE=1 ;;
L) LIST=1 ;;
l) export LINK=1 ;&
i) export INHIBIT_INIT=1 ;;
t) export DRYRUN=1 ;;
h) cat <<EOL
Usage: ${0:A:t} [-acdlLit] [TOPIC...]
-a Target all enabled topics (ignores TOPIC args)
-c Afterwards, remove dead symlinks & empty dot-directories in $HOME.
Can be used alone.
-d Disable & unlink topic(s)
-l Only enable & relink topic(s) (implies -i)
-L List enabled topics
-i Do not run install/update/clean init scripts
-t Do a test run; do not actually do anything
EOL
exit ;;
*) >&2 echo-fail "Aborted."
exit 1 ;;
esac
done
shift $((OPTIND-1))
if [[ -n $LIST ]]; then
_topics | tr ' ' $'\n'
else
pushd -qL "$DOTFILES"
if [[ -n $REMOVE ]]; then
topic-remove ${TARGETS:-$@}
else
topic-init ${TARGETS:-$@}
fi
[[ -n $CLEAN ]] && clean
popd -q
fi
fi
# vim:set ft=sh: