#!/bin/bash set -euf -o pipefail # Usage usage() { cat </dev/null; then echo $$ > "$lockfile/pid" else otherpid="$(cat "$lockfile/pid")" if [ $? != 0 ]; then echo "Lock failed, PID $otherpid is active." exit 2 fi if ! kill -0 "$otherpid" &>/dev/null; then # lock is stale, remove it and restart rm -rf "$lockfile" exec bash "$0" "$@" else # lock is valid echo "Lock failed, PID $otherpid is active." exit 2 fi fi } unlock() { rm -rf "$lockfile" } ssh_cleanup() { /bin/kill "$SSH_AGENT_PID" } # Check if we know the github host key. If not, add it. get_github_host_key () { if [ "$(whoami)" == 'root' ]; then known_hosts=/etc/ssh/ssh_known_hosts elif [ "$(whoami)" == 'www-data' ]; then known_hosts=/var/www/.ssh/known_hosts else known_hosts=$HOME/.ssh/known_hosts fi if ! ssh-keygen -q -F github.com -f "$known_hosts" > /dev/null; then key=$(ssh-keyscan -H github.com 2>/dev/null) echo "$key" >> "$known_hosts" fi } load_rcfile() { if [[ -z ${rcfile+:} ]]; then if [[ -e ./deployrc ]]; then rcfile="./deployrc" source ./deployrc elif [[ -e ./.deployrc ]]; then rcfile="./.deployrc" source ./.deployrc elif [[ -e ~/.deployrc ]]; then rcfile="$HOME/.deployrc" source ~/.deployrc elif [[ -e /etc/deployrc ]]; then rcfile="/etc/deployrc" source /etc/deployrc else echo "Could not find deployrc." exit 1 fi else if [[ -e "$rcfile" ]]; then source "$rcfile" else echo "Could not read $rcfile." exit 1 fi fi lockfile="/tmp/$(basename "$rcfile").lock" # Do we have a git url to work from? if [[ -z ${url+:} ]]; then echo "No git url specified." exit 1 fi if [[ -z ${gitref+:} ]]; then gitref='master'; fi # Check to see if variables are set. if [[ -z ${rootpath+:} ]]; then echo "No rootpath set in deployrc." exit 1 fi if [[ -z ${currentpath+:} ]]; then currentpath="$rootpath/current"; fi if [[ -z ${previouspath+:} ]]; then previouspath="$rootpath/previous"; fi if [[ -z ${sharedpath+:} ]]; then sharedpath="$rootpath/shared"; fi if [[ -z ${deploypath+:} ]]; then deploypath="$rootpath/deploys"; fi if [[ -z ${deployname+:} ]]; then deployname="$(date +%Y%m%dT%H%M%S)"; fi if [[ -z ${targetpath+:} ]]; then targetpath="$deploypath/$deployname"; fi if [[ -z ${chownneeded+:} ]]; then chownneeded=false else if [[ -z ${chuser+:} ]]; then echo "chownneeded is true, but no chuser is specified." exit 1 fi fi if [[ -z ${keepcopies+:} ]]; then keepcopies=3; fi if [[ -z ${runcomposer+:} ]]; then runcomposer=true; fi if [[ -z ${composeropts+:} ]]; then composeropts=""; fi if [[ -z ${runnpm+:} ]]; then runnpm=true; fi if [[ -z ${npmopts+:} ]]; then npmopts=""; fi if [[ -n ${sshkeypath+:} && -r ${sshkeypath} ]]; then eval "$(ssh-agent -s)" >/dev/null 2>&1 set +f ssh-add "$sshkeypath" >/dev/null 2>&1 set -f trap ssh_cleanup EXIT fi } print_rcfile() { cat </dev/null 2>&1 fi } # Update master symlink. link_current() { if [[ -h "$currentpath" ]]; then previoustarget="$(readlink "$currentpath")" $runner ln -snf "$previoustarget" "$previouspath" if $chownneeded; then $runner chown -h "$chuser" "$currentpath" fi else if [[ -e "$currentpath" ]]; then echo "Cowardly refusing to overwrite non-symlink $currentpath with a symlink to $targetpath" exit 1 fi fi if [[ ! -d "$targetpath" ]]; then echo "Cowardly refusing to symlink to a missing directory: $targetpath" exit 1 fi $runner ln -snf "$targetpath" "$currentpath" if $chownneeded; then $runner chown -h "$chuser" "$currentpath" fi } link_rollback() { if [[ -h "$previouspath" ]]; then previoustarget="$(readlink "$previouspath")" else echo "Could not determine rollback target."; exit 1 fi if [[ ! -d "$previoustarget" ]]; then echo "Cowardly refusing to symlink to a missing directory: $previoustarget" exit 1 fi if [[ -h "$currentpath" ]]; then currenttarget="$(readlink "$currentpath")" if [[ "$previoustarget" == "$currenttarget" ]]; then echo "No older revision; can't roll back." exit 1 fi else if [[ -e "$currentpath" ]]; then echo "Cowardly refusing to overwrite non-symlink $currentpath with a symlink to $previoustarget" exit 1 fi fi $runner ln -snf "$previoustarget" "$currentpath" if $chownneeded; then $runner chown -h "$chuser" "$currentpath" fi } run_if_function() { if [[ $(type -t "$1") == "function" ]]; then $1 fi } # Strategies for execution dryrun() { echo "$@" } runhere() { dryrun "$@" if [[ -d "$targetpath" ]]; then cd "$targetpath" && ("$@") else ("$@") fi } # Process options. action='deploy' runner='runhere' wantedref=''; while getopts "c:dt:hkrsxl" opt; do case "$opt" in c) rcfile="$OPTARG" ;; d) runner='dryrun' ;; t) wantedref="$OPTARG" ;; h) action='help' ;; k) action='clean' ;; r) action='rollback' ;; s) action='showpaths' ;; x) action='printrcfile' ;; l) action='shellcheck' ;; *) usage exit 1 ;; \?) usage exit 1 ;; esac done # Choose an action. case "$action" in 'deploy') load_rcfile if [[ -n "${wantedref}" ]]; then gitref="$wantedref"; fi if lock "$@"; then clone run_if_function post_checkout_hook check_buildtools run_buildtools link_current run_if_function post_symlink_hook unlock else echo "Could not get lockfile $lockfile" exit 1 fi ;; 'help') usage ;; 'clean') load_rcfile if lock "$@"; then clean unlock else echo "Could not get lockfile $lockfile" exit 1 fi ;; 'rollback') load_rcfile if lock "$@"; then link_rollback unlock else echo "Could not get lockfile $lockfile" exit 1 fi ;; 'showpaths') load_rcfile print_variables ;; 'printrcfile') print_rcfile ;; 'shellcheck') shellcheck -e 1090,1091,2154 "$0" ;; esac