Skip to content

Commit

Permalink
Simplified bootstrapper, source optional bootstrap script in each top…
Browse files Browse the repository at this point in the history
…ic instead which in turn is responsible for symlinking files and installing things
  • Loading branch information
agross committed Nov 11, 2016
1 parent 95106b6 commit a23e896
Show file tree
Hide file tree
Showing 23 changed files with 133 additions and 146 deletions.
51 changes: 32 additions & 19 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -50,28 +50,42 @@ agross@linux ~/.dotfiles
$ ./bootstrap
[ INFO ] Installing dotfiles from /home/agross/.dotfiles
[ INFO ] Installing dotfiles in /home/agross for platform linux
[ INFO ] Installing dotfiles to $HOME=/home/agross for $OSTYPE=linux
[ OK ] Linked /home/agross/.dotfiles == /home/agross/.dotfiles
[ INFO ] Running /home/agross/.dotfiles/bash/bootstrap
[ OK ] Linked /home/agross/.dotfiles/bash/bash_profile.symlink == /home/agross/.bash_profile
[ OK ] Linked /home/agross/.dotfiles/bash/bashrc.symlink == /home/agross/.bashrc
[ OK ] Linked /home/agross/.dotfiles/bash/inputrc.symlink == /home/agross/.inputrc
[ INFO ] Running /home/agross/.dotfiles/git/bootstrap
[ OK ] Linked /home/agross/.dotfiles/git/gitconfig.symlink == /home/agross/.gitconfig
[ OK ] Linked /home/agross/.dotfiles/git/gitconfig.training.symlink == /home/agross/.gitconfig.training
[ OK ] Linked /home/agross/.dotfiles/git/git-wtfrc.symlink == /home/agross/.git-wtfrc
[ OK ] Linked /home/agross/.dotfiles/git/gitshrc.symlink == /home/agross/.gitshrc
[ OK ] Linked /home/agross/.dotfiles/git/gitconfig.symlink == /home/agross/.gitconfig
[ INFO ] Skipped /home/agross/.dotfiles/mintty as it is excluded for platform linux
[ INFO ] Running /home/agross/.dotfiles/mintty/bootstrap
[ INFO ] Running /home/agross/.dotfiles/ruby/bootstrap
[ OK ] Linked /home/agross/.dotfiles/ruby/gemrc.symlink == /home/agross/.gemrc
[ OK ] Linked /home/agross/.dotfiles/ruby/guard.rb.symlink == /home/agross/.guard.rb
[ OK ] Linked /home/agross/.dotfiles/ruby/irbrc.symlink == /home/agross/.irbrc
[ OK ] Linked /home/agross/.dotfiles/ruby/pryrc.symlink == /home/agross/.pryrc
[ INFO ] Running /home/agross/.dotfiles/screen/bootstrap
[ OK ] Linked /home/agross/.dotfiles/screen/screenrc.symlink == /home/agross/.screenrc
[ INFO ] Skipped /home/agross/.dotfiles/ssh as it is excluded for platform linux
[ INFO ] Running /home/agross/.dotfiles/ssh/bootstrap
[ INFO ] Running /home/agross/.dotfiles/tmux/bootstrap
[ OK ] Linked /home/agross/.dotfiles/tmux/tmux.conf.symlink == /home/agross/.tmux.conf
[ INFO ] Running /home/agross/.dotfiles/vim/bootstrap
[ OK ] Linked /home/agross/.dotfiles/vim/vim.symlink == /home/agross/.vim
[ OK ] Linked /home/agross/.dotfiles/vim/vimrc.symlink == /home/agross/.vimrc
[ INFO ] Running /home/agross/.dotfiles/wget/bootstrap
[ OK ] Linked /home/agross/.dotfiles/wget/wgetrc.symlink == /home/agross/.wgetrc
[ OK ] Linked /home/agross/.dotfiles/zsh/zlogin.symlink == /home/agross/.zlogin
[ INFO ] Running /home/agross/.dotfiles/zsh/bootstrap
[ OK ] Linked /home/agross/.dotfiles/zsh/zprofile.symlink == /home/agross/.zprofile
[ OK ] Linked /home/agross/.dotfiles/zsh/zshrc.symlink == /home/agross/.zshrc
[ OK ] Linked /home/agross/.dotfiles/zsh/zshenv.symlink == /home/agross/.zshenv
[ OK ] Linked /home/agross/.dotfiles/tmux/tmux.conf.symlink to /home/agross/.tmux.conf
[ OK ] Linked /home/agross/.dotfiles/zsh/zshrc.symlink == /home/agross/.zshrc
[ INFO ] Running /home/agross/.dotfiles/htop/bootstrap
[ OK ] Linked /home/agross/.dotfiles/htop/htoprc.symlink == /home/agross/.htoprc
[ INFO ] Running /home/agross/.dotfiles/elixir/bootstrap
[ OK ] Linked /home/agross/.dotfiles/elixir/iex.exs.symlink == /home/agross/.iex.exs
[ INFO ] Running /home/agross/.dotfiles/gpg/bootstrap
[ INFO ] All installed from /home/agross/.dotfiles
```
Expand All @@ -83,9 +97,7 @@ dotfiles are structured around topics. Topics are directories under the dotfiles
```
dotfiles
├─ example
| ├─ .exclude-platforms # bootstrapper #1: specifies excluded platforms for this topic
| ├─ example.symlink # bootstrapper #2: symlinked to ~/.example
| ├─ .install.sh # bootstrapper #3: performs additional installation after symlinking
| ├─ bootstrap # bootstrapper: script to symlink files and install additional programs
| ├─ zprofile.zsh # zsh #1: run for login shells
| ├─ path.zsh # zsh #2: modifies $PATH
| ├─ something.zsh # zsh #3: additional setup
Expand All @@ -104,21 +116,22 @@ There are some special files that either the `bootstrap` script or [zsh](#shell)

### `bootstrap`-specific files

#### topic/\*.symlink

Files and directories with a `.symlink` extension are symlinked to your home directory with the `.symlink` extension removed and a dot character prepended.
The bootstrapper will create an implicit symlink for the dotfiles directory itself. It will always be symlinked to `$HOME/.dotfiles` (unless you [`git clone`d the dotfiles](#installation) there.) This makes it easier to refer to other dotfiles from within dotfiles as you can use a static path. For example, [my git mergetool scripts point to `$HOME/.dotfiles/git/tools`](https://github.com/agross/dotfiles/blob/master/git/gitconfig.symlink#L56).

There is an implicit symlink for the dotfiles directory itself: It will always be symlinked to `$HOME/.dotfiles` (unless you [`git clone`d the dotfiles](#installation) there.)
#### topic/bootstrap

#### topic/.install.sh
`bootstrap` will source each `topic/bootstrap` file and thereby run it using bash. The script can then

An optional installer script that is run after the topic's symlinks have been created.
* symlink files using the `symlink $source $target` function. `$target` may be omitted, e.g. `symlink foo.symlink` will create the symlink as `$HOME/.foo` pointing to `$DOTFILES/topic/foo.symlink`.
* install additional programs at the script's discretion.

#### topic/.exclude-platforms
Each `topic/bootstrap` has the following environment variables available:

`bootstrap` will not process the topic if the current platform is listed in the file (one platform per line). The bootstrapper currently detects platforms `cygwin`, `windows`, `linux`, `mac`, `msys` and `freebsd`.
| Variable | Description |
| ----------| ----------- |
| `$OSTYPE` | Normalized operating system, e.g. `linux`, `mac`, `windows` for msysgit and Git for Windows, `cygwin`, or the original `$OSTYPE` |
| `$HOME` | Home directory for the operating system, e.g. `$HOME` for all Linux-style `$OSTYPE`s and `/c/Users/<you>/` for `$OSTYPE == 'windows'` |

If the `.exclude-platforms` file does not exist or if it is empty, the topic is processed on all platforms.

### [zsh](#shell)-specific files

Expand Down
1 change: 0 additions & 1 deletion bash/.exclude-platforms

This file was deleted.

5 changes: 5 additions & 0 deletions bash/bootstrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[[ "$OSTYPE" == 'windows' ]] && return

symlink "$topic/bash_profile.symlink"
symlink "$topic/bashrc.symlink"
symlink "$topic/inputrc.symlink"
167 changes: 55 additions & 112 deletions bootstrap
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
set -e

# Show executed commands.
# set -x
#set -x

light_red="\e[01;31m"
green="\e[0;32m"
Expand Down Expand Up @@ -37,32 +37,19 @@ fail () {
}

platforms () {
if [ -n "$platforms_cache" ]; then
printf "$platforms_cache"
return
fi

if [ "$(uname)" == "Darwin" ]; then
printf 'mac'
elif [ "$(uname -s)" == "Linux" ]; then
printf 'linux'
elif [ "$(uname -s)" == "FreeBSD" ]; then
printf 'freebsd'
elif [ "$(uname -o)" == "Cygwin" ]; then
printf "cygwin\nwindows"
elif [ "$(uname -o)" == "Msys" ]; then
printf 'msys'
else
printf 'unknown'
fi
case "$OSTYPE" in
linux*) printf 'linux';;
darwin) printf 'mac';;
msys) printf 'windows';;
cygwin) printf "cygwin\nwindows";;
*) printf "$OSTYPE";;
esac
}
# Cache value.
export platforms_cache="$(platforms)"

