Rewrite bootstrap; +clean script, update README

This commit is contained in:
Henrik Lissner 2016-11-15 02:20:36 +01:00
parent f8a64521de
commit 781ff33832
3 changed files with 209 additions and 84 deletions

View file

@ -3,27 +3,69 @@
<img src="http://i.giphy.com/ZHlGzvZb130nm.gif" align="right" /> <img src="http://i.giphy.com/ZHlGzvZb130nm.gif" align="right" />
These are my dotfiles. They fuel my madness. Designed for MacOS, Arch Linux and the My dotfiles. Fuel for my madness. Designed for MacOS, Arch Linux and
various debian-based boxes I frequently ssh into. the various debian-based boxes I frequently ssh into.
My dotfiles are organized into "topics". The `./boostrap` script is used to install or It is organized into topics, some with their own READMEs. Check them out for
update them. more targeted information. The `./boostrap` script is used to install and update
them.
+ `bootstrap install [topic1 topic2 ...]`: symlinks topics to `./.enabled.d` and ## Bootstrap
dotfiles therein to $HOME (then runs $topic/install)
+ `bootstrap update`: updates (enabled) topics that have a $topic/update script
+ `bootstrap clean`: deletes dead symlinks in $HOME
+ Each command has a one-letter shortcut: `./bootstrap i os/macos`
Or use one of my recipes: `bootstrap` is my idempotent dotfile deployment script. This is how it works:
+ `bootstrap install recipes/ganymede` (my laptop) You pass the directories of "topics" you want to install and it will:
1. symlink `$topic/bin/*` to `~/.bin/`
2. symlink `$topic/.*` to `~/` _and_ `$topic/.*/*` to `~/.$1/` (i.e. 2-level
symlinking). e.g. `os/arch/.Xresources` will be symlinked to
`~/.Xresources`, and `os/arch/.config/redshift.conf` will be symlinked to
`~/.config/redshift.conf` (`~/.config` is not a symlink).
3. run `$topic/install` the first time
4. run `$topic/update` on consecutive runs
5. symlink `$topic` to `$DOTFILES/.enabled.d/` to keep track of what's enabled.
Running `bootstrap` without arguments will run this process on all
enabled topics in `.enabled.d/`.
> NOTE: Be sure to add ~/.bin to PATH
```
Usage: bootstrap [-iuldyn] [topic1[ topic2[ ...]]]
Options:
-d Do a dry run (only output commands, no changes)
-i Don't run install scripts
-l Don't symlink dotfiles or bin scripts
-n Don't overwrite conflicts (no prompts
-u Don't run update scripts
-y Overwrite conflicts (no prompts)
```
Or write a recipe list (examples in `recipes/`):
+ `bootstrap $(recipes/ganymede)` (my laptop)
To update the topics that you've already enabled, simply run `bootstrap`, and it
will update your symlinks and run `$topic/update` wherever they may exist.
A few good-to-knows (where $topic is an enabled topic): A few good-to-knows (where $topic is an enabled topic):
+ zshrc auto-sources `$topic/*.zsh` + zshrc auto-sources `$topic/*.zsh`
+ `$topic/bin` is added to PATH + `$topic/bin/*` scripts will be symlinked to `~/.bin`. The included bash/zsh
+ `$topic/.*` (starts with a dot) is symlinked to $HOME topics add `~/.bin` to `PATH`.
## Clean
`clean` will delete broken symlinks in `$HOME`, and can "disable" topics by
removing their symlinks in `.enabled.d/`
```
Usage: clean [-du]
Options:
-d Do a dry run (only output commands, no changes)
-u Uninstall all enabled topics
```
## Relevant ## Relevant

188
bootstrap
View file

