Last active
March 9, 2017 07:23
-
-
Save jwaldrip/01bfa48f03816a4df4ad to your computer and use it in GitHub Desktop.
Full CI-CD with Heroku and Circle CI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
machine: | |
services: | |
- 'postgresql' | |
- 'redis' | |
environment: | |
REDIS_URL: 'redis://localhost:6379/0' | |
dependencies: | |
pre: | |
- 'git config user.email [email protected]' | |
- 'git config user.name $CIRCLE_USERNAME' | |
- 'bundle config --global frozen 1' | |
cache_directories: | |
- 'vendor/bundle' | |
test: | |
override: | |
- 'bundle exec rspec --format progress --color spec' | |
deployment: | |
# Deploy the pre-release develop branch to staging | |
staging: | |
branch: develop | |
commands: | |
- 'WORKER_DYNOS=1 ./bin/heroku-deploy brandfolder-staging $CIRCLE_SHA1' | |
# Deploy the production apps, production and admin | |
production: | |
branch: master | |
commands: | |
- 'WORKER_DYNOS=2 ./bin/heroku-deploy brandfolder-production $CIRCLE_SHA1' | |
- 'heroku config -s -a brandfolder-production | xargs heroku config:set -a brandfolder-admin' | |
- 'WORKER_DYNOS=0 ./bin/heroku-deploy brandfolder-admin $CIRCLE_SHA1' | |
# Create a new app on each deploy of a branch in the feature namespace | |
feature: | |
branch: /feature\/.*/ | |
commands: | |
- 'mkdir -p ./tmp' | |
- 'echo "bf-$CIRCLE_BRANCH" | sed "s/feature\///" | cut -c 1-30 > ./tmp/.appname' | |
- 'heroku apps:destroy -a `cat ./tmp/.appname` --confirm `cat ./tmp/.appname` || true' | |
- 'heroku create `cat ./tmp/.appname`' | |
- 'heroku addons:add -a `cat ./tmp/.appname` heroku-postgresql' | |
- 'heroku addons:add -a `cat ./tmp/.appname` sendgrid' | |
- 'heroku addons:add -a `cat ./tmp/.appname` rediscloud' | |
- 'heroku config:set -a `cat ./tmp/.appname` SECRET_KEY_BASE=`rake secret` LOCKUP_CODEWORD=cantshowthis' | |
- 'SKIP_MIGRATIONS=true WORKER_DYNOS=0 ./bin/heroku-deploy `cat ./tmp/.appname` $CIRCLE_SHA1' | |
- 'heroku pg:info --app `cat ./tmp/.appname` | grep --color=never -oE "HEROKU_POSTGRESQL[A-Z_]+" > ./tmp/.dbname' | |
- 'heroku pg:reset --app `cat ./tmp/.appname` --confirm `cat ./tmp/.appname` `cat ./tmp/.dbname`' | |
- 'bundle exec rake db:rebuild' | |
- 'heroku pg:push --app `cat ./tmp/.appname` circle_ruby_test `cat ./tmp/.dbname`' | |
- 'curl -f https://`cat ./tmp/.appname`.herokuapp.com' | |
# Deploy all things in the hotfix namespace to the hotfix app and Copy settings from prod | |
hotfix: | |
branch: /hotfix\/.*/ | |
commands: | |
- 'heroku config -s -a brandfolder-production | xargs heroku config:set -a brandfolder-hotfix' | |
- 'heroku config:set -a brandfolder-production CACHE_DISABLED=false' | |
- 'WORKER_DYNOS=0 ./bin/heroku-deploy brandfolder-hotfix $CIRCLE_SHA1' | |
# Notify Kato of the Deploy | |
notify: | |
webhooks: | |
- url: 'https://api.kato.im/rooms/d14ec35cbb7cd962ab6e134be39614634bcf1fb2da856b821c8e2e4de1f98a3a/circleci' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
class Deployer | |
LAST_MIGRATION_CMD = %{bundle exec rake db:migrate:status | grep "up" | grep -oE "[0-9]{8}[0-9]+" | tail -1} | |
def self.deploy! | |
new.deploy! | |
end | |
def initialize | |
@appname = ARGV[0] || raise("appname required") | |
@sha = ARGV[1] || raise("sha required") | |
@worker_dynos = ENV['WORKER_DYNOS'] || "1" | |
end | |
def local_sha | |
ENV['CIRCLE_SHA'] || `git rev-parse --verify HEAD`.strip | |
end | |
def remote_sha | |
`heroku releases -n 1 -a #{@appname} | awk '/Deploy/ {print $3}'`.strip | |
end | |
def remote | |
"[email protected]:#{@appname}.git" | |
end | |
def deploy! | |
puts "deploying `#{@sha}` to `#{@appname}`" | |
with_migrations do | |
output = `git push #{remote} #{@sha}:refs/heads/master` | |
!output.lines.last.to_s.include?("Aborting") | |
end | |
finish | |
end | |
private | |
def finish | |
system("heroku scale worker=#{@worker_dynos} --app #{@appname}") | |
system("heroku maintenance:off --app #{@appname}") | |
system('git stash pop', out: '/dev/null', err: '/dev/null') | |
unless local_sha.include? remote_sha | |
STDERR.puts("deploy invalid, remote: #{remote_sha} does not match #{local_sha}") | |
exit 1 | |
end | |
end | |
def pending_migrations? | |
return false if ENV['SKIP_MIGRATIONS'] | |
return true if ENV['FORCE_MIGRATIONS'] | |
@pending_migrations ||= begin | |
`#{LAST_MIGRATION_CMD}`.strip != | |
`heroku run --app #{@appname} #{LAST_MIGRATION_CMD}`.strip | |
end | |
end | |
def with_migrations(&block) | |
return instance_eval(&block) unless pending_migrations? | |
system("heroku maintenance:on --app #{@appname}") || exit(1) | |
system("heroku ps:stop worker --app #{@appname}") || finish | |
instance_eval(&block) | |
system("heroku run rake db:migrate --app #{@appname}") | |
end | |
end | |
Deployer.deploy! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment