-
Notifications
You must be signed in to change notification settings - Fork 241
/
install.bash
executable file
·422 lines (369 loc) · 17.2 KB
/
install.bash
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
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
#!/bin/bash
set -euo pipefail
shopt -s extglob
DEBUG=${DEBUG:-''}
FORCE_DOWNLOAD=${FORCE_DOWNLOAD:-''}
SEARCH_ROOT=${SEARCH_ROOT:-''}
# default is a pre-commit hook; if "pre-push" is the first arg to the script, then it sets up as pre-push
declare HOOK_SCRIPT='pre-commit'
if [[ $# -gt 0 && $1 =~ pre-push.* ]]; then
HOOK_SCRIPT='pre-push'
fi
function run() {
# Arguments: $1 = 'pre-commit' or 'pre-push'. whether to set talisman up as pre-commit or pre-push hook
# Environment variables:
# DEBUG="any-non-emply-value": verbose output for debugging the script
# FORCE_DOWNLOAD="non-empty" : download talisman binary & hook script even if already installed. useful as an upgrade option.
# VERSION="version-number" : download a specific version of talisman
# INSTALL_ORG_REPO="..." : the github org/repo to install from (default thoughtworks/talisman)
# Download appropriate (appropriate = based on OS and ARCH) talisman binary from github
# Get other related install scripts from github
# Copy the talisman binary and the talisman_hook_script to $TALISMAN_SETUP_DIR ($HOME/.talisman/bin)
# Setup a hook script at <git-template-dir>/hooks/pre-<commit/push>.
# When a new repo is created via either git clone or init, this hook script is copied across.
# This is symlinked to the talisman_hook_script in $TALISMAN_SETUP_DIR, for ease of upgrade.
# For each git repo found in the search root (default $HOME), setup a hook script at .git/hooks/pre-<commit/push>
# This is symlinked to the talisman_hook_script in $TALISMAN_SETUP_DIR, for ease of upgrade.
declare TALISMAN_BINARY_NAME
E_CHECKSUM_MISMATCH=2
E_UNSUPPORTED_ARCH=5
IFS=$'\n'
VERSION=${VERSION:-'latest'}
INSTALL_ORG_REPO=${INSTALL_ORG_REPO:-'thoughtworks/talisman'}
DEFAULT_GLOBAL_TEMPLATE_DIR="$HOME/.git-template" # create git-template dir here if not already setup
TALISMAN_SETUP_DIR=${HOME}/.talisman/bin # location of central install: talisman binary and hook script
TALISMAN_HOOK_SCRIPT_PATH=${TALISMAN_SETUP_DIR}/talisman_hook_script
SCRIPT_ORG_REPO=${SCRIPT_ORG_REPO:-$INSTALL_ORG_REPO}
SCRIPT_BASE="https://raw.githubusercontent.com/${SCRIPT_ORG_REPO}/master/global_install_scripts"
TEMP_DIR=$(mktemp -d 2>/dev/null || mktemp -d -t 'talisman_setup')
trap "rm -r ${TEMP_DIR}" EXIT
chmod 0700 ${TEMP_DIR}
REPO_HOOK_SETUP_SCRIPT_PATH="${TEMP_DIR}/setup_talisman_hook_in_repo.bash" # script for setting up git hooks on one repo
function echo_error() {
echo -ne $(tput setaf 1) >&2
echo "$1" >&2
echo -ne $(tput sgr0) >&2
}
export -f echo_error
function echo_debug() {
[[ -z "${DEBUG}" ]] && return
echo -ne $(tput setaf 3) >&2
echo "$1" >&2
echo -ne $(tput sgr0) >&2
}
export -f echo_debug
function echo_success() {
echo -ne $(tput setaf 2)
echo "$1" >&2
echo -ne $(tput sgr0)
}
export -f echo_success
function operating_system() {
OS=$(uname -s)
case $OS in
"Linux")
echo "linux"
;;
"Darwin")
echo "darwin"
;;
MINGW32_NT-*)
echo "windows"
;;
MINGW64_NT-*)
echo "windows"
;;
MSYS_NT-*)
echo "windows"
;;
*)
echo_error "Talisman currently only supports Windows, Linux and MacOS(darwin) systems."
echo_error "If this is a problem for you, please open an issue: https://github.com/${INSTALL_ORG_REPO}/issues/new"
exit $E_UNSUPPORTED_ARCH
;;
esac
}
function set_talisman_binary_name() {
# based on OS (linux/darwin) and ARCH(32/64 bit)
declare SUFFIX
SUFFIX=$(operating_system)
ARCH=$(uname -m)
case $ARCH in
"x86_64")
SUFFIX="${SUFFIX}_amd64"
;;
"i686" | "i386")
SUFFIX="${SUFFIX}_386"
;;
"arm64")
SUFFIX="${SUFFIX}_arm64"
;;
*)
echo_error "Talisman currently only supports x86 and x86_64 and arm64 architectures."
echo_error "If this is a problem for you, please open an issue: https://github.com/${INSTALL_ORG_REPO}/issues/new"
exit $E_UNSUPPORTED_ARCH
;;
esac
TALISMAN_BINARY_NAME="talisman_${SUFFIX}"
if [[ $SUFFIX == *"windows"* ]]; then
TALISMAN_BINARY_NAME="${TALISMAN_BINARY_NAME}.exe"
fi
}
function download() {
OBJECT=$1
DOWNLOAD_URL=$(curl -Ls https://api.github.com/repos/"$INSTALL_ORG_REPO"/releases/latest |
grep download_url | awk '{print $2}' | tr -d '"' | grep "$OBJECT")
echo_debug "Downloading ${OBJECT} from ${DOWNLOAD_URL}"
curl --location --silent ${DOWNLOAD_URL} >${TEMP_DIR}/${OBJECT}
}
function verify_checksum() {
FILE_NAME=$1
CHECKSUM_FILE_NAME='checksums'
echo_debug "Verifying checksum for ${FILE_NAME}"
download ${CHECKSUM_FILE_NAME}
pushd ${TEMP_DIR} >/dev/null 2>&1
grep ${TALISMAN_BINARY_NAME} ${CHECKSUM_FILE_NAME} >${CHECKSUM_FILE_NAME}.single
if ! command -v shasum &> /dev/null; then
sha256sum -c ${CHECKSUM_FILE_NAME}.single
else
shasum -a 256 -c ${CHECKSUM_FILE_NAME}.single
fi
popd >/dev/null 2>&1
echo_debug "Checksum verification successfull!"
echo
}
function download_talisman_binary() {
download ${TALISMAN_BINARY_NAME}
verify_checksum ${TALISMAN_BINARY_NAME}
}
function get_dependent_scripts() {
echo_debug "Downloading dependent scripts"
curl --silent "${SCRIPT_BASE}/talisman_hook_script.bash" >${TEMP_DIR}/talisman_hook_script.bash
curl --silent "${SCRIPT_BASE}/setup_talisman_hook_in_repo.bash" >${REPO_HOOK_SETUP_SCRIPT_PATH}
chmod +x ${REPO_HOOK_SETUP_SCRIPT_PATH}
echo_debug "Contents of temp_dir: $(ls ${TEMP_DIR})"
}
function setup_talisman() {
# setup talisman & hook script in a central location for ease of download ($TALISMAN_SETUP_DIR)
mkdir -p ${TALISMAN_SETUP_DIR}
cp ${TEMP_DIR}/${TALISMAN_BINARY_NAME} ${TALISMAN_SETUP_DIR}
chmod +x ${TALISMAN_SETUP_DIR}/${TALISMAN_BINARY_NAME}
sed -e "s@\${TALISMAN_BINARY}@"${TALISMAN_SETUP_DIR}/${TALISMAN_BINARY_NAME}"@" ${TEMP_DIR}/talisman_hook_script.bash >${TALISMAN_HOOK_SCRIPT_PATH}
chmod +x ${TALISMAN_HOOK_SCRIPT_PATH}
add_talisman_home_as $TALISMAN_SETUP_DIR
sudo ln -s "${TALISMAN_SETUP_DIR}/${TALISMAN_BINARY_NAME}" /usr/local/bin/talisman
}
function setup_git_template_talisman_hook() {
# check for existing git-template dir, if not create it in $HOME/.git-template
# if git-template dir already contains the pre-<commit/push> script that we want to use for talisman,
# don't setup talisman, warn the user and suggest using a hook chaining mechanism like pre-commit (from pre-commit.com)
# Setup a symlink from <.git-template dir>/hooks/pre-<commit/push> to the central talisman hook script
TEMPLATE_DIR=$(git config --global init.templatedir) || true # find the template_dir if it exists
if [[ "$TEMPLATE_DIR" == "" ]]; then # if no template dir, create one
echo "No git template directory is configured. Let's add one."
echo "(this will override any system git templates and modify your git config file)"
echo
read -e -u1 -p "Git template directory: ($DEFAULT_GLOBAL_TEMPLATE_DIR) " TEMPLATE_DIR
echo
TEMPLATE_DIR=${TEMPLATE_DIR:-$DEFAULT_GLOBAL_TEMPLATE_DIR}
git config --global init.templatedir ${TEMPLATE_DIR}
else
echo "Using existing git template dir: $TEMPLATE_DIR."
echo
fi
# Support '~' in path
TEMPLATE_DIR=${TEMPLATE_DIR/#\~/$HOME}
if [ -e "${TEMPLATE_DIR}/hooks/${HOOK_SCRIPT}" ]; then
# does this handle the case of upgrade - already have the hook installed, but is the old version?
if [ "${TALISMAN_HOOK_SCRIPT_PATH}" -ef "${TEMPLATE_DIR}/hooks/${HOOK_SCRIPT}" ]; then
echo_success "Talisman template hook already installed."
else
echo_error "It looks like you already have a ${HOOK_SCRIPT} hook"
echo_error "installed at '${TEMPLATE_DIR}/hooks/${HOOK_SCRIPT}'."
echo_error "If this is a expected, you should consider setting-up a tool"
echo_error "like pre-commit (brew install pre-commit)"
echo_error "WARNING! Global talisman hook not installed into git template."
echo_error "Newly (git-init/git-clone)-ed repositories will not be covered by talisman."
fi
else
mkdir -p "$TEMPLATE_DIR/hooks"
echo "Setting up template ${HOOK_SCRIPT} hook"
OS=$(operating_system)
case $OS in
"windows")
TEMPLATE_DIR_WIN=$(sed -e 's/\/\([a-z]\)\//\1:\\/' -e 's/\//\\/g' <<<"$TEMPLATE_DIR")
TALISMAN_HOOK_SCRIPT_PATH_WIN=$(sed -e 's/\/\([a-z]\)\//\1:\\/' -e 's/\//\\/g' <<<"$TALISMAN_HOOK_SCRIPT_PATH")
cmd <<<"mklink "$TEMPLATE_DIR_WIN\\hooks\\$HOOK_SCRIPT" "$TALISMAN_HOOK_SCRIPT_PATH_WIN"" >/dev/null
;;
*)
ln -svf ${TALISMAN_HOOK_SCRIPT_PATH} ${TEMPLATE_DIR}/hooks/${HOOK_SCRIPT}
;;
esac
echo_success "Talisman template hook successfully installed."
fi
}
function set_talisman_env_variables_properly() {
FILE_PATH="$1"
if [ -f $FILE_PATH ] && grep -q "TALISMAN_HOME" $FILE_PATH; then
if ! grep -q ">>> talisman >>>" $FILE_PATH; then
sed -i'-talisman.bak' '/TALISMAN_HOME/d' $FILE_PATH
echo -e "\n" >>${ENV_FILE}
echo "# >>> talisman >>>" >>$FILE_PATH
echo "# Below environment variables should not be modified unless you know what you are doing" >>$FILE_PATH
echo "export TALISMAN_HOME=${TALISMAN_SETUP_DIR}" >>$FILE_PATH
echo "alias talisman=\$TALISMAN_HOME/${TALISMAN_BINARY_NAME}" >>$FILE_PATH
echo "# <<< talisman <<<" >>$FILE_PATH
fi
fi
}
function add_talisman_home_as() {
# set TALISMAN_HOME path for user if user opts for it
# user has option to set TALISMAN_HOME in .bashrc or .profile
# user can opt out of auto-setup of TALISMAN_HOME to set it up later manually
TALISMAN_SETUP_DIR="$1"
echo "Setting up TALISMAN_HOME in path"
if [ -n "${TALISMAN_HOME:-}" ]; then
echo -e "TALISMAN_HOME is already set\n"
set_talisman_env_variables_properly ~/.bashrc
set_talisman_env_variables_properly ~/.bash_profile
set_talisman_env_variables_properly ~/.profile
return 0
fi
BASHRC_OPT="Set TALISMAN_HOME in ~/.bashrc"
BASHPROFILE_OPT="Set TALISMAN_HOME in ~/.bash_profile"
PROFILE_OPT="Set TALISMAN_HOME in ~/.profile"
SELFSETUP_OPT="I will set it later"
TALISMAN_HOME=""
echo -e "\n\nPLEASE CHOOSE WHERE YOU WISH TO SET TALISMAN_HOME VARIABLE AND talisman binary PATH (Enter option number): "
options=(${BASHRC_OPT} ${BASHPROFILE_OPT} ${PROFILE_OPT} ${SELFSETUP_OPT})
select opt in "${options[@]}"; do
case $opt in
${BASHRC_OPT})
set_talisman_home_and_binary_path ~/.bashrc
break
;;
${BASHPROFILE_OPT})
set_talisman_home_and_binary_path ~/.bash_profile
break
;;
${PROFILE_OPT})
set_talisman_home_and_binary_path ~/.profile
break
;;
${SELFSETUP_OPT})
echo "You chose to set TALISMAN_HOME and binary path by yourself. Remember to set TALISMAN_HOME=${TALISMAN_SETUP_DIR} and alias talisman =${TALISMAN_SETUP_DIR}/${TALISMAN_BINARY_NAME}\n\n"
break
;;
*) echo "invalid option $REPLY" ;;
esac
done
}
function set_talisman_interactive_mode() {
# set interactive mode for talisman if user opts in for it
# default to non-interactive mode
TALISMAN_INTERACTIVE_OPTION="false"
OS=$(operating_system)
if [[ $OS != "windows" ]]; then
echo -e "\n\nSetting up interaction mode"
read -rep $'\nDO YOU WANT TO BE PROMPTED WHEN ADDING EXCEPTIONS TO .talismanrc FILE? \nEnter y to install in interactive mode. Press any key to continue without interactive mode (y/n):' INTERACTIVE_OPT
if [[ $INTERACTIVE_OPT =~ ^[Yy]$ ]]; then
TALISMAN_INTERACTIVE_OPTION="true"
fi
fi
}
function set_talisman_home_and_binary_path() {
ENV_FILE="$1"
set_talisman_interactive_mode
echo -e "Setting up TALISMAN_HOME in ${ENV_FILE}"
echo -e "\n" >>${ENV_FILE}
echo "# >>> talisman >>>" >>${ENV_FILE}
echo "# Below environment variables should not be modified unless you know what you are doing" >>${ENV_FILE}
echo "export TALISMAN_HOME=${TALISMAN_SETUP_DIR}" >>${ENV_FILE}
echo "alias talisman=\$TALISMAN_HOME/${TALISMAN_BINARY_NAME}" >>${ENV_FILE}
echo "export TALISMAN_INTERACTIVE=${TALISMAN_INTERACTIVE_OPTION}" >>${ENV_FILE}
echo "# <<< talisman <<<" >>${ENV_FILE}
printf '\e[1;34m%-6s\e[m' "After the installation is complete, you will need to manually restart the terminal or source ${ENV_FILE} file"
echo
read -n 1 -s -r -p "Press any key to continue ..."
echo
}
function setup_git_talisman_hooks_at() {
# find all .git repos from a specified $SEARCH_ROOT and setup .git/hooks/pre-<commit/push> in each of those
# Symlink .git/hooks/pre-<commit/push> to the central talisman hook script
# use find -name .git -exec REPO_HOOK_SETUP_SCRIPT to find all git repos and setup the hook
# If the $SEARCH_ROOT is root, then use sudo and ask for the user's password to execute
# This will not clobber any pre-existing hooks, instead suggesting a hook chaining tool like pre-commit (pre-commit.com)
# The REPO_HOOK_SETUP_SCRIPT takes care of pre-commit vs pre-push & not clobbering any hooks which are already setup
# If the REPO_HOOK_SETUP_SCRIPT finds a pre-existing hook, it will write these to the EXCEPTIONS_FILE
# Look into the REPO_HOOK_SETUP_SCRIPT for more detailed info on that script
SEARCH_ROOT="$1"
SEARCH_CMD="find"
EXTRA_SEARCH_OPTS=""
echo -e "\tSearching ${SEARCH_ROOT} for git repositories"
SUDO_PREFIX=""
if [[ "${SEARCH_ROOT}" == "/" ]]; then
echo -e "\tPlease enter your password when prompted to enable script to search as root user:"
SUDO_PREFIX="sudo"
EXTRA_SEARCH_OPTS="-xdev \( -path '/private/var' -prune \) -o"
fi
EXCEPTIONS_FILE=${TEMP_DIR}/pre-existing-hooks.paths
touch ${EXCEPTIONS_FILE}
CMD_STRING="${SUDO_PREFIX} ${SEARCH_CMD} ${SEARCH_ROOT} ${EXTRA_SEARCH_OPTS} -name .git -type d -exec ${REPO_HOOK_SETUP_SCRIPT_PATH} ${TALISMAN_HOOK_SCRIPT_PATH} ${EXCEPTIONS_FILE} {} ${HOOK_SCRIPT} \;"
echo_debug "EXECUTING: ${CMD_STRING}"
eval "${CMD_STRING}" || true
NUMBER_OF_EXCEPTION_REPOS=$(cat ${EXCEPTIONS_FILE} | wc -l)
OS=$(operating_system)
if [ ${NUMBER_OF_EXCEPTION_REPOS} -gt 0 ]; then
if [[ $OS == "windows" ]]; then
EXCEPTIONS_FILE_HOME_PATH="${HOME}/talisman_missed_repositories.paths"
mv ${EXCEPTIONS_FILE} ${EXCEPTIONS_FILE_HOME_PATH}
echo_error ""
echo_error "Please see ${EXCEPTIONS_FILE_HOME_PATH} for a list of repositories"
echo_error "that couldn't automatically be hooked up with talisman as ${HOOK_SCRIPT}"
echo_error "If you are already using husky for git hooks (https://github.com/typicode/husky)"
echo_error "Add the following script to husky pre-commit in your package.json for the repositories listed ${EXCEPTIONS_FILE_HOME_PATH}"
echo "\"bash -c '\"%TALISMAN_HOME%\\${TALISMAN_BINARY_NAME}\" --githook pre-commit'\""
else
EXCEPTIONS_FILE_HOME_PATH="${HOME}/talisman_missed_repositories.paths"
mv ${EXCEPTIONS_FILE} ${EXCEPTIONS_FILE_HOME_PATH}
echo_error ""
echo_error "Please see ${EXCEPTIONS_FILE_HOME_PATH} for a list of repositories"
echo_error "that couldn't automatically be hooked up with talisman as ${HOOK_SCRIPT}"
echo_error "You should consider installing a tool like pre-commit (https://pre-commit.com) in those repositories"
echo_error "Add the following repo definition into .pre-commit-config.yaml after installing pre-commit in each such repository"
tee $HOME/.talisman-precommit-config <<END_OF_SCRIPT
- repo: local
hooks:
- id: talisman-precommit
name: talisman
entry: bash -c 'if [ -n "\${TALISMAN_HOME:-}" ]; then \${TALISMAN_HOME}/talisman_hook_script pre-commit; else echo "TALISMAN does not exist. Consider installing from https://github.com/thoughtworks/talisman . If you already have talisman installed, please ensure TALISMAN_HOME variable is set to where talisman_hook_script resides, for example, TALISMAN_HOME=\${HOME}/.talisman/bin"; fi'
language: system
pass_filenames: false
types: [text]
verbose: true
END_OF_SCRIPT
fi
fi
}
set_talisman_binary_name
get_dependent_scripts
# currently doesn't check if the talisman binary and the talisman hook script are upto date
# would be good to create a separate script which does the upgrade and the initial install
if [[ ! -x ${TALISMAN_SETUP_DIR}/${TALISMAN_BINARY_NAME} || ! -x ${TALISMAN_HOOK_SCRIPT_PATH} || -n ${FORCE_DOWNLOAD} ]]; then
echo "Downloading talisman binary"
download_talisman_binary
echo
echo "Setting up talisman binary and helper script in $HOME/.talisman"
setup_talisman
fi
echo "Setting up ${HOOK_SCRIPT} hook in git template directory"
setup_git_template_talisman_hook
echo
echo "Setting up talisman hook recursively in git repos"
if [[ "$SEARCH_ROOT" == "" ]]; then
read -e -p "Please enter root directory to search for git repos (Default: ${HOME}): " SEARCH_ROOT
fi
SEARCH_ROOT=${SEARCH_ROOT:-$HOME}
setup_git_talisman_hooks_at $SEARCH_ROOT
}
run $0 $@