@ -1,82 +1,128 @@
#!/bin/bash #!/usr/bin/env bash
export DOTFILES=$(cd $(dirname $0) && pwd -P)
source "$DOTFILES/shell/common.sh" # 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)
# -n Don't overwrite conflicts (no prompts
set -e set -e
DOTFILES=$(cd $(dirname $0) && pwd -P)
source "$DOTFILES/shell/common.sh"
shopt -s nullglob
# #
install() { blue="\033[34m"
for topic in $*; magenta="\033[35m"
do green="\033[32m"
local path="$(dirname $0)/$topic" orange="\033[33m"
if [ -d "$path" ]; grey="\033[36m"
then red="\033[31m"
[ -f "$path/install" ] && . "$path/install" nc="\033[0m"
#
# Link available dotfiles (1 level deep) _run() {
for file in "$1"/.[a-zA-Z]* local cmd="${@//$DOTFILES\//}"
do cmd="${cmd//$HOME/~}"
if [ -d "$file" ]; then local clr=""
for subfile in "$file"/* case $cmd in
do *~/.bin/) clr="${magenta}" ;;
local base="$HOME/$(basename $file)" ln*) clr="" ;;
[ ! -d "$base" ] && mkdir -p "$base" mkdir*) clr="${blue}" ;;
ln -svf "$subfile" "$base" *) clr="${orange}"
done esac
elif [ -f "$file" ]; then echo -e " $PREFIX$clr$cmd$nc"
ln -svf "$file" "$HOME" [[ $DRYRUN ]] || { $@ | sed 's/^/ => /'; }
fi }
done
# Symlink enabled topic folders _link() {
if [ ! -h "$DOTFILES/.enabled.d/$(basename $path)" ] if [[ -f "$2" || ( -d "$2" && -f "$2/${1##*/}" ) ]]; then
then if [[ $yes ]]; then
ln -svf "$DOTFILES/$path" "$DOTFILES/.enabled.d" [[ $yes = 1 ]] && choice=y || choice=n
fi else
elif [ -f "$path" ] read -n 1 -p "? ${1##*/} exists in $2 -- overwrite? [y/n] " choice
then echo
install $(cat "$path")
fi fi
done case $choice in
} y|Y) PREFIX="${red}OVERWRITE:${nc} " _run ln -svf "$1" "$2"
;;
update() { *) PREFIX="${red}ABORTED:${nc} " DRYRUN=1 _run ln -svf "$1" "$2"
for topic in $DOTFILES/.enabled.d/*/ ;;
do esac
[ -x "$topic/update" ] && . "$topic/update" else
done _run ln -svf "$1" "$2"
}
clean() {
# Temporarily move dotfiles to kill symlinks
mv $DOTFILES /tmp/dotfiles
# Clean up dead symlinks
find -L $HOME -iname '.*' -type l -maxdepth 1 -exec rm -fv {} \;
if [ -d "$HOME/.config" ]
then
find -L $HOME/.config -iname '.*' -type l -maxdepth 1 -exec rm -fv {} \;
fi fi
# Move it back
mv /tmp/dotfiles $DOTFILES
} }
usage() {
echo "Nope"
exit
}
## ##
(( "$#" == 0 )) && usage install=1
link=1
update=1
yes=
while getopts iuldyn opt; do
case $opt in
d) DRYRUN=1 ;;
i) install= ;;
l) link= ;;
n) yes=0 ;;
u) update= ;;
y) yes=1 ;;
*) >&2 echo "Aborted."
exit 1 ;;
esac
done
shift $((OPTIND-1))
COMMAND="$1" #
shift # [[ -d ~/.bin ]] || _run mkdir -p ~/.bin
case "$COMMAND" in PATH="$DOTFILES/.bin:$PATH"
i|install) install $* ;; for topic in ${@:-$(find "$DOTFILES/.enabled.d" -maxdepth 1 -type l)}; do
u|update) update ;; [[ -d $topic ]] || continue
c|clean) clean ;;
r|reinstall) tpath=$(realpath "$topic")
clean tname="$(basename $(dirname $tpath))_${tpath##*/}"
install $* echo -e "${green}${tname//_//}${nc}"
;; # Link binaries
*) usage ;; for bin in $tpath/bin/*; do
esac [[ "$link" && -x "$bin" ]] && _link "$bin" "~/.bin/"
done
# Link topic to .enabled.d
if [[ -h "$DOTFILES/.enabled.d/$tname" ]]; then
[[ "$update" && -x $tpath/update ]] && _run "$tpath/update"
else
# Register topic as 'enabled'
_run ln -svf "$tpath" "$DOTFILES/.enabled.d/$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:

37
clean Executable file
View file

@ -0,0 +1,37 @@
#!/usr/bin/env bash
# A symlink cleanup script.
#
# Usage: clean [-du]
#
# Options:
# -d Do a dry run (only output commands, no changes)
# -u Uninstall all enabled topics
_run() { [[ $DRYRUN ]] && echo "$@" || $@; }
uninstall=
while getopts du opt; do
case $opt in
d) DRYRUN=1 ;;
u) uninstall=1 ;;
*) >&2 echo "Invalid argument: $OPTARG"
exit 1
;;
esac
done
shift $((OPTIND-1))
shopt -s nullglob
links=( $HOME/.* $HOME/.*/* )
for link in ${links[@]}; do
if [[ -h "$link" && ! -e "$link" ]]; then
_run rm -vf "$link"
fi
done
if [[ $uninstall ]]; then
_run rm -v "$(cd $(dirname $0) && pwd -P)"/.enabled.d/*
fi
# vim:set ft=sh: