/
dotfiles
executable file
·200 lines (177 loc) · 5.57 KB
/
dotfiles
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#!/bin/bash
echo 'Dotfiles - "Cowboy" Ben Alman - http://benalman.com/'
if [[ "$1" == "-h" || "$1" == "--help" ]]; then cat <<HELP
Usage: $(basename "$0")
See the README for documentation.
https://github.com/cowboy/dotfiles
Copyright (c) 2012 "Cowboy" Ben Alman
Licensed under the MIT license.
http://benalman.com/about/license/
HELP
exit; fi
# Logging stuff.
function e_header() { echo -e "\n\033[1m$@\033[0m"; }
function e_success() { echo -e " \033[1;32m✔\033[0m $@"; }
function e_error() { echo -e " \033[1;31m✖\033[0m $@"; }
function e_arrow() { echo -e " \033[1;33m➜\033[0m $@"; }
# Given a list of desired items and installed items, return a list
# of uninstalled items. Arrays in bash are insane (not in a good way).
function to_install() {
local debug desired installed i desired_s installed_s remain
if [[ "$1" == 1 ]]; then debug=1; shift; fi
# Convert args to arrays, handling both space- and newline-separated lists.
read -ra desired < <(echo "$1" | tr '\n' ' ')
read -ra installed < <(echo "$2" | tr '\n' ' ')
# Sort desired and installed arrays.
unset i; while read -r; do desired_s[i++]=$REPLY; done < <(
printf "%s\n" "${desired[@]}" | sort
)
unset i; while read -r; do installed_s[i++]=$REPLY; done < <(
printf "%s\n" "${installed[@]}" | sort
)
# Get the difference. comm is awesome.
unset i; while read -r; do remain[i++]=$REPLY; done < <(
comm -13 <(printf "%s\n" "${installed_s[@]}") <(printf "%s\n" "${desired_s[@]}")
)
[[ "$debug" ]] && for v in desired desired_s installed installed_s remain; do
echo "$v ($(eval echo "\${#$v[*]}")) $(eval echo "\${$v[*]}")"
done
echo "${remain[@]}"
}
# Offer the user a chance to skip something.
function skip() {
REPLY=noskip
read -t 5 -n 1 -s -p "To skip, press X within 5 seconds. "
if [[ "$REPLY" =~ ^[Xx]$ ]]; then
echo "Skipping!"
else
echo "Continuing..."
return 1
fi
}
# Initialize.
function init_do() {
source "$2"
}
# Copy files.
function copy_header() { e_header "Copying files into home directory"; }
function copy_test() {
if [[ -e "$2" && ! "$(cmp "$1" "$2" 2> /dev/null)" ]]; then
echo "same file"
elif [[ "$1" -ot "$2" ]]; then
echo "destination file newer"
fi
}
function copy_do() {
e_success "Copying ~/$1."
cp "$2" ~/
}
# Link files.
function link_header() { e_header "Linking files into home directory"; }
function link_test() {
[[ "$1" -ef "$2" ]] && echo "same file"
}
function link_do() {
e_success "Linking ~/$1."
ln -sf ${2#$HOME/} ~/
}
# Copy, link, init, etc.
function do_stuff() {
local base dest skip
local files=(~/.dotfiles/$1/*)
# No files? abort.
if (( ${#files[@]} == 0 )); then return; fi
# Run _header function only if declared.
[[ $(declare -f "$1_header") ]] && "$1_header"
# Iterate over files.
for file in "${files[@]}"; do
base="$(basename $file)"
dest="$HOME/$base"
# Run _test function only if declared.
if [[ $(declare -f "$1_test") ]]; then
# If _test function returns a string, skip file and print that message.
skip="$("$1_test" "$file" "$dest")"
if [[ "$skip" ]]; then
e_error "Skipping ~/$base, $skip."
continue
fi
# Destination file already exists in ~/. Back it up!
if [[ -e "$dest" ]]; then
e_arrow "Backing up ~/$base."
# Set backup flag, so a nice message can be shown at the end.
backup=1
# Create backup dir if it doesn't already exist.
[[ -e "$backup_dir" ]] || mkdir -p "$backup_dir"
# Backup file / link / whatever.
mv "$dest" "$backup_dir"
fi
fi
# Do stuff.
"$1_do" "$base" "$file"
done
}
# Enough with the functions, let's do stuff.
# If Git is not installed...
if [[ ! "$(type -P git)" ]]; then
# OSX
if [[ "$OSTYPE" =~ ^darwin ]]; then
# It's easiest to get Git via Homebrew, so get that first.
if [[ ! "$(type -P brew)" ]]; then
e_header "Installing Homebrew"
/usr/bin/ruby -e "$(curl -fsSL https://raw.github.com/gist/323731)"
brew update
fi
# If Homebrew was installed, install Git.
if [[ "$(type -P brew)" ]]; then
e_header "Installing Git"
brew install git
fi
# Ubuntu.
elif [[ "$(cat /etc/issue 2> /dev/null)" =~ Ubuntu ]]; then
# Git is fairly easy.
e_header "Installing Git"
sudo apt-get -qq install git-core
fi
fi
# If Git isn't installed by now, something exploded. We gots to quit!
if [[ ! "$(type -P git)" ]]; then
e_error "Git should be installed. It isn't. Aborting."
exit 1
fi
# Initialize.
if [[ ! -d ~/.dotfiles ]]; then
new_dotfiles_install=1
# ~/.dotfiles doesn't exist? Clone it!
e_header "Downloading dotfiles"
git clone --recursive git://github.com/cowboy/dotfiles.git ~/.dotfiles
cd ~/.dotfiles
else
# Make sure we have the latest files.
e_header "Updating dotfiles"
cd ~/.dotfiles
git pull
git submodule update --init --recursive --quiet
fi
# Tweak file globbing.
shopt -s dotglob
shopt -s nullglob
# Create caches directory, if it doesn't already exist.
mkdir -p "$HOME/.dotfiles/caches"
# If backups are needed, this is where they'll go.
backup_dir="$HOME/.dotfiles/backups/$(date "+%Y_%m_%d-%H_%M_%S")/"
backup=
# Execute code for each file in these subdirectories.
do_stuff "init"
do_stuff "copy"
do_stuff "link"
# Alert if backups were made.
if [[ "$backup" ]]; then
echo -e "\nBackups were moved to ~/${backup_dir#$HOME/}"
fi
# Lest I forget to do a few additional things...
if [[ "$new_dotfiles_install" && -e "conf/firsttime_reminder.sh" ]]; then
e_header "First-Time Reminders"
source "conf/firsttime_reminder.sh"
fi
# All done!
e_header "All done!"