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" />
|
||||
|
||||
These are my dotfiles. They fuel my madness. Designed for MacOS, Arch Linux and the
|
||||
various debian-based boxes I frequently ssh into.
|
||||
My dotfiles. Fuel for my madness. Designed for MacOS, Arch Linux and
|
||||
the various debian-based boxes I frequently ssh into.
|
||||
|
||||
My dotfiles are organized into "topics". The `./boostrap` script is used to install or
|
||||
update them.
|
||||
It is organized into topics, some with their own READMEs. Check them out for
|
||||
more targeted information. The `./boostrap` script is used to install and update
|
||||
them.
|
||||
|
||||
+ `bootstrap install [topic1 topic2 ...]`: symlinks topics to `./.enabled.d` and
|
||||
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`
|
||||
## Bootstrap
|
||||
|
||||
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):
|
||||
|
||||
+ zshrc auto-sources `$topic/*.zsh`
|
||||
+ `$topic/bin` is added to PATH
|
||||
+ `$topic/.*` (starts with a dot) is symlinked to $HOME
|
||||
+ `$topic/bin/*` scripts will be symlinked to `~/.bin`. The included bash/zsh
|
||||
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
|
||||
|
||||
|
|
188
bootstrap
188
bootstrap
|
@ -1,82 +1,128 @@
|
|||
#!/bin/bash
|
||||
export DOTFILES=$(cd $(dirname $0) && pwd -P)
|
||||
source "$DOTFILES/shell/common.sh"
|
||||
#!/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)
|
||||
# -n Don't overwrite conflicts (no prompts
|
||||
|
||||
set -e
|
||||
DOTFILES=$(cd $(dirname $0) && pwd -P)
|
||||
source "$DOTFILES/shell/common.sh"
|
||||
shopt -s nullglob
|
||||
|
||||
#
|
||||
install() {
|
||||
for topic in $*;
|
||||
do
|
||||
local path="$(dirname $0)/$topic"
|
||||
if [ -d "$path" ];
|
||||
then
|
||||
[ -f "$path/install" ] && . "$path/install"
|
||||
blue="\033[34m"
|
||||
magenta="\033[35m"
|
||||
green="\033[32m"
|
||||
orange="\033[33m"
|
||||
grey="\033[36m"
|
||||
red="\033[31m"
|
||||
nc="\033[0m"
|
||||
#
|
||||
|
||||
# Link available dotfiles (1 level deep)
|
||||
for file in "$1"/.[a-zA-Z]*
|
||||
do
|
||||
if [ -d "$file" ]; then
|
||||
for subfile in "$file"/*
|
||||
do
|
||||
local base="$HOME/$(basename $file)"
|
||||
[ ! -d "$base" ] && mkdir -p "$base"
|
||||
ln -svf "$subfile" "$base"
|
||||
done
|
||||
elif [ -f "$file" ]; then
|
||||
ln -svf "$file" "$HOME"
|
||||
fi
|
||||
done
|
||||
_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/^/ => /'; }
|
||||
}
|
||||
|
||||
# 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")
|
||||
_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
|
||||
done
|
||||
}
|
||||
|
||||
update() {
|
||||
for topic in $DOTFILES/.enabled.d/*/
|
||||
do
|
||||
[ -x "$topic/update" ] && . "$topic/update"
|
||||
done
|
||||
}
|
||||
|
||||
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 {} \;
|
||||
case $choice in
|
||||
y|Y) 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
|
||||
# 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
|
||||
case "$COMMAND" in
|
||||
i|install) install $* ;;
|
||||
u|update) update ;;
|
||||
c|clean) clean ;;
|
||||
r|reinstall)
|
||||
clean
|
||||
install $*
|
||||
;;
|
||||
*) usage ;;
|
||||
esac
|
||||
#
|
||||
# [[ -d ~/.bin ]] || _run mkdir -p ~/.bin
|
||||
PATH="$DOTFILES/.bin:$PATH"
|
||||
for topic in ${@:-$(find "$DOTFILES/.enabled.d" -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" "~/.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