Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 396 lines (328 sloc) 7.93 KB
#!/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.log
TEST=1
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
-T, --no-tests ignore test hook
-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}"
# PR #50
log executing pre-deploy-local
local pdl=`config_get pre-deploy-local`
runLocal $pdl || abort pre-deploy-local hook failed
hook pre-deploy || abort pre-deploy hook failed
if run "cd $path/source && git rev-parse --quiet --verify $branch" > /dev/null; then
log fast forward $branch
run "cd $path/source && git checkout $branch && git pull origin $branch"
test $? -eq 0 || abort git pull failed
else
# fetch source
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
# checkout ref to branch
log checkout $ref to $branch
run "cd $path/source && git checkout $ref -b $branch"
test $? -eq 0 || abort git checkout failed
fi
# 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 --short HEAD\` \
>> $path/.deploys"
test $? -eq 0 || abort deploy log append failed
hook post-deploy || abort post-deploy hook failed
if test $TEST -eq 1; then
hook test
if test $? -ne 0; then
log tests failed, reverting deploy
quickly_revert_to 1 && log "revert complete" && exit
fi
else
log ignoring tests
fi
# 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, ignoring tests.
#
quickly_revert_to() {
local n=$1
log "quickly reverting $n deploy(s)"
local commit=`nth_deploy_commit $((n + 1))`
TEST=0 deploy "$commit"
}
#
# 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 ;;
-T|--no-tests) TEST=0 ;;
-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
# deploy
deploy "${REF:-`config_get ref`}" "${BRANCH}"