|
| 1 | +#!/usr/bin/env bash |
| 2 | +set -Eeuo pipefail |
| 3 | + |
| 4 | +trap cleanup SIGINT SIGTERM ERR EXIT |
| 5 | + |
| 6 | +usage() { |
| 7 | + cat <<EOF |
| 8 | +USAGE ${0} --github-token=XXXX-XXX --repository-name=dataforgoodfr/python_template [--branch=main] [--deploy-script=.d4g-tools/deploy/pull/pull.sh] [--lock-location=/tmp/lock] [-v] [-h] |
| 9 | +
|
| 10 | +This script will check a repository's status. |
| 11 | +If a newer commit exists on the given branch, it will start the deployment script. |
| 12 | +This script will report a status on the Github commit, and prevent multiple concurrent deployments. |
| 13 | +The only assumption this script makes is that the deploy script will update the local git repository |
| 14 | +to the branch HEAD commit. |
| 15 | +
|
| 16 | +WARNING : This script does NOT handle traffic stopping, airgap deployments, or any other strategy. |
| 17 | +That part is on you to implement in the deploy script. |
| 18 | +
|
| 19 | +Supported parameters : |
| 20 | +-h, --help : display this message |
| 21 | +-v, --verbose : enable enhanced logging |
| 22 | +--github-token : Github token to use for API calls. [Required] |
| 23 | +--repository-name: : Github repository name. [Required] |
| 24 | +--branch : Branch to check for new commits. [Default : main] |
| 25 | +--deploy-script : Script to run when a new commit is found. [Default : bin/deploy] |
| 26 | +--lock-location : Location of the lock file. [Default : /tmp/deploy_\${repository_name}.lock] |
| 27 | +EOF |
| 28 | + exit 1 |
| 29 | +} |
| 30 | + |
| 31 | +cleanup() { |
| 32 | + trap - SIGINT SIGTERM ERR EXIT |
| 33 | + # script cleanup here |
| 34 | + exit 0 |
| 35 | +} |
| 36 | + |
| 37 | +report_github_status() { |
| 38 | + COMMIT_SHA=$1 |
| 39 | + STATUS=$2 |
| 40 | + DESCRIPTION=$3 |
| 41 | + debug "COMMIT_SHA: $COMMIT_SHA" |
| 42 | + debug "STATUS: $STATUS" |
| 43 | + debug "DESCRIPTION: $DESCRIPTION" |
| 44 | + curl -s -H "Authorization: token ${GITHUB_TOKEN}" -X POST \ |
| 45 | + -d "{\"state\": \"${STATUS}\", \"description\": \"$DESCRIPTION\", \"context\": \"deploy\"}" \ |
| 46 | + "https://api.github.com/repos/${REPOSITORY_NAME}/statuses/${COMMIT_SHA}" >/dev/null |
| 47 | +} |
| 48 | + |
| 49 | +deploy_lock() { |
| 50 | + ACTION=$1 |
| 51 | + SHA=$2 |
| 52 | + debug "ACTION: $ACTION" |
| 53 | + debug "SHA: $SHA" |
| 54 | + if [ "${ACTION}" == "acquire" ]; then |
| 55 | + if [ -f "$LOCK_LOCATION" ]; then |
| 56 | + error "Deploy lock already exists at $LOCK_LOCATION. Exiting." |
| 57 | + error $(cat "$LOCK_LOCATION") |
| 58 | + exit 1 |
| 59 | + else |
| 60 | + # Create lock with timestamp and sha as content |
| 61 | + echo "$(date +"%Y-%m-%dT%H:%M:%S%:z") - $SHA" >"$LOCK_LOCATION" |
| 62 | + fi |
| 63 | + elif [ "${ACTION}" == "release" ]; then |
| 64 | + if [ -f "$LOCK_LOCATION" ]; then |
| 65 | + rm "$LOCK_LOCATION" |
| 66 | + else |
| 67 | + error "Deploy lock does not exist. Exiting." |
| 68 | + exit 1 |
| 69 | + fi |
| 70 | + else |
| 71 | + error "Invalid action. Exiting." |
| 72 | + exit 1 |
| 73 | + fi |
| 74 | +} |
| 75 | + |
| 76 | +info() { |
| 77 | + gum style --foreground=4 "$(date +"%Y-%m-%dT%H:%M:%S%:z") $@" |
| 78 | +} |
| 79 | + |
| 80 | +warning() { |
| 81 | + gum style --foreground=3 "$(date +"%Y-%m-%dT%H:%M:%S%:z") $@" |
| 82 | +} |
| 83 | + |
| 84 | +success() { |
| 85 | + gum style --bold --foreground=2 "$(date +"%Y-%m-%dT%H:%M:%S%:z") $@" |
| 86 | +} |
| 87 | + |
| 88 | +error() { |
| 89 | + gum style --bold --foreground=1 "$(date +"%Y-%m-%dT%H:%M:%S%:z") $@" |
| 90 | +} |
| 91 | + |
| 92 | +debug() { |
| 93 | + if [ "$DEBUG" == 'true' ]; then |
| 94 | + gum style --faint "$(date +"%Y-%m-%dT%H:%M:%S%:z") $@" |
| 95 | + fi |
| 96 | +} |
| 97 | + |
| 98 | +parse_params() { |
| 99 | + # Sane defaults |
| 100 | + DEBUG="false" |
| 101 | + RUN_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P) |
| 102 | + SOURCE=$(dirname "$RUN_DIR") |
| 103 | + GITHUB_TOKEN="" |
| 104 | + REPOSITORY_NAME="" |
| 105 | + BRANCH="main" |
| 106 | + DEPLOY_SCRIPT="${SOURCE}/.d4g-tools/deploy/pull/pull.sh" |
| 107 | + |
| 108 | + while :; do |
| 109 | + case "${1-}" in |
| 110 | + -h | --help) |
| 111 | + usage |
| 112 | + ;; |
| 113 | + -v | --verbose) |
| 114 | + DEBUG="true" |
| 115 | + ;; |
| 116 | + --github-token=*) |
| 117 | + GITHUB_TOKEN="${1#*=}" |
| 118 | + ;; |
| 119 | + --repository-name=*) |
| 120 | + REPOSITORY_NAME="${1#*=}" |
| 121 | + REPO_CANONICAL_NAME=$(echo $REPOSITORY_NAME | tr '/' '_') |
| 122 | + LOCK_LOCATION="/tmp/deploy_${REPO_CANONICAL_NAME}.lock" |
| 123 | + ;; |
| 124 | + --branch=*) |
| 125 | + BRANCH="${1#*=}" |
| 126 | + ;; |
| 127 | + --deploy-script=*) |
| 128 | + DEPLOY_SCRIPT="${1#*=}" |
| 129 | + ;; |
| 130 | + --lock-location=*) |
| 131 | + LOCK_LOCATION="${1#*=}" |
| 132 | + ;; |
| 133 | + -?*) |
| 134 | + echo "Unknown option: $1" |
| 135 | + usage |
| 136 | + ;; |
| 137 | + *) |
| 138 | + break |
| 139 | + ;; |
| 140 | + esac |
| 141 | + shift |
| 142 | + done |
| 143 | + |
| 144 | + debug "DEBUG: $DEBUG" |
| 145 | + debug "RUN_DIR: $RUN_DIR" |
| 146 | + debug "GITHUB_TOKEN: $GITHUB_TOKEN" |
| 147 | + debug "REPOSITORY_NAME: $REPOSITORY_NAME" |
| 148 | + debug "BRANCH: $BRANCH" |
| 149 | + debug "DEPLOY_SCRIPT: $DEPLOY_SCRIPT" |
| 150 | + debug "LOCK_LOCATION: $LOCK_LOCATION" |
| 151 | + |
| 152 | + if [ -z "${GITHUB_TOKEN}" ]; then |
| 153 | + error "Github token is required." |
| 154 | + usage |
| 155 | + fi |
| 156 | + if [ -z "${REPOSITORY_NAME}" ]; then |
| 157 | + error "Repository name is required." |
| 158 | + usage |
| 159 | + fi |
| 160 | + |
| 161 | + return 0 |
| 162 | +} |
| 163 | + |
| 164 | +parse_params "$@" |
| 165 | + |
| 166 | +# Check if a new commit exists |
| 167 | +HEAD_COMMIT=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" "https://api.github.com/repos/${REPOSITORY_NAME}/commits/${BRANCH}" | jq -r '.sha') |
| 168 | +LOCAL_COMMIT=$(cd $SOURCE && git rev-parse HEAD) |
| 169 | + |
| 170 | +debug "HEAD_COMMIT: $HEAD_COMMIT" |
| 171 | +debug "LOCAL_COMMIT: $LOCAL_COMMIT" |
| 172 | + |
| 173 | +if [ "${HEAD_COMMIT}" != "${LOCAL_COMMIT}" ]; then |
| 174 | + info "New commit found. Running deploy script." |
| 175 | + # Acquire deploy lock |
| 176 | + deploy_lock acquire $HEAD_COMMIT |
| 177 | + report_github_status $HEAD_COMMIT "pending" "Deployment in progress." |
| 178 | + # Measure time taken to deploy |
| 179 | + START_TIME=`date +%s` |
| 180 | + ${DEPLOY_SCRIPT} |
| 181 | + END_TIME=`date +%s` |
| 182 | + DEPLOY_TIME=$((END_TIME-START_TIME)) |
| 183 | + # get deploy script exit code |
| 184 | + DEPLOY_EXIT_CODE=$? |
| 185 | + if [ $DEPLOY_EXIT_CODE -eq 0 ]; then |
| 186 | + report_github_status $HEAD_COMMIT "success" "Deployment successful." |
| 187 | + deploy_lock release $HEAD_COMMIT |
| 188 | + success "Deployment successful, took ${DEPLOY_TIME}s 🚀." |
| 189 | + else |
| 190 | + report_github_status $HEAD_COMMIT "failure" |
| 191 | + deploy_lock release $HEAD_COMMIT "Deployment failed." |
| 192 | + error "An error occurred while deploying. Exiting." |
| 193 | + exit 1 |
| 194 | + fi |
| 195 | +else |
| 196 | + success "No new commit found. Exiting." |
| 197 | +fi |
| 198 | + |
| 199 | +cleanup |
0 commit comments