Created
December 22, 2015 15:50
-
-
Save theoephraim/d2087563cfef8479d3fa to your computer and use it in GitHub Desktop.
Capistrano style deploys using flightplan
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
plan = require 'flightplan' | |
moment = require 'moment-timezone' | |
_ = require 'lodash' | |
nconf = require 'nconf' | |
request = require 'superagent' | |
async = require 'async' | |
# TODO: move to /lib like the others | |
require './src/run-init' | |
USER = 'breather' | |
NUM_DEPLOYS = 5 | |
SUBDOMAIN = nconf.get('BREATHER_SUBDOMAIN') | |
REPO = nconf.get('REPO_NAME') | |
REPO_URL = "https://github.com/breather/#{REPO}/" | |
NO_FRONTEND = !!nconf.get('NO_FRONTEND') | |
ssh_key = nconf.get('BREATHER_SSH_KEY') || "#{process.env.HOME}/.ssh/id_rsa" | |
deploy_branch = 'master' | |
# SERVERS | |
plan.target 'staging', _.map nconf.get('IP:staging').split(' '), (ip) -> | |
host: ip | |
username: USER | |
privateKey: ssh_key | |
plan.target 'production', _.map nconf.get('IP:production').split(' '), (ip) -> | |
host: ip | |
username: USER | |
privateKey: ssh_key | |
# TASKS | |
plan.local 'deploy', (local) -> | |
# confirm deployment to production | |
if plan.runtime.target == 'production' | |
input = local.prompt('Are you sure you want to deploy to production? [yes/no]') | |
if input.indexOf('yes') == -1 | |
plan.abort('user canceled deploy') | |
if plan.runtime.options.branch | |
deploy_branch = plan.runtime.options.branch | |
local.log("Pushing master to #{SUBDOMAIN}.breather.com") | |
else | |
git_dirty = local.exec('git diff --shortstat') | |
if git_dirty.stdout | |
plan.abort('Please commit your local changes and push them to github to deploy') | |
git_local_commit = local.exec('git rev-parse HEAD').stdout | |
git_remote_commit = local.exec('git rev-parse @{u}').stdout | |
if git_local_commit != git_remote_commit | |
plan.abort('push local commits to github to deploy') | |
current_branch = local.exec("git rev-parse --abbrev-ref HEAD").stdout | |
deploy_branch = current_branch.replace("\n", '') | |
if plan.runtime.target != 'staging' | |
env_type = "#{plan.runtime.options.customTarget || plan.runtime.target}" | |
local.log("Pushing #{deploy_branch} to #{SUBDOMAIN}#{env_type || ''}.breatherdev.com") | |
# DEPLOY - deploys current branch for staging or master for production | |
plan.remote 'deploy', (remote) -> | |
# set nocache to not use the old node_modules and bower libs | |
no_dependency_cache = !!plan.runtime.options.nocache | |
deploy_name = moment.tz('America/New_York').format('YYYY-MM-DD-HHmmss')+'-'+deploy_branch.replace(/\//g, '-') | |
remote.log 'Deploying -- '+deploy_name | |
remote.log 'Checking out latest from github' | |
remote.exec "mkdir -p #{REPO}" | |
remote.exec "cd #{REPO} && git clone [email protected]:breather/#{REPO}.git -b #{deploy_branch} #{deploy_name}" | |
cd = "cd #{REPO}/#{deploy_name} &&" | |
if !no_dependency_cache | |
remote.log 'Using cached dependencies from last deploy (npm)' | |
remote.exec "cp -R #{REPO}/current/node_modules #{REPO}/#{deploy_name}/node_modules" | |
if !NO_FRONTEND | |
remote.exec "mkdir #{REPO}/#{deploy_name}/assets/lib-bower" | |
remote.exec "cp -R #{REPO}/current/assets/lib-bower #{REPO}/#{deploy_name}/assets/lib-bower" | |
remote.log 'Installing dependencies' | |
remote.exec "#{cd} npm install --production" | |
if !NO_FRONTEND | |
remote.log 'Building assets' | |
remote.exec "#{cd} ./node_modules/.bin/grunt build" | |
remote.log 'Deploying assets' | |
remote.exec "#{cd} ./node_modules/.bin/grunt deploy-assets" | |
remote.log 'Relinking new deployment' | |
remote.exec "ln -snf ~/#{REPO}/#{deploy_name} ~/#{REPO}/current" | |
remote.log 'Reloading application (zero-downtime)' | |
remote.exec "pm2 reload all" | |
remote.log "Deleting old deploys (last #{NUM_DEPLOYS} are saved)" | |
remote.exec "cd #{REPO} && rm -rf `ls -td 201* | awk 'NR>#{NUM_DEPLOYS}'`" | |
# ROLLBACK - rolls back and restarts | |
plan.remote 'rollback', (remote) -> | |
deploys = remote.exec("cd #{REPO} && ls -td 201*").stdout.replace(/\n$/, '') | |
deploys = deploys.split("\n") | |
current_deploy = remote.exec("readlink -f #{REPO}/current").stdout | |
current_deploy = current_deploy.substr(current_deploy.lastIndexOf('/')+1).replace("\n", '') | |
current_index = _.indexOf(deploys, current_deploy) | |
num_available = deploys.length - current_index - 1 | |
if num_available == 0 | |
plan.abort('No old deploys available to roll back to') | |
rollback_num = remote.prompt("How many deploys do you want to rollback? [default: 1, max: #{num_available}]") | |
if rollback_num == '' then rollback_num = 1 | |
rollback_num = parseInt(rollback_num) | |
if rollback_num < 1 || rollback_num > num_available | |
plan.abort('Invalid # of deploys to roll back') | |
rollback_name = deploys[current_index + rollback_num] | |
remote.log "Relinking deployment #{rollback_num} back" | |
remote.exec "ln -snf ~/#{REPO}/#{rollback_name} ~/#{REPO}/current" | |
remote.log 'Reloading application (zero-downtime)' | |
remote.exec "pm2 reload all" | |
# RESTART - restarts pm2 | |
plan.remote 'restart', (remote) -> | |
remote.log 'Restarting application' | |
if plan.runtime.options.force | |
remote.exec 'pm2 kill' | |
# spawn as many instances as we have cores on the machine | |
remote.exec "cd #{REPO} && pm2 start current/app.coffee -i 0 --name \"#{REPO}\"" | |
else | |
remote.exec "pm2 reload all" | |
# STATUS - shows pm2 status | |
plan.remote 'status', (remote) -> | |
remote.exec "pm2 list" | |
# STATUS - shows pm2 status | |
plan.remote 'logs', (remote) -> | |
remote.exec "pm2 logs" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment