Rewrite bootstrap; +clean script, update README
This commit is contained in:
parent
f8a64521de
commit
781ff33832
68
README.md
68
README.md
|
@ -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
188
bootstrap
|
@ -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
|
|
||||||
if [ ! -h "$DOTFILES/.enabled.d/$(basename $path)" ]
|
|
||||||
then
|
|
||||||
ln -svf "$DOTFILES/$path" "$DOTFILES/.enabled.d"
|
|
||||||
fi
|
|
||||||
elif [ -f "$path" ]
|
|
||||||
then
|
|
||||||
install $(cat "$path")
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
_link() {
|
||||||
for topic in $DOTFILES/.enabled.d/*/
|
if [[ -f "$2" || ( -d "$2" && -f "$2/${1##*/}" ) ]]; then
|
||||||
do
|
if [[ $yes ]]; then
|
||||||
[ -x "$topic/update" ] && . "$topic/update"
|
[[ $yes = 1 ]] && choice=y || choice=n
|
||||||
done
|
else
|
||||||
}
|
read -n 1 -p "? ${1##*/} exists in $2 -- overwrite? [y/n] " choice
|
||||||
|
echo
|
||||||
clean() {
|
fi
|
||||||
# Temporarily move dotfiles to kill symlinks
|
case $choice in
|
||||||
mv $DOTFILES /tmp/dotfiles
|
y|Y) PREFIX="${red}OVERWRITE:${nc} " _run ln -svf "$1" "$2"
|
||||||
# Clean up dead symlinks
|
;;
|
||||||
find -L $HOME -iname '.*' -type l -maxdepth 1 -exec rm -fv {} \;
|
*) PREFIX="${red}ABORTED:${nc} " DRYRUN=1 _run ln -svf "$1" "$2"
|
||||||
if [ -d "$HOME/.config" ]
|
;;
|
||||||
then
|
esac
|
||||||
find -L $HOME/.config -iname '.*' -type l -maxdepth 1 -exec rm -fv {} \;
|
else
|
||||||
|
_run ln -svf "$1" "$2"
|
||||||
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
37
clean
Executable 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:
|
Loading…
Reference in a new issue