|
| 1 | +#!/bin/bash |
| 2 | +set -euo pipefail |
| 3 | +shopt -s extglob |
| 4 | + |
| 5 | +DEBUG=${DEBUG:-''} |
| 6 | + |
| 7 | +# default is a pre-commit hook; if "pre-push" is the first arg to the script, then it sets up as pre-push |
| 8 | +declare HOOK_SCRIPT='pre-commit' |
| 9 | +if [[ $# -gt 0 && $1 =~ pre-push.* ]] ; then |
| 10 | + HOOK_SCRIPT='pre-push' |
| 11 | +fi |
| 12 | + |
| 13 | +function run() { |
| 14 | + # Arguments: $1 = 'pre-commit' or 'pre-push'. whether to set talisman up as pre-commit or pre-push hook |
| 15 | + # Environment variables: |
| 16 | + # DEBUG="any-non-emply-value": verbose output for debugging the script |
| 17 | + # FORCE_DOWNLOAD="non-empty" : download talisman binary & hook script even if already installed. useful as an upgrade option. |
| 18 | + # VERSION="version-number" : download a specific version of talisman |
| 19 | + # INSTALL_ORG_REPO="..." : the github org/repo to install from (default thoughtworks/talisman) |
| 20 | + |
| 21 | + # Get other related install scripts from github |
| 22 | + # Copy the talisman_hook_script to $TALISMAN_SETUP_DIR (/usr/local/talisman/bin) |
| 23 | + # Setup a hook script at <git-template-dir>/hooks/pre-<commit/push>. |
| 24 | + # When a new repo is created via either git clone or init, this hook script is copied across. |
| 25 | + # This is symlinked to the talisman_hook_script in $TALISMAN_SETUP_DIR, for ease of upgrade. |
| 26 | + # For each git repo found in the search root (default $HOME), setup a hook script at .git/hooks/pre-<commit/push> |
| 27 | + # This is symlinked to the talisman_hook_script in $TALISMAN_SETUP_DIR, for ease of upgrade. |
| 28 | + |
| 29 | + declare TALISMAN_BINARY_NAME="talisman" |
| 30 | + |
| 31 | + |
| 32 | + IFS=$'\n' |
| 33 | + INSTALL_ORG_REPO=${INSTALL_ORG_REPO:-'thoughtworks/talisman'} |
| 34 | + |
| 35 | + DEFAULT_GLOBAL_TEMPLATE_DIR="$HOME/.git-template" # create git-template dir here if not already setup |
| 36 | + TALISMAN_SETUP_DIR="/usr/local/Cellar/talisman/*/bin" # location of central install: talisman binary and hook script |
| 37 | + TALISMAN_HOOK_SCRIPT_DIR="$HOME/.talisman" |
| 38 | + TALISMAN_HOOK_SCRIPT_PATH="$HOME/.talisman"/talisman_hook_script |
| 39 | + SCRIPT_ORG_REPO=${SCRIPT_ORG_REPO:-$INSTALL_ORG_REPO} |
| 40 | + SCRIPT_BASE="https://raw.githubusercontent.com/${SCRIPT_ORG_REPO}/master/global_install_scripts" |
| 41 | + |
| 42 | + TEMP_DIR=$(mktemp -d 2>/dev/null || mktemp -d -t 'talisman_setup') |
| 43 | + trap "rm -r ${TEMP_DIR}" EXIT |
| 44 | + chmod 0700 ${TEMP_DIR} |
| 45 | + |
| 46 | + REPO_HOOK_SETUP_SCRIPT_PATH="${TEMP_DIR}/setup_talisman_hook_in_repo.bash" # script for setting up git hooks on one repo |
| 47 | + |
| 48 | + function echo_error() { |
| 49 | + echo -ne $(tput setaf 1) >&2 |
| 50 | + echo "$1" >&2 |
| 51 | + echo -ne $(tput sgr0) >&2 |
| 52 | + } |
| 53 | + export -f echo_error |
| 54 | + |
| 55 | + function echo_debug() { |
| 56 | + [[ -z "${DEBUG}" ]] && return |
| 57 | + echo -ne $(tput setaf 3) >&2 |
| 58 | + echo "$1" >&2 |
| 59 | + echo -ne $(tput sgr0) >&2 |
| 60 | + } |
| 61 | + export -f echo_debug |
| 62 | + |
| 63 | + function echo_success { |
| 64 | + echo -ne $(tput setaf 2) |
| 65 | + echo "$1" >&2 |
| 66 | + echo -ne $(tput sgr0) |
| 67 | + } |
| 68 | + export -f echo_success |
| 69 | + |
| 70 | + function get_dependent_scripts() { |
| 71 | + echo "Downloading dependent scripts" |
| 72 | + curl --silent "${SCRIPT_BASE}/talisman_hook_script.bash" > ${TEMP_DIR}/talisman_hook_script.bash |
| 73 | + curl --silent "${SCRIPT_BASE}/setup_talisman_hook_in_repo.bash" > ${REPO_HOOK_SETUP_SCRIPT_PATH} |
| 74 | + chmod +x ${REPO_HOOK_SETUP_SCRIPT_PATH} |
| 75 | + echo "Contents of temp_dir: `ls ${TEMP_DIR}`" |
| 76 | + } |
| 77 | + |
| 78 | + function setup_talisman() { |
| 79 | + # setup talisman & hook script in a central location for ease of download ($TALISMAN_SETUP_DIR) |
| 80 | + mkdir -p ${TALISMAN_HOOK_SCRIPT_DIR} |
| 81 | + sed -e "s@\${TALISMAN_BINARY}@"${TALISMAN_SETUP_DIR}/${TALISMAN_BINARY_NAME}"@" ${TEMP_DIR}/talisman_hook_script.bash > ${TALISMAN_HOOK_SCRIPT_PATH} |
| 82 | + chmod +x ${TALISMAN_HOOK_SCRIPT_PATH} |
| 83 | + add_talisman_home_as $TALISMAN_SETUP_DIR |
| 84 | + } |
| 85 | + |
| 86 | + function setup_git_template_talisman_hook() { |
| 87 | + # check for existing git-template dir, if not create it in $HOME/.git-template |
| 88 | + # if git-template dir already contains the pre-<commit/push> script that we want to use for talisman, |
| 89 | + # don't setup talisman, warn the user and suggest using a hook chaining mechanism like pre-commit (from pre-commit.com) |
| 90 | + # Setup a symlink from <.git-template dir>/hooks/pre-<commit/push> to the central talisman hook script |
| 91 | + |
| 92 | + TEMPLATE_DIR=$(git config --global init.templatedir) || true # find the template_dir if it exists |
| 93 | + |
| 94 | + if [[ "$TEMPLATE_DIR" == "" ]]; then # if no template dir, create one |
| 95 | + echo "No git template directory is configured. Let's add one." |
| 96 | + echo "(this will override any system git templates and modify your git config file)" |
| 97 | + echo |
| 98 | + read -u1 -p "Git template directory: ($DEFAULT_GLOBAL_TEMPLATE_DIR) " TEMPLATE_DIR |
| 99 | + echo |
| 100 | + TEMPLATE_DIR=${TEMPLATE_DIR:-$DEFAULT_GLOBAL_TEMPLATE_DIR} |
| 101 | + git config --global init.templatedir ${TEMPLATE_DIR} |
| 102 | + else |
| 103 | + echo "Using existing git template dir: $TEMPLATE_DIR." |
| 104 | + echo |
| 105 | + fi |
| 106 | + |
| 107 | + # Support '~' in path |
| 108 | + TEMPLATE_DIR=${TEMPLATE_DIR/#\~/$HOME} |
| 109 | + |
| 110 | + if [ -e "${TEMPLATE_DIR}/hooks/${HOOK_SCRIPT}" ]; then |
| 111 | + # does this handle the case of upgrade - already have the hook installed, but is the old version? |
| 112 | + if [ "${TALISMAN_HOOK_SCRIPT_PATH}" -ef "${TEMPLATE_DIR}/hooks/${HOOK_SCRIPT}" ]; then |
| 113 | + echo_success "Talisman template hook already installed." |
| 114 | + else |
| 115 | + echo_error "It looks like you already have a ${HOOK_SCRIPT} hook" |
| 116 | + echo_error "installed at '${TEMPLATE_DIR}/hooks/${HOOK_SCRIPT}'." |
| 117 | + echo_error "If this is a expected, you should consider setting-up a tool" |
| 118 | + echo_error "like pre-commit (brew install pre-commit)" |
| 119 | + echo_error "WARNING! Global talisman hook not installed into git template." |
| 120 | + echo_error "Newly (git-init/git-clone)-ed repositories will not be covered by talisman." |
| 121 | + fi |
| 122 | + else |
| 123 | + mkdir -p "$TEMPLATE_DIR/hooks" |
| 124 | + echo "Setting up template ${HOOK_SCRIPT} hook" |
| 125 | + ln -svf ${TALISMAN_HOOK_SCRIPT_PATH} ${TEMPLATE_DIR}/hooks/${HOOK_SCRIPT} |
| 126 | + echo_success "Talisman template hook successfully installed." |
| 127 | + fi |
| 128 | + } |
| 129 | + |
| 130 | + function add_talisman_home_as() { |
| 131 | + # set TALISMAN_HOME path for user if user opts for it |
| 132 | + # user has option to set TALISMAN_HOME in .bashrc or .profile |
| 133 | + # user can opt out of auto-setup of TALISMAN_HOME to set it up later manually |
| 134 | + TALISMAN_SETUP_DIR="$1" |
| 135 | + echo "Setting up TALISMAN_HOME in path" |
| 136 | + |
| 137 | + if [ -n "${TALISMAN_HOME:-}" ]; then |
| 138 | + echo -e "TALISMAN_HOME is already set\n" |
| 139 | + return 0; |
| 140 | + fi |
| 141 | + |
| 142 | + BASHRC_OPT="Set TALISMAN_HOME in ~/.bashrc" |
| 143 | + BASHPROFILE_OPT="Set TALISMAN_HOME in ~/.bash_profile" |
| 144 | + PROFILE_OPT="Set TALISMAN_HOME in ~/.profile" |
| 145 | + SELFSETUP_OPT="I will set it later" |
| 146 | + TALISMAN_HOME="" |
| 147 | + |
| 148 | + echo -e "\n\nPLEASE CHOOSE WHERE YOU WISH TO SET TALISMAN_HOME VARIABLE (Enter option number): " |
| 149 | + options=(${BASHRC_OPT} ${BASHPROFILE_OPT} ${PROFILE_OPT} ${SELFSETUP_OPT}) |
| 150 | + select opt in "${options[@]}" |
| 151 | + do |
| 152 | + case $opt in |
| 153 | + ${BASHRC_OPT}) |
| 154 | + set_talisman_home_in ~/.bashrc |
| 155 | + break |
| 156 | + ;; |
| 157 | + ${BASHPROFILE_OPT}) |
| 158 | + set_talisman_home_in ~/.bash_profile |
| 159 | + break |
| 160 | + ;; |
| 161 | + ${PROFILE_OPT}) |
| 162 | + set_talisman_home_in ~/.profile |
| 163 | + break |
| 164 | + ;; |
| 165 | + ${SELFSETUP_OPT}) |
| 166 | + echo "You chose to set TALISMAN_HOME by yourself. Remember to set TALISMAN_HOME=${TALISMAN_SETUP_DIR}\n\n" |
| 167 | + break |
| 168 | + ;; |
| 169 | + *) echo "invalid option $REPLY";; |
| 170 | + esac |
| 171 | + done |
| 172 | + } |
| 173 | + |
| 174 | + function set_talisman_home_in() { |
| 175 | + ENV_FILE="$1" |
| 176 | + echo -e "Setting up TALISMAN_HOME in ${ENV_FILE}" |
| 177 | + echo "export TALISMAN_HOME=${TALISMAN_SETUP_DIR}" >> ${ENV_FILE} |
| 178 | + 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" |
| 179 | + echo |
| 180 | + read -n 1 -s -r -p "Press any key to continue ..." |
| 181 | + echo |
| 182 | + } |
| 183 | + |
| 184 | + function setup_git_talisman_hooks_at() { |
| 185 | + # find all .git repos from a specified $SEARCH_ROOT and setup .git/hooks/pre-<commit/push> in each of those |
| 186 | + # Symlink .git/hooks/pre-<commit/push> to the central talisman hook script |
| 187 | + # use find -name .git -exec REPO_HOOK_SETUP_SCRIPT to find all git repos and setup the hook |
| 188 | + # If the $SEARCH_ROOT is root, then use sudo and ask for the user's password to execute |
| 189 | + # This will not clobber any pre-existing hooks, instead suggesting a hook chaining tool like pre-commit (pre-commit.com) |
| 190 | + # The REPO_HOOK_SETUP_SCRIPT takes care of pre-commit vs pre-push & not clobbering any hooks which are already setup |
| 191 | + # If the REPO_HOOK_SETUP_SCRIPT finds a pre-existing hook, it will write these to the EXCEPTIONS_FILE |
| 192 | + # Look into the REPO_HOOK_SETUP_SCRIPT for more detailed info on that script |
| 193 | + |
| 194 | + SEARCH_ROOT="$1" |
| 195 | + SEARCH_CMD="find" |
| 196 | + EXTRA_SEARCH_OPTS="" |
| 197 | + echo -e "\tSearching ${SEARCH_ROOT} for git repositories" |
| 198 | + |
| 199 | + SUDO_PREFIX="" |
| 200 | + if [[ "${SEARCH_ROOT}" == "/" ]]; then |
| 201 | + echo -e "\tPlease enter your password when prompted to enable script to search as root user:" |
| 202 | + SUDO_PREFIX="sudo" |
| 203 | + EXTRA_SEARCH_OPTS="-xdev \( -path '/private/var' -prune \) -o" |
| 204 | + fi |
| 205 | + EXCEPTIONS_FILE=${TEMP_DIR}/pre-existing-hooks.paths |
| 206 | + touch ${EXCEPTIONS_FILE} |
| 207 | + |
| 208 | + 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} \;" |
| 209 | + |
| 210 | + echo_debug "EXECUTING: ${CMD_STRING}" |
| 211 | + eval "${CMD_STRING}" || true |
| 212 | + |
| 213 | + NUMBER_OF_EXCEPTION_REPOS=`cat ${EXCEPTIONS_FILE} | wc -l` |
| 214 | + |
| 215 | + OS=$(uname -s) |
| 216 | + if [ ${NUMBER_OF_EXCEPTION_REPOS} -gt 0 ]; then |
| 217 | + EXCEPTIONS_FILE_HOME_PATH="${HOME}/talisman_missed_repositories.paths" |
| 218 | + mv ${EXCEPTIONS_FILE} ${EXCEPTIONS_FILE_HOME_PATH} |
| 219 | + echo_error "" |
| 220 | + echo_error "Please see ${EXCEPTIONS_FILE_HOME_PATH} for a list of repositories" |
| 221 | + echo_error "that couldn't automatically be hooked up with talisman as ${HOOK_SCRIPT}" |
| 222 | + echo_error "You should consider installing a tool like pre-commit (https://pre-commit.com) in those repositories" |
| 223 | + echo_error "Add the following repo definition into .pre-commit-config.yaml after installing pre-commit in each such repository" |
| 224 | + tee $HOME/.talisman-precommit-config <<END_OF_SCRIPT |
| 225 | +- repo: local |
| 226 | + hooks: |
| 227 | + - id: talisman-precommit |
| 228 | + name: talisman |
| 229 | + 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' |
| 230 | + language: system |
| 231 | + pass_filenames: false |
| 232 | + types: [text] |
| 233 | + verbose: true |
| 234 | +END_OF_SCRIPT |
| 235 | + fi |
| 236 | + } |
| 237 | + |
| 238 | + get_dependent_scripts |
| 239 | + |
| 240 | + # currently doesn't check if the talisman binary and the talisman hook script are upto date |
| 241 | + # would be good to create a separate script which does the upgrade and the initial install |
| 242 | + setup_talisman |
| 243 | + echo "Setting up ${HOOK_SCRIPT} hook in git template directory" |
| 244 | + setup_git_template_talisman_hook |
| 245 | + echo |
| 246 | + echo "Setting up talisman hook recursively in git repos" |
| 247 | + read -p "Please enter root directory to search for git repos (Default: ${HOME}): " SEARCH_ROOT |
| 248 | + SEARCH_ROOT=${SEARCH_ROOT:-$HOME} |
| 249 | + setup_git_talisman_hooks_at $SEARCH_ROOT |
| 250 | + } |
| 251 | + |
| 252 | +run $0 $@ |
0 commit comments