readlink_e () {
local file=$1
local file="$1"

if [ "$(platforms)" == 'freebsd' -o "$(platforms)" == 'mac' ]; then
if [[ "$(platforms)" == 'mac' ]]; then
printf "$(realpath "$file")"
return $?
fi
Expand All @@ -71,49 +58,33 @@ readlink_e () {
}

home_directory () {
local platform=$1

case "$platform" in
mac|linux|msys|cygwin|freebsd )
home=$HOME
;;
windows )
home="$(printf "%s" "$(cygpath --unix $USERPROFILE)")"
;;
* )
fail "$(printf "Cannot determine home directories for platform %b%s%b" $green "$platform" $reset_color)"
;;
case "${1?Need platform}" in
windows) printf "$(cygpath --unix $USERPROFILE)";;
*) printf "$HOME";;
esac
}

platform_excluded () {
local topic=$1
local platform=$2
local exclude_file="$topic/.exclude-platforms"

if [ ! -f "$exclude_file" ]; then
return 1
fi

if [ -n "$(/bin/grep "$platform" "$exclude_file")" ]; then
return 0
symlink () {
local src="${1?Need link source}"
# If dst is not given, use src file name in $HOME, prepended with ., minus .symlink extension.
local dst="$2"
if [[ -z "$dst" ]]; then
local filename="$(basename "$src")"
local dst="$HOME/.${filename%.*}"
fi

return 1
}

link_file () {
local src=$1
local dst=$2

local overwrite= backup= skip=
local action=

if [ -f "$dst" -o -d "$dst" -o -L "$dst" ]; then
if [ "$overwrite_all" == "false" ] && [ "$backup_all" == "false" ] && [ "$skip_all" == "false" ]; then
local current_src="$(readlink_e "$dst")"
if [[ -f "$dst" ]] || \
[[ -d "$dst" ]] || \
[[ -L "$dst" ]]; then
if [[ "$overwrite_all" == "false" ]] && \
[[ "$backup_all" == "false" ]] && \
[[ "$skip_all" == "false" ]]; then
local current_target="$(readlink_e "$dst")"

if [ "$current_src" == "$src" ]; then
if [[ "$current_target" == "$src" ]]; then
skip=true;
else
user "$(printf "File already exists: %b%s%b (%b%s%b)
Expand All @@ -134,32 +105,13 @@ link_file () {
read -n 1 action < /dev/tty

case "$action" in
o )
overwrite=true
break
;;
O )
overwrite_all=true
break
;;
b )
backup=true
break
;;
B )
backup_all=true
break
;;
s )
skip=true
break
;;
S )
skip_all=true
break
;;
* )
;;
o) overwrite=true; break;;
O) overwrite_all=true; break;;
b) backup=true; break;;
B) backup_all=true break;;
s) skip=true; break;;
S) skip_all=true; break ;;
*) ;;
esac
done
fi
Expand All @@ -184,12 +136,11 @@ link_file () {
fi
fi

if [ "$skip" != "true" ]; then # "false" or empty
if [ "$skip" != "true" ]; then # "false" or empty.
# Create native symlinks on Windows.
export CYGWIN=winsymlinks:nativestrict
ln -s "$1" "$2"
CYGWIN=winsymlinks:nativestrict ln --symbolic "$src" "$dst"

success "$(printf "Linked %b%s%b to %b%s%b" $green "$1" $reset_color $green "$2" $reset_color)"
success "$(printf "Linked %b%s%b to %b%s%b" $green "$src" $reset_color $green "$dst" $reset_color)"
fi
}

