forked from divad12/khan-dotfiles
-
Notifications
You must be signed in to change notification settings - Fork 18
/
setup.sh
executable file
·350 lines (300 loc) · 11.7 KB
/
setup.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
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
#!/bin/bash
# This has files that are used by Khan Academy developers. This setup
# script is OS-agnostic; it installs things like dotfiles, python
# libraries, etc that are the same on Linux, OS X, maybe even cygwin.
# It is intended to be idempotent; you can safely run it multiple
# times. It should be run from the root of the khan-dotfiles directory.
# Bail on any errors
set -e
# Install in $HOME by default, but can set an alternate destination via $1.
ROOT=${1-$HOME}
mkdir -p "$ROOT"
# the directory all repositories will be cloned to
REPOS_DIR="$ROOT/khan"
# derived path location constants
DEVTOOLS_DIR="$REPOS_DIR/devtools"
KACLONE_BIN="$DEVTOOLS_DIR/ka-clone/bin/ka-clone"
# Load shared setup functions.
. "$DEVTOOLS_DIR"/khan-dotfiles/shared-functions.sh
# the directory this script exists in, regardless of where it is called from
#
# TODO(mroth): some of the historical parts of this script assume the user is
# running this from within the directory (and they are in fact instructed to do
# so), but it may be worth auditing and removing all CWD requirements in the
# future.
DIR=$(dirname "$0")
# Will contain a string on a mac and be empty on linux
IS_MAC=$(which sw_vers || echo "")
IS_MAC_ARM=$(test "$(uname -m)" = "arm64" && echo arm64 || echo "")
# On a clean install of Mac OS X, /opt/homebrew/bin is not in the PATH.
# Thus, stuff installed with the arm64 version of homebrew is not visible.
# (This was not the case on intel macs where /usr/local/bin is in the path
# of a clean OS install - in which case all homebrew stuff is visible.)
if [[ -n "${IS_MAC_ARM}" ]]; then
echo "Adding arm64 homebrew to path"
PATH=/opt/homebrew/bin:${PATH}
fi
trap exit_warning EXIT # from shared-functions.sh
warnings=""
add_warning() {
echo "WARNING: $*"
warnings="$warnings\nWARNING: $*"
}
add_fatal_error() {
echo "FATAL ERROR: $*"
exit 1
}
check_dependencies() {
echo "Checking system dependencies"
# We need git >=1.7.11 for '[push] default=simple'.
if ! git --version | grep -q -e 'version 1.7.1[1-9]' \
-e 'version 1.[89]' \
-e 'version 2'; then
echo "Must have git >= 1.8. See http://git-scm.com/downloads"
exit 1
fi
# For convenience, let's make sure github is in the known-hosts
# file as well. This is a noop if it's already in there.
mkdir -p ~/.ssh
grep -q github.com ~/.ssh/known_hosts 2>/dev/null || \
ssh-keyscan github.com >> ~/.ssh/known_hosts
# Make sure we have the ssh keys we need to pull from git.
maybe_generate_ssh_keys # in shared-functions.sh
# You need to have run the setup to install binaries: node, npm/etc.
if ! npm --version >/dev/null; then
echo "You must install binaries before running $0. See"
echo " https://khanacademy.atlassian.net/wiki/x/VgKiC"
exit 1
fi
}
install_dotfiles() {
echo "Installing and updating dotfiles (.bashrc, etc)"
# Remove cached variables in case user did migration assistant
# It will be regenerated by .profile.khan
rm -rf ~/.config/khan/cache/dotfiles
# Most dotfiles are installed as symlinks.
# (But we ignore .git/etc which are actually part of the repo!)
#
# TODO(mroth): for organization, we should keep all dotfiles in a
# subdirectory, but to make that change will require repairing old symlinks
# so they don't break when the target moves.
for file in .*.khan .*.khan-xtra .git_template/commit_template .vim/ftplugin/*.vim; do
mkdir -p "$ROOT/$(dirname "$file")"
source=$(pwd)/"$file"
dest="$ROOT/$file"
# if dest is already a symlink pointing to correct source, skip it
if [ -h "$dest" -a "$(readlink "$dest")" = "$source" ]; then
:
# else if dest already exists, warn user and skip dotfile
elif [ -e "$dest" ]; then
add_warning "Not symlinking to $dest because it already exists."
# otherwise, verbosely symlink the file (with --force)
else
ln -sfvn "$source" "$dest"
fi
done
# A few dotfiles are copied so the user can change them. They all
# have names like bashrc.default, which is installed as .bashrc.
# They all have the property they 'include' khan-specific code.
for file in *.default; do
dest="$ROOT/.$(echo "$file" | sed s/.default$//)" # foo.default -> .foo
ka_version=.$(echo "$file" | sed s/default/khan/) # .bashrc.khan, etc.
if [ ! -e "$dest" ]; then
cp -f "$file" "$dest"
elif ! fgrep -q "$ka_version" "$dest"; then
echo "WARNING: $dest does not 'include' $ka_version;"
echo " (see $(pwd)/$file and add the contents to $dest)"
should_append=$(get_yn_input "Should we append $file to $dest so things work as expected?" "y")
if [ "$should_append" = "y" ]; then
cat "$file" >> "$dest"
fi
fi
done
# *.template files are also copied so the user can change them. Unlike the
# "default" files above, these do not include KA code, they are merely
# useful defaults we want to install if the user doesnt have anything
# already.
#
# We should avoid installing anything absolutely not necessary in this
# category, so for now, this is just a global .gitignore
for file in *.template; do
dest="$ROOT/.$(echo "$file" | sed s/.template$//)" # foo.default -> .foo
if [ ! -e "$dest" ]; then
cp -f "$file" "$dest"
fi
done
# Make sure we pick up any changes we've made, so later steps of install don't fail.
. ~/.profile
}
# clone a repository without any special sauce. should only be used in order to
# bootstrap ka-clone, or if you are certain you don't want a khanified repo.
# $1: url of the repository to clone. $2: directory to put repo
clone_repo() {
(
mkdir -p "$2"
cd "$2"
dirname=$(basename "$1")
if [ ! -d "$dirname" ]; then
git clone "$1"
cd "$dirname"
git submodule update --init --recursive
fi
)
}
clone_kaclone() {
if [ -d "$DEVTOOLS_DIR/ka-clone" ]; then
echo "Updating ka-clone tool"
git -C "$DEVTOOLS_DIR/ka-clone" checkout master
git -C "$DEVTOOLS_DIR/ka-clone" pull
else
echo "Installing ka-clone tool"
clone_repo [email protected]:Khan/ka-clone "$DEVTOOLS_DIR"
fi
}
clone_webapp() {
echo "Cloning main webapp repository"
# By this point, we must have git and ka-clone working, so a failure likely
# means the user doesn't have access to webapp (it's the only private repo
# we clone here) -- we give a more useful error than just "not found".
kaclone_repo [email protected]:Khan/webapp "$REPOS_DIR/" -p --email="$gitmail" || add_fatal_error \
"Unable to clone Khan/webapp -- perhaps you don't have access? " \
"If you can't view https://github.com/Khan/webapp, ask #it in " \
"Slack to be added."
}
clone_mobile() {
if [ ! -d "$REPOS_DIR/mobile" ]; then
update "Cloning mobile repository..."
kaclone_repo [email protected]:Khan/mobile "$REPOS_DIR/" -p --email="$gitmail" || add_fatal_error \
"Unable to clone Khan/mobile -- perhaps you don't have access? " \
"If you can't view https://github.com/Khan/mobile, ask #it in " \
"Slack to be added."
fi
}
# clones a specific devtool
clone_devtool() {
kaclone_repo "$1" "$DEVTOOLS_DIR" --email="$gitmail"
# TODO(mroth): for devtools only, we should try to do:
# git pull --quiet --ff-only
# but need to make sure we do it in master only!
}
# clones all devtools
clone_devtools() {
echo "Installing devtools"
clone_devtool [email protected]:Khan/ka-clone # already cloned, so will --repair the first time
clone_devtool [email protected]:Khan/khan-linter
clone_devtool [email protected]:Khan/git-workflow
clone_devtool [email protected]:Khan/our-lovely-cli
}
# khan-dotfiles is also a KA repository...
# thus, use kaclone --repair on current dir to khanify it as well!
kaclone_repair_self() {
(cd "$DIR" && "$KACLONE_BIN" --repair --quiet)
}
clone_repos() {
clone_kaclone
clone_devtools
clone_webapp
clone_mobile
kaclone_repair_self
}
# Sets up pipenv
setup_python() {
# Used by various infra projects for managing python3 environment
echo "Installing pipenv for python3"
pip3_install -q pipenv
}
# Must have cloned the repos first.
install_deps() {
echo "Installing global dependencies"
# Need to install yarn first before run `make install_deps`
# in webapp.
echo "Installing yarn"
if ! which yarn >/dev/null 2>&1; then
if [[ -n "${IS_MAC}" ]]; then
# Mac does not require root - npm is in /usr/local via brew
npm install -g yarn
else
# Linux requires sudo permissions
sudo npm install -g yarn
fi
fi
# By default, third party Go tools are install to this directory
mkdir -p "$ROOT"/go/bin
# Install all the requirements for khan
# This also installs npm deps.
echo "Installing webapp dependencies"
# This checks for gcloud, so we do it after install_and_setup_gcloud.
( cd "$REPOS_DIR/webapp" && make deps )
}
install_and_setup_gcloud() {
"$DEVTOOLS_DIR"/khan-dotfiles/setup-gcloud.sh -r "$ROOT"
}
download_db_dump() {
if ! [ -f "$REPOS_DIR/webapp/datastore/current.sqlite" ]; then
echo "Downloading a recent datastore dump"
( cd "$REPOS_DIR/webapp" ; make current.sqlite )
fi
}
create_pg_databases() {
echo "Creating postgres databases"
( cd "$REPOS_DIR/webapp" ; make pg_create )
}
# Make sure we store userinfo so we can pass appropriately when ka-cloning.
update_userinfo() {
echo "Updating your git user info"
# check if git user.name exists anywhere, if not, set that globally
set +e
gitname=$(git config user.name)
set -e
if [ -z "$gitname" ]; then
read -p "Enter your full name (First Last): " name
git config --global user.name "$name"
gitname=$(git config user.name)
fi
# Set a "sticky" KA email address in the global kaclone.email gitconfig
# ka-clone will check for this as the default to use when cloning
# (we still pass --email to ka-clone in this script for redundancy, but
# this setting will apply to any future CLI usage of ka-clone.)
set +e
gitmail=$(git config kaclone.email)
set -e
if [ -z "$gitmail" ]; then
read -p "Enter your KA email, without the @khanacademy.org ($USER): " emailuser
emailuser=${emailuser:-$USER}
defaultemail="[email protected]"
git config --global kaclone.email "$defaultemail"
gitmail=$(git config kaclone.email)
echo "Setting kaclone default email to $defaultemail"
fi
}
# Install webapp's git hooks
install_hooks() {
echo "Installing git hooks"
( cd "$REPOS_DIR/webapp" && make hooks )
}
install_our_lovely_cli() {
cd "$DEVTOOLS_DIR/our-lovely-cli"
npm install
}
check_dependencies
install_dotfiles
update_userinfo
# the order for these is (mostly!) important, beware
setup_python
clone_repos
install_our_lovely_cli # pre-req: clone_repos
install_and_setup_gcloud # pre-req: setup_python
install_deps # pre-reqs: clone_repos, install_and_setup_gcloud
install_hooks # pre-req: clone_repos
download_db_dump # pre-req: install_deps
create_pg_databases # pre-req: install_deps
echo
echo "---------------------------------------------------------------------"
if [ -n "$warnings" ]; then
echo "-- WARNINGS:"
# echo is very inconsistent about whether it supports -e. :-(
echo "$warnings" | sed 's/\\n/\n/g'
else
echo "DONE!"
fi
trap - EXIT