Skip to content

Instantly share code, notes, and snippets.

@stevenharman
Last active June 8, 2017 01:15
Show Gist options
  • Save stevenharman/27b8bcdb0c0ed49f07ab73c13f06900f to your computer and use it in GitHub Desktop.
Save stevenharman/27b8bcdb0c0ed49f07ab73c13f06900f to your computer and use it in GitHub Desktop.
A reasonable, and admittedly naïve, attempt at an automated Rails + CircleCI + Heroku deploy script. 🐉 THERE BE DRAGONS! 🐲
#!/usr/bin/env bash
#
# Usage: bin/heroku_deploy <heroku-app-name> [git-treeish : default=HEAD]
. ./shell_colors
set -euo pipefail
app_name=$1
app_remote=$app_name
deploy_treeish=${2:-HEAD}
deploy_sha=$(git rev-parse --verify $deploy_treeish)
use_maintenance_mode=1
# Do NOT put production into maintenance mode during deploys. We really need a better way of doing zero-downtime deploys.
# 🐉 THERE BE DRAGONS! 🐲
if [ "$app_name" = "<YOUR_PRODUCTION_HEROKU_APP_NAME_HERE>" ]; then
use_maintenance_mode=0
fi
printf "☁️ Setting up heroku git remote '$app_remote'.\n"
# Unshallow the repo -- required for recent git upgrade on CircleCI
[[ ! -s "$(git rev-parse --git-dir)/shallow" ]] || git fetch --unshallow
if ! git remote | grep -q "$app_remote"; then
git remote add "$app_remote" git@heroku.com:$app_name.git
fi
git fetch "$app_remote"
printf "\n🔐 Configuring ~/.netrc with Heroku credentials.\n"
if [[ ! -f ~/.netrc ]]; then
touch ~/.netrc
chmod 600 ~/.netrc # Heroku cli complains about permissions without this
fi
if grep 'api.heroku.com' ~/.netrc -q; then
printf "\n⚠️${YELLOW} You have existing credentials for 'api.heroku.com' and/or 'git.heroku.com'.${NO_COLOR}\n"
printf "ℹ️ Ensure the credientals in '~/.netrc' are valid.\n"
else
cat >>~/.netrc <<-EOF
machine api.heroku.com
login $HEROKU_EMAIL
password $HEROKU_TOKEN
machine git.heroku.com
login $HEROKU_EMAIL
password $HEROKU_TOKEN
EOF
fi
printf "\n💾 Checking for pending migrations and Heroku worker dyno size.\n"
migration_changes=$(git diff HEAD $app_remote/master --name-only -- db | wc -l)
prev_workers=$(heroku ps --app $app_name | { grep "^worker." || true; } | wc -l | tr -d ' ')
# migrations require downtime so enter maintenance mode
if [ $migration_changes -gt 0 ]; then
printf "\n⚠️${YELLOW} $migration_changes files needing migration.${NO_COLOR}\n"
if [ "$use_maintenance_mode" -eq "1" ]; then
heroku maintenance:on --app "$app_name"
fi
# Make sure workers are not running during a migration
printf "\n⚠️${YELLOW} Scaling $prev_workers down to '0'.${NO_COLOR}\n"
heroku scale worker=0 --app "$app_name"
fi
printf "\n💾 Capturing DB backup for '$app_name'.\n"
heroku pg:backups capture --app "$app_name"
# deploy code changes (and implicitly restart the app and any running workers)
printf "\n⛅️ Pushing code to '$app_name'.\n"
git push "$app_remote" "$deploy_sha":refs/heads/master
# run database migrations if needed and restart background workers once finished
if [ $migration_changes -gt 0 ]; then
printf "\n⏳ Migrating database for '$app_name'.\n"
# ignore errors so failed db migrations don't immediately abort our deploy script.
set +e
heroku run bin/rake db:migrate --app "$app_name" --exit-code
migration_status=$? # store the exit code
set -e
printf "\n👷 Scaling 'worker' dynos to '$prev_workers'.\n"
heroku scale worker="$prev_workers" --app "$app_name"
printf "\n♻️ Restarting dynos for '$app_name'.\n"
heroku restart --app "$app_name"
if [ "$use_maintenance_mode" -eq "1" ]; then
heroku maintenance:off --app "$app_name"
fi
if test $migration_status -ne 0; then
printf "💀${RED} Migrations fails. Failing the build.${NO_COLOR}\n"
exit $migration_status
fi
fi
printf "\n🎉${GREEN} Deploy for '$app_name' finished. Enjoy the new bits!${NO_COLOR}\n"
#!/usr/bin/env bash
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NO_COLOR='\033[0m'
CLEAR_LINE='\r\033[K'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment