-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathbuild.lib
More file actions
220 lines (194 loc) · 7.68 KB
/
build.lib
File metadata and controls
220 lines (194 loc) · 7.68 KB
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
# This shell script library sets variables and supplies functions shared
# between continuous integration build scripts. It is intended to be
# loaded, e.g., by scripts that are run by the Jenkins CI server. The
# working directory is assumed to be the root of a workspace where the
# website code is checked out into a subdirectory.
# First, define configuration options that may be overridden, e.g.:
# $ APPENGINE_ROOT=/home/ci/google_appengine build-commit.sh
# Commands:
: ${BASE_PYTHON:=python}
: ${DBG_PYTHON:=python2.7-dbg}
: ${MAKE:=make}
: ${VIRTUALENV:=virtualenv}
# Paths:
: ${APPENGINE_ROOT:=/usr/local/google_appengine}
: ${WORKSPACE_ROOT:=.}
: ${WEBSITE_ROOT:=$WORKSPACE_ROOT/webapp}
: ${VIRTUALENV_ROOT:=$WORKSPACE_ROOT/env}
: ${JENKINS_TMPDIR:=$WORKSPACE_ROOT/tmp}
: ${SECRETS_DIR:=$HOME/secrets_py}
# Make all the paths absolute, so clients can chdir with impunity.
# We use the nice side-effect of readlink -f that it absolutizes.
APPENGINE_ROOT=`readlink -f "$APPENGINE_ROOT"`
WEBSITE_ROOT=`readlink -f "$WEBSITE_ROOT"`
VIRTUALENV_ROOT=`readlink -f "$VIRTUALENV_ROOT"`
JENKINS_TMPDIR=`readlink -f "$JENKINS_TMPDIR"`
SECRETS_DIR=`readlink -f "$SECRETS_DIR"`
# Default HipChat info to use for alerting:
: ${HIPCHAT_ROOM:=HipChat Tests}
: ${HIPCHAT_SENDER:=Testybot}
# Sanity check that we're in the right place, the working directory
# above the website source directory.
if [ ! -d "$WEBSITE_ROOT" ]; then
echo "Website root \"$WEBSITE_ROOT\" not found"
exit 1
fi
# Set up the environment for subprocesses.
mkdir -p "$JENKINS_TMPDIR"
find "$JENKINS_TMPDIR" -mindepth 1 -maxdepth 1 -ctime +5 -print0 \
| xargs -0 rm -rf # keep temp files for 5 days
export TMPDIR="$JENKINS_TMPDIR"
export PATH="$VIRTUALENV_ROOT/bin:$PATH:$APPENGINE_ROOT"
# Set up a virtualenv and enter it. If already in a virtualenv this
# does nothing.
ensure_virtualenv() {
if [ -n "$VIRTUAL_ENV" ]; then
echo "You are already in a virtualenv"
return 0
elif [ -d "$VIRTUALENV_ROOT" ]; then
echo "Virtualenv already exists"
else
echo "Creating new virtualenv(s)"
# We create a "normal" virtualenv we use most of the time, and
# a "dbg" virtualenv that uses python2.7-dbg and lets us debug
# running python processes using gdb.
"$VIRTUALENV" --python="$BASE_PYTHON" "$VIRTUALENV_ROOT".normal
if `which "$DBG_PYTHON" >/dev/null 2>&1`; then
"$VIRTUALENV" --python="$DBG_PYTHON" "$VIRTUALENV_ROOT".dbg
# Need one more fix, as per http://stackoverflow.com/questions/22931774/how-to-use-gdb-python-debugging-extension-inside-virtualenv
cp -a /usr/lib/debug/usr/bin/python*gdb.py \
"$VIRTUALENV_ROOT".dbg/bin
fi
# Have 'env' point to 'env.normal'. To debug, you just manually
# change the symlink to point to env.dbg
ln -snf "`basename $VIRTUALENV_ROOT`".normal "$VIRTUALENV_ROOT"
echo <<EOF >"$WORKSPACE_ROOT/README.debugging"
If you want to be able to debug a running python process using gdb
(to debug hangs or segfaults, say), do the following:
ln -snf env.dbg env
<run your python process>
gdb -p <python process id, from 'ps' or similar>
(gdb) py-bt # etc
For more information, see https://wiki.python.org/moin/DebuggingWithGdb
EOF
fi
. "$VIRTUALENV_ROOT/bin/activate"
}
# Renames $1 to $2 quickly, even if $2 already exists.
# (This is most useful if $2 is a directory.) It does this by
# registering an at-exit handler, using trap. (We choose to
# fully delete $2 at exit and not earlier so that the disk
# I/O of deletion doesn't interfere with other tasks we're doing.)
# WARNING: This overwrites other trap handlers, so be careful with it!
DIRS_TO_DELETE=""
trap '[ -z "$DIRS_TO_DELETE" ] || rm -rf $DIRS_TO_DELETE &' 0
fast_mv_f() {
# Where we put the dest directory before we delete it. By default
# it's just <destdir>.to-delete but you can override that with $3.
tmploc=${3-"$2.to-delete"}
# This is almost certainly a noop, but needed if you run fast_mv_f
# twice in succession, or twice in the same script.
rm -rf "$tmploc"
if [ -e "$2" ]; then
mv "$2" "$tmploc"
fi
mv "$1" "$2"
DIRS_TO_DELETE="$DIRS_TO_DELETE $tmploc"
}
# Decrypt secrets.py into a file outside of the Jenkins workspace, we use
# $HOME/secrets_py/ as set up by jenkins/setup.sh in the Khan/aws-config
# project. Then make it importable by setting PYTHONPATH.
# This is idempotent; it's a noop the second and subsequent calls.
decrypt_secrets_py_and_add_to_pythonpath() {
if echo "$PYTHONPATH" | grep -q "$SECRETS_DIR":; then
return # So it's safe to call this twice
fi
# Copy the .cast5, which secrets.py uses to do a freshness check.
cp "$WEBSITE_ROOT"/secrets.py.cast5 "$SECRETS_DIR"
# The decryption command was copied from the make target "secrets_decrypt"
# in the webapp project.
openssl cast5-cbc -d -in "$SECRETS_DIR"/secrets.py.cast5 -out "$SECRETS_DIR"/secrets.py -kfile "$SECRETS_DIR"/secrets.py.cast5.password
chmod 600 "$SECRETS_DIR"/secrets.py
export PYTHONPATH="$SECRETS_DIR":$PYTHONPATH
}
# ubuntu defines its own alert by default, that we don't care about.
if type alert >/dev/null; then unalias alert; fi
# Send an alert to hipchat and the logs. Decrypts secrets if necessary.
# The alertlib subrepo in webapp must be checked out for this to work.
# $1: severity level; $2+: message
alert() {
severity="$1"
shift
decrypt_secrets_py_and_add_to_pythonpath
if echo "$@" | grep -q '<[^ ].*>'; then # a hack, but a pretty good one
html=--html
else
html=
fi
echo "$@" \
| ${WEBSITE_ROOT}/third_party/alertlib-khansrc/alert.py \
--severity="$severity" $html \
--hipchat "$HIPCHAT_ROOM" --hipchat-sender "$HIPCHAT_SENDER" \
--logs
}
## Some Git utilities
# $1: directory to run the pull in (can be in a sub-repo)
# NOTE: this does a git reset, and always changes the branch to master!
safe_pull() {
cd "$1"
git reset --hard
git checkout master
git reset --hard
git pull --rebase origin master || { git rebase --abort; exit 1; }
# Don't try to update submodule if we are ourselves a submodule.
[ -f "`git rev-parse --show-toplevel`/.git" ] || \
git submodule update --init --recursive
cd -
}
# $1: directory to run the push in (can be in a sub-repo)
safe_push() {
cd "$1"
# In case there have been any changes since the script began, we
# do 'pull; push'. On failure, we undo all our work.
git pull --rebase origin master || {
git rebase --abort
git reset --hard HEAD^
exit 1
}
git push origin master || {
git reset --hard HEAD^
exit 1
}
cd -
}
# $1 is the directory holding the subrepo to update (no trailing slash!)
safe_subrepo_update() {
git checkout master
git add "$1"
if git commit --dry-run | grep -q -e 'no changes added' -e 'nothing to commit' -e 'nothing added'; then
echo "No need to update substate for $1: no new content created"
else
git commit -m "$1 substate [auto]"
safe_push .
fi
}
# $1: the directory to commit in (can be in a sub-repo)
# $2+: arguments to 'git commit' (don't need to specify -a)
# NOTE: This 'git add's all new files in the commit-directory.
safe_commit_and_push() {
dir="$1"
shift
cd "$dir"
if [ -z "$(git status --porcelain | head -n 1)" ]; then
echo "No changes, skipping commit"
else
git add .
git commit -a "$@"
fi
cd -
safe_push "$dir"
# If .git is a file and not a directory, we're a subrepo
if [ -f "$dir/.git" ]; then
safe_subrepo_update "$dir"
fi
}