This repository has been archived by the owner on Sep 23, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
/
bootstrap.sh
242 lines (211 loc) · 6.52 KB
/
bootstrap.sh
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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
#!/bin/bash
ORG=18F
REPOS=laptoptwo
ORG_REPOS=${ORG}/${REPOS}
# GOAL
# Use Ansible for systems automation, not Bash.
# Therefore, my goal is to get to Ansible as quickly as possible.
# EXIT CODES
INSTALL_HOMEBREW_FAILED=-100
INSTALL_GIT_FAILED=-101
INSTALL_ANSIBLE_FAILED=-102
####################################
# LOGGING AND MESSAGING
# These helper functions are used throughout for reporting and
# logging what is going on. By default, very little goes to the
# user, but everything does go to the log.
# PURPOSE
# Creates a temporary logfile in a way that lets the OS
# decide where it should go.
create_logfile () {
export LAPTOP_SETUP_LOGFILE=$(mktemp -t "laptop-log")
}
# PURPOSE
# Sets up redirects so that STDOUT and STDERR make their way to
# a temporary logfile.
setup_logging () {
# https://serverfault.com/questions/103501/how-can-i-fully-log-all-bash-scripts-actions
# Save all the pipes.
# 3 is Stdout. 4 is stderr.
exec 3>&1 4>&2
# Restore some.
trap 'exec 2>&4 1>&3' 0 1 2 3
# Redirect stdout/stderr to a logfile.
exec 1>> "${LAPTOP_SETUP_LOGFILE}" 2>&1
_status "Logfile started. It can be accessed for debugging purposes."
_variable "LAPTOP_SETUP_LOGFILE"
}
# COLORS!
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
PURPLE='\033[0;35m'
# No color
NC='\033[0m'
_msg () {
TAG="$1"
COLOR="$2"
MSG="$3"
printf "[${TAG}] ${MSG}\n" >&1
printf "[${COLOR}${TAG}${NC}] ${MSG}\n" >&3
}
_status () {
MSG="$1"
_msg "STATUS" "${GREEN}" "${MSG}"
}
_debug () {
MSG="$1"
_msg "DEBUG" "${YELLOW}" "${MSG}"
}
_err () {
MSG="$1"
_msg "ERROR" "${RED}" "${MSG}"
}
_variable () {
VAR="$1"
_msg "$VAR" "${PURPLE}" "${!VAR}"
}
####################################
# CHECKS
# These are helper functions for checking if things exist,
# etc. Used a lot, clarifies the code.
# https://stackoverflow.com/questions/592620/how-can-i-check-if-a-program-exists-from-a-bash-script
command_exists () {
type "$1" &> /dev/null ;
}
command_does_not_exist () {
if command_exists "$1"; then
return 1
else
return 0
fi
}
# PURPOSE
# Restores the file descriptors after capturing/redirecting.
restore_console () {
# https://stackoverflow.com/questions/21106465/restoring-stdout-and-stderr-to-default-value
# Reconnect stdout and close the third filedescriptor.
exec 1>&4 4>&-
# Reconnect stderr
exec 1>&3 3>&-
}
# PURPOSE
# Exit, and let the user know what to do.
exit_with_status () {
$EXITCODE = $1
_err ""
_err "The logfile for this run can be found at"
_err ""
_err "${LAPTOP_SETUP_LOGFILE}"
_err ""
_err "The contents of the logfile have been copied to the clipboard."
_err "Please command-click the link below to open the laptoptwo issue tracker."
_err ""
_err "https://github.com/${ORG}/${REPOS}/issues"
_err ""
_err "Then, paste (command-v) the contents of the clipboard into a new issue."
_err ""
_err "You can run the command"
_err ""
_err "pbcopy < ${LAPTOP_SETUP_LOGFILE}"
_err ""
_err "to re-copy the log to the clipboard."
_err "Exiting."
pbcopy < "${LAPTOP_SETUP_LOGFILE}"
exit $EXITCODE
}
# PURPOSE
# Installs homebrew.
install_homebrew () {
if command_does_not_exist brew; then
_status "Installing the Homebrew package manager."
_status "You will probably be prompted for your password."
ruby -e "$(curl --location --fail --silent --show-error https://raw.githubusercontent.com/Homebrew/install/master/install)"
# This will be local to this script for now; Ansible will set the shell
# variable properly once we're bootstrapped.
export PATH="/usr/local/bin:$PATH"
else
_status "Update Homebrew."
brew update
fi
}
# PURPOSE
# What it says.
exit_if_install_homebrew_failed () {
if command_does_not_exist brew; then
_err "Homebrew cannot be found. Exiting."
exit_with_status $INSTALL_HOMEBREW_FAILED
fi
}
# install_tooling_via_brew :: None -> None
# PURPOSE
# What it says on the tin. Everything is easier if we have
# python and git installed.
install_tooling_via_brew () {
_status "Installing python via brew."
# This will error if the package is not installed.
# Therefore, it will install. Or, if it is installed, nothing will happen.
# https://apple.stackexchange.com/questions/284379/with-homebrew-how-to-check-if-a-software-package-is-installed
brew list python || brew install python
_status "Checking for git."
if command_does_not_exist git; then
_status "Installing git."
brew list git || brew install git
fi
_status "Checking for ansible."
if command_does_not_exist ansible; then
_status "Installing ansible."
brew list ansible || brew install ansible
fi
}
exit_if_install_tooling_via_brew_failed () {
if command_does_not_exist git; then
_err "git should be installed at this point; it is not. Exiting."
exit_with_status $INSTALL_GIT_FAILED
fi
if command_does_not_exist ansible; then
_err "ansible should be installed at this point; it is not. Exiting."
exit_with_status $INSTALL_ANSIBLE_FAILED
fi
if command_does_not_exist ansible-playbook; then
_err "ansible-playbook should be installed at this point; it is not. Exiting."
exit_with_status $INSTALL_ANSIBLE_FAILED
fi
if command_does_not_exist ansible-pull; then
_err "ansible-pull should be installed at this point; it is not. Exiting."
exit_with_status $INSTALL_ANSIBLE_FAILED
fi
}
# PURPOSE
# Creates a temporary directory in the user's temp space.
# On macOS, mktemp doesn't behave the same as Linux. Beware.
setup_tmp_dir () {
# See
# https://unix.stackexchange.com/questions/30091/fix-or-alternative-for-mktemp-in-os-x
# for why this is necessary.
_status "Creating a temporary directory."
_status "(This is small, and will disappear on reboot.)"
DIRNAME=laptop-$(date +%s)
export LAPTOP_TMP_DIR
LAPTOP_TMP_DIR=$(mktemp -d -t "$DIRNAME")
}
run_playbook () {
pushd "${LAPTOP_TMP_DIR}" || exit
restore_console
ansible-pull -v -U https://github.com/${ORG_REPOS} playbook.yaml -v -i hosts
setup_logging
popd || exit
}
main () {
create_logfile
setup_logging
install_homebrew
exit_if_install_homebrew_failed
install_tooling_via_brew
exit_if_install_tooling_via_brew_failed
setup_tmp_dir
# Let the playbook drive the exit code.
run_playbook
exit $?
}
main