123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376 |
- #!/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}"
|