Expand All @@ -202,40 +153,32 @@ install_dotfiles () {

local platforms="$(platforms)"
while IFS=$'\n' read -r platform; do
local home
home_directory "$platform"
local HOME="$(home_directory "$platform")"
local OSTYPE="$platform"

echo
info "$(printf "Installing dotfiles in %b%s%b for platform %b%s%b" $green "$home" $reset_color $green "$platform" $reset_color)"
info "$(printf "Installing dotfiles to %b\$HOME%b=%b%s%b for %b\$OSTYPE%b=%b%s%b" \
$light_red $reset_color \
$green "$HOME" $reset_color \
$light_red $reset_color \
$light_yellow "$OSTYPE" $reset_color)"

# First, add a symlink for this dotfiles directory.
local dotfiles="$home/.dotfiles"
link_file "$dotfiles_root" "$dotfiles"
symlink "$dotfiles_root" "$HOME/.dotfiles"

# Find direct child directories (topics), exclude those starting with dots.
local topics="$(/usr/bin/find "$dotfiles_root" -mindepth 1 -maxdepth 1 -type d -not -name '\.*')"
# Find direct child directories (topics).
local topics="$(/usr/bin/find "$dotfiles_root" -mindepth 1 -maxdepth 1 -type d)"
while IFS=$'\n' read -r topic; do
[[ -z "$topic" ]] && continue

if platform_excluded "$topic" "$platform"; then
info "$(printf "Skipped %b%s%b as it is excluded for platform %b%s%b" $green "$topic" $reset_color $green "$platform" $reset_color)"
continue
fi

# Find files and directories named *.symlink below each topic directory, exclude dot files.
local symlinks="$(/usr/bin/find "$topic" -mindepth 1 -maxdepth 1 \( -type f -or -type d \) -name '*.symlink' -not -name '\.*')"
while IFS=$'\n' read -r src; do
[[ -z "$src" ]] && continue

dst="$home/.$(basename "${src%.*}")"
link_file "$src" "$dst"
done <<< "$symlinks"
# Find (optional) topic/bootstrap script and run it.
local bootstrap="$topic/bootstrap"
if [[ -f "$bootstrap" ]]; then
info "$(printf "Running %b%s%b" \
$green "$bootstrap" $reset_color)"

# Run optional install script.
local install="$topic/.install.sh"
if [ -x "$install" ]; then
info "$(printf "Running %b%s%b" $green "$install" $reset_color)"
sh -c "$install"
# Run script from inside topic.
source "$bootstrap"
fi
done <<< "$topics"
done <<< "$platforms"
Expand Down
1 change: 1 addition & 0 deletions elixir/bootstrap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
symlink "$topic/iex.exs.symlink"
6 changes: 6 additions & 0 deletions git/bootstrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
symlink "$topic/gitconfig.symlink"
symlink "$topic/gitconfig.training.symlink"
symlink "$topic/git-wtfrc.symlink"

[[ "$OSTYPE" == "windows" ]] && return
symlink "$topic/gitshrc.symlink"
3 changes: 3 additions & 0 deletions gpg/bootstrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[[ "$OSTYPE" == 'windows' ]] && return

[[ -d "$topic/gnupg.symlink" ]] && symlink "$topic/gnupg.symlink"
2 changes: 0 additions & 2 deletions htop/.exclude-platforms

This file was deleted.

5 changes: 5 additions & 0 deletions htop/bootstrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[[ "$OSTYPE" == 'cygwin' ]] || \
[[ "$OSTYPE" == 'windows' ]] && \
return

symlink "$topic/htoprc.symlink"
4 changes: 0 additions & 4 deletions mintty/.exclude-platforms

This file was deleted.

3 changes: 3 additions & 0 deletions mintty/bootstrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[[ "$OSTYPE" != 'cygwin' ]] && return

symlink "$topic/minttyrc.symlink"
4 changes: 4 additions & 0 deletions ruby/bootstrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
symlink "$topic/gemrc.symlink"
symlink "$topic/guard.rb.symlink"
symlink "$topic/irbrc.symlink"
symlink "$topic/pryrc.symlink"
1 change: 0 additions & 1 deletion screen/.exclude-platforms

This file was deleted.

3 changes: 3 additions & 0 deletions screen/bootstrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[[ "$OSTYPE" == 'windows' ]] && return

symlink "$topic/screenrc.symlink"
3 changes: 0 additions & 3 deletions ssh/.exclude-platforms

This file was deleted.

3 changes: 3 additions & 0 deletions ssh/bootstrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[[ "$OSTYPE" != 'cygwin' ]] && [[ "$OSTYPE" != 'windows' ]] && return

symlink "$topic/ssh.symlink"
1 change: 0 additions & 1 deletion tmux/.exclude-platforms

This file was deleted.

3 changes: 3 additions & 0 deletions tmux/bootstrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[[ "$OSTYPE" == 'windows' ]] && return

symlink "$topic/tmux.conf.symlink"
1 change: 0 additions & 1 deletion vim/.exclude-platforms

This file was deleted.

4 changes: 4 additions & 0 deletions vim/bootstrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[[ "$OSTYPE" == 'windows' ]] && return

symlink "$topic/vim.symlink"
symlink "$topic/vimrc.symlink"
1 change: 1 addition & 0 deletions wget/bootstrap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
symlink "$topic/wgetrc.symlink"
Loading

0 comments on commit a23e896

Please sign in to comment.