377 lines
7.6 KiB
Bash
377 lines
7.6 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
#
|
|
# deploy(1) - Minimalistic shell script to deploy Git repositories.
|
|
# Released under the MIT License.
|
|
#
|
|
# https://github.com/visionmedia/deploy
|
|
#
|
|
|
|
VERSION="0.6.0"
|
|
CONFIG=./deploy.conf
|
|
LOG=/tmp/pm2-deploy-${USER:-default}.log
|
|
FORCE=0
|
|
REF=
|
|
ENV=
|
|
|
|
#
|
|
# Read PIPED JSON
|
|
#
|
|
read conf
|
|
|
|
#
|
|
# Output usage information.
|
|
#
|
|
|
|
usage() {
|
|
cat <<-EOF
|
|
|
|
Usage: deploy [options] <env> [command]
|
|
|
|
Options:
|
|
|
|
-C, --chdir <path> change the working directory to <path>
|
|
-c, --config <path> set config path. defaults to ./deploy.conf
|
|
-V, --version output program version
|
|
-h, --help output help information
|
|
-f, --force skip local change checking
|
|
|
|
Commands:
|
|
|
|
setup run remote setup commands
|
|
revert [n] revert to [n]th last deployment or 1
|
|
config [key] output config file or [key]
|
|
curr[ent] output current release commit
|
|
prev[ious] output previous release commit
|
|
exec|run <cmd> execute the given <cmd>
|
|
list list previous deploy commits
|
|
ref [ref] deploy [ref]
|
|
|
|
EOF
|
|
}
|
|
|
|
#
|
|
# Abort with <msg>
|
|
#
|
|
|
|
abort() {
|
|
echo
|
|
echo " $@" 1>&2
|
|
echo
|
|
exit 1
|
|
}
|
|
|
|
#
|
|
# Log <msg>.
|
|
#
|
|
|
|
log() {
|
|
echo " ○ $@"
|
|
}
|
|
|
|
#
|
|
# Get config value by <key>.
|
|
#
|
|
|
|
config_get() {
|
|
local key=$1
|
|
echo $(expr "$conf" : '.*"'$key'":"\([^"]*\)"')
|
|
}
|
|
|
|
#
|
|
# Output version.
|
|
#
|
|
|
|
version() {
|
|
echo $VERSION
|
|
}
|
|
|
|
#
|
|
# Return the ssh command to run.
|
|
#
|
|
|
|
ssh_command() {
|
|
local user="`config_get user`"
|
|
if test -n "$user"; then
|
|
local url="$user@`config_get host`"
|
|
else
|
|
local url="`config_get host`"
|
|
fi
|
|
local unexpanded_key="`config_get key`"
|
|
local key="${unexpanded_key/#\~/$HOME}"
|
|
local forward_agent="`config_get forward-agent`"
|
|
local port="`config_get port`"
|
|
local needs_tty="`config_get needs_tty`"
|
|
local ssh_options="`config_get ssh_options`"
|
|
|
|
test -n "$forward_agent" && local agent="-A"
|
|
test -n "$key" && local identity="-i $key"
|
|
test -n "$port" && local port="-p $port"
|
|
test -n "$needs_tty" && local tty="-t"
|
|
test -n "ssh_options" && local ssh_opts="$ssh_options"
|
|
echo "ssh $ssh_opts $tty $agent $port $identity $url"
|
|
}
|
|
|
|
#
|
|
# Run the given remote <cmd>.
|
|
#
|
|
|
|
runRemote() {
|
|
local shell="`ssh_command`"
|
|
echo $shell "\"$@\"" >> $LOG
|
|
$shell $@
|
|
}
|
|
|
|
#
|
|
# Run the given local <cmd>.
|
|
#
|
|
|
|
runLocal() {
|
|
echo "\"$@\"" >> $LOG
|
|
/usr/bin/env bash -c "$*"
|
|
}
|
|
|
|
#
|
|
# Run the given <cmd> either locally or remotely
|
|
#
|
|
|
|
run() {
|
|
local host="`config_get host`"
|
|
if [[ $host == localhost ]]
|
|
then
|
|
runLocal $@
|
|
else
|
|
runRemote $@
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Output config or [key].
|
|
#
|
|
|
|
config() {
|
|
echo $(expr "$conf" : '.*"$key":"\([^"]*\)"')
|
|
}
|
|
|
|
#
|
|
# Execute hook <name> relative to the path configured.
|
|
#
|
|
|
|
hook() {
|
|
test -n "$1" || abort hook name required
|
|
local hook=$1
|
|
local path=`config_get path`
|
|
local cmd=`config_get $hook`
|
|
if test -n "$cmd"; then
|
|
log "executing $hook \`$cmd\`"
|
|
run "cd $path/current; \
|
|
SHARED=\"$path/shared\" \
|
|
$cmd 2>&1 | tee -a $LOG; \
|
|
exit \${PIPESTATUS[0]}"
|
|
test $? -eq 0
|
|
else
|
|
log hook $hook
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Pre Setup hook runs on the host before the actual setup runs
|
|
# multiple commands or a script
|
|
#
|
|
|
|
hook_pre_setup() {
|
|
local cmd=`config_get pre-setup`
|
|
if test -n "$cmd"; then
|
|
local is_script=($cmd)
|
|
if [ -f "${is_script[0]}" ]; then
|
|
log "executing pre-setup script \`$cmd\`"
|
|
local shell="`ssh_command`"
|
|
runLocal "$shell 'bash -s' -- < $cmd"
|
|
else
|
|
log "executing pre-setup \`$cmd\`"
|
|
run "$cmd"
|
|
fi
|
|
test $? -eq 0
|
|
else
|
|
log hook pre-setup
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Run setup.
|
|
#
|
|
|
|
setup() {
|
|
local path=`config_get path`
|
|
local repo=`config_get repo`
|
|
local ref=`config_get ref`
|
|
local fetch=`config_get fetch`
|
|
local branch=${ref#*/}
|
|
|
|
hook_pre_setup || abort pre-setup hook failed
|
|
run "mkdir -p $path/{shared/{logs,pids},source}"
|
|
test $? -eq 0 || abort setup paths failed
|
|
log running setup
|
|
log cloning $repo
|
|
if test "$fetch" != "fast"; then
|
|
log "full fetch"
|
|
run "git clone --branch $branch $repo $path/source"
|
|
else
|
|
log "fast fetch"
|
|
run "git clone --depth=5 --branch $branch $repo $path/source"
|
|
fi
|
|
test $? -eq 0 || abort failed to clone
|
|
run "ln -sfn $path/source $path/current"
|
|
test $? -eq 0 || abort symlink failed
|
|
hook post-setup || abort post-setup hook failed
|
|
log setup complete
|
|
}
|
|
|
|
#
|
|
# Deploy [ref].
|
|
#
|
|
|
|
deploy() {
|
|
local ref=$1
|
|
local branch=$2
|
|
|
|
if test -z "$branch"; then
|
|
branch=${ref#*/}
|
|
fi
|
|
local path=`config_get path`
|
|
local fetch=`config_get fetch`
|
|
|
|
log "deploying ${ref}"
|
|
|
|
# 1- Execute local commands
|
|
log executing pre-deploy-local
|
|
local pdl=`config_get pre-deploy-local`
|
|
runLocal $pdl || abort pre-deploy-local hook failed
|
|
|
|
# 2- Execute pre deploy commands on remote server
|
|
hook pre-deploy || abort pre-deploy hook failed
|
|
|
|
# 3- Fetch updates
|
|
log fetching updates
|
|
if test "$fetch" != "fast"; then
|
|
log "full fetch"
|
|
run "cd $path/source && git fetch --all --tags"
|
|
else
|
|
log "fast fetch"
|
|
run "cd $path/source && git fetch --depth=5 --all --tags"
|
|
fi
|
|
test $? -eq 0 || abort fetch failed
|
|
|
|
# 4- If no reference retrieve shorthand name for the remote repo
|
|
if test -z "$ref"; then
|
|
log fetching latest tag
|
|
ref=`run "cd $path/source && git for-each-ref \
|
|
--sort=-*authordate \
|
|
--format='%(refname)' \
|
|
--count=1 | cut -d '/' -f 3"`
|
|
test $? -eq 0 || abort failed to determine latest tag
|
|
fi
|
|
|
|
# 5- Reset to ref
|
|
log resetting HEAD to $ref
|
|
run "cd $path/source && git reset --hard $ref"
|
|
test $? -eq 0 || abort git reset failed
|
|
|
|
# 6- Link current
|
|
run "ln -sfn $path/source $path/current"
|
|
test $? -eq 0 || abort symlink failed
|
|
|
|
# deploy log
|
|
run "cd $path/source && \
|
|
echo \`git rev-parse HEAD\` \
|
|
>> $path/.deploys"
|
|
test $? -eq 0 || abort deploy log append failed
|
|
|
|
hook post-deploy || abort post-deploy hook failed
|
|
|
|
# done
|
|
log successfully deployed $ref
|
|
}
|
|
|
|
#
|
|
# Get current commit.
|
|
#
|
|
|
|
current_commit() {
|
|
local path=`config_get path`
|
|
run "cd $path/source && \
|
|
git rev-parse --short HEAD"
|
|
}
|
|
|
|
#
|
|
# Get <n>th deploy commit.
|
|
#
|
|
|
|
nth_deploy_commit() {
|
|
local n=$1
|
|
local path=`config_get path`
|
|
run "cat $path/.deploys | tail -n $n | head -n 1 | cut -d ' ' -f 1"
|
|
}
|
|
|
|
#
|
|
# List deploys.
|
|
#
|
|
|
|
list_deploys() {
|
|
local path=`config_get path`
|
|
run "tac $path/.deploys | awk '{printf \"%d\t%s\n\", NR-1, \$0}'"
|
|
}
|
|
|
|
#
|
|
# Revert to the <n>th last deployment.
|
|
#
|
|
|
|
revert_to() {
|
|
local n=$1
|
|
log "reverting $n deploy(s)"
|
|
local commit=`nth_deploy_commit $((n + 1))`
|
|
deploy "$commit"
|
|
}
|
|
|
|
#
|
|
# Ensure all changes are committed and pushed before deploying.
|
|
#
|
|
|
|
check_for_local_changes() {
|
|
local path=`config_get path`
|
|
|
|
if [ $FORCE -eq 1 ]
|
|
then
|
|
return
|
|
fi
|
|
git --no-pager diff --exit-code --quiet || abort "commit or stash your changes before deploying"
|
|
git --no-pager diff --exit-code --quiet --cached || abort "commit your staged changes before deploying"
|
|
[ -z "`git rev-list @{upstream}.. -n 1`" ] || abort "push your changes before deploying"
|
|
}
|
|
|
|
# parse argv
|
|
while test $# -ne 0; do
|
|
arg=$1; shift
|
|
case $arg in
|
|
-h|--help) usage; exit ;;
|
|
-V|--version) version; exit ;;
|
|
-C|--chdir) log cd $1; cd $1; shift ;;
|
|
-F|--force) FORCE=1 ;;
|
|
run|exec) run "cd `config_get path`/current && $@"; exit ;;
|
|
console) console; exit ;;
|
|
curr|current) current_commit; exit ;;
|
|
prev|previous) nth_deploy_commit 2; exit ;;
|
|
revert) revert_to ${1-1}; exit ;;
|
|
setup) setup $@; exit ;;
|
|
list) list_deploys; exit ;;
|
|
config) config $@; exit ;;
|
|
ref) REF=$1; BRANCH=$2 ;;
|
|
esac
|
|
done
|
|
|
|
check_for_local_changes
|
|
|
|
echo
|
|
# deploy
|
|
deploy "${REF:-`config_get ref`}" "${BRANCH}"
|