Skip to content

Instantly share code, notes, and snippets.

@ibanez270dx
Last active September 20, 2021 20:36
Show Gist options
  • Save ibanez270dx/b5f5cc041d69f4edeca6e4be26fa35af to your computer and use it in GitHub Desktop.
Save ibanez270dx/b5f5cc041d69f4edeca6e4be26fa35af to your computer and use it in GitHub Desktop.
Capistrano Deploy Tasks
namespace :deploy do
task :confirmation do
color = case fetch(:stage)
when :production then 31
when :staging then 33
else 34
end
puts <<~WARN
\e[1mYou are about to deploy branch \e[#{color}m#{fetch(:branch)}\e[0m\e[1m to \e[#{color}m#{fetch(:stage)}\e[0m
\e[31mAre you sure you want to continue?\e[0m
WARN
ask :value, "Y/n"
unless %w(Y y).include?(fetch(:value))
puts "\nDeploy cancelled!"
exit
end
if fetch(:stage) == :production
puts <<~WARN
\e[1m\e[41mAre you \e[3mREALLY\e[23m sure you want to continue?\e[22m\e[0m
WARN
ask :value, "Y/n"
unless %w(Y y).include?(fetch(:value))
puts "\nDeploy cancelled!"
exit
end
end
end
end
Capistrano::DSL.stages.each do |stage|
# skip deploy confirmations for continuous integration
before("deploy:prepare_tree", "deploy:confirmation") unless ENV["CI"]
end
# Deploy Tagging based on capistrano/deploytags
# https://github.com/mydrive/capistrano-deploytags/blob/master/lib/capistrano/tasks/deploytags.rake
# https://github.com/mydrive/capistrano-deploytags/blob/master/lib/capistrano/deploytags.rb
namespace :deploy do
desc "prepare git tree so we can tag on successful deployment"
before :deploy, :prepare_tree do
run_locally do
branch = fetch(:branch, false)
stage = fetch(:stage, false)
unless branch && stage
error ':branch and :stage must be defined for deploy tagging'
raise 'define :branch and :stage'
end
execute :git, "fetch #{fetch(:git_remote, 'origin')}"
diff_output = capture :git, "diff #{branch} --shortstat"
unless diff_output.empty?
error "Whoa there, partner. Dirty trees can't deploy. Git yerself clean first"
raise 'Dirty git tree'
end
execute :git, "checkout #{branch}"
info "Pulling from #{branch}"
execute :git, "pull #{fetch(:git_remote, 'origin')} #{branch}"
end
end
desc "add git tags and set app version (Errbit) for each successful deployment"
after :cleanup, :tagdeploy do
run_locally do
revision = fetch(:current_revision)
tag_name = "#{fetch(:stage)}-#{Time.now.strftime("%Y.%m.%d-%H%M%S-%Z")}"
commit_message = fetch(:ci) ?
"#{fetch(:deployer)} deployed #{revision} to #{fetch(:stage)}" :
"#{local_user.strip} deployed #{revision} to #{fetch(:stage)}"
unless fetch(:sshkit_backend) == SSHKit::Backend::Printer # unless --dry-run flag present
# configure git on the CI
if fetch(:ci)
execute :git, "config user.email \"dev@cognoa.com\""
execute :git, "config user.name \"CircleCI\""
end
# git tags
execute :git, "tag -a #{tag_name} -m \"#{commit_message}\" #{revision}"
execute :git, "push #{fetch(:git_remote, 'origin')} #{tag_name}"
info "[deploy-tags] tagged #{revision} with #{tag_name}"
# set tag name for New Relic
set :newrelic_revision, tag_name
# upload .deploy_tag for the status page
File.open(".deploy_tag", "w+") { |file| file.write(tag_name) }
on roles(:app) do
info "[deploy-tags] uploading deploy tag to #{current_path}/.deploy_tag"
upload! ".deploy_tag", "#{current_path}/.deploy_tag"
end
# rename .deploy_tag
File.rename ".deploy_tag", ".last_deployed_tag"
end
end
end
end
namespace :image_service do
desc "Run image pre-scaling task"
task :prescale do
on roles(:db) do
info "Pre-scale common image sizes"
within release_path do
with rails_env: fetch(:rails_env) do
execute :rake, "image_service:prescale"
end
end
end
end
desc "Collect stage image URLs, generate vegeta load test script, and download locally"
task :vegeta do
on roles(:db) do
info "Collect image URLs, generate load test script, and download"
within release_path do
with rails_env: fetch(:rails_env) do
execute :rake, "image_service:vegeta"
download! "vegeta.sh", "vegeta-#{fetch(:rails_env)}.sh"
end
end
end
end
end
namespace :maintenance do
desc "Turn on maintenance mode"
task :enable do
on roles(:web) do
execute "mv #{release_path}/public/maintenance_off.html #{release_path}/public/maintenance_on.html "
end
end
desc "Turn off maintenance mode"
task :disable do
on roles(:web) do
execute "mv #{release_path}/public/maintenance_on.html #{release_path}/public/maintenance_off.html "
end
end
end
require "slack-notifier"
require "active_support"
namespace :slack do
desc "Notify Slack #devbots channel that a deploy has started"
task :notify_started do
run_locally do
info "notifying slack of deploy start"
set :time_started, Time.now.to_i
set :ci, !ENV["CI"].nil?
set :deployer, (fetch(:ci) ? "CircleCI" : local_user.strip)
capbot "Deploy to #{fetch(:stage)} started by #{fetch(:deployer)}", attachments: [{
color: "#5D89B5", thumb_url: %w[
https://emojis.slackmojis.com/emojis/images/1519135949/3567/typing_frog.gif
https://emojis.slackmojis.com/emojis/images/1498862355/2559/cattyping.gif
https://emojis.slackmojis.com/emojis/images/1450319443/19/excited.gif
https://emojis.slackmojis.com/emojis/images/1495224268/2311/ship_it_parrot.gif
https://emojis.slackmojis.com/emojis/images/1450731446/228/matrix.gif
https://emoji.slack-edge.com/T03U1SY4X/excellent/89ec39a77709cbe1.gif
https://emoji.slack-edge.com/T03U1SY4X/fingerscrossed/a941afd535c7b067.jpg
https://emoji.slack-edge.com/T03U1SY4X/challenge_accepted/98341bd35c39e2ce.jpg
https://emoji.slack-edge.com/T03U1SY4X/allthethings/1a2b66eefae6ca81.jpg
].sample
}]
end
end
desc "Notify Slack #devbots channel of a successful deployment"
task :notify_success do
run_locally do
info "notifying slack of successfully deploy"
set :deploy_tag, File.read(".last_deployed_tag").strip
time = Integer(Time.now.to_i - fetch(:time_started, 0))
capbot "Deploy to #{fetch(:stage)} by #{fetch(:deployer)} completed in #{time} seconds", attachments: [{
color: "good", thumb_url: %w[
https://emojis.slackmojis.com/emojis/images/1471119458/990/party_parrot.gif
https://emojis.slackmojis.com/emojis/images/1498861777/2539/micdrop.gif
https://emojis.slackmojis.com/emojis/images/1450319453/122/carlton.gif
https://emojis.slackmojis.com/emojis/images/1471045833/765/beer.gif
https://emojis.slackmojis.com/emojis/images/1466661664/559/mindblown.gif
https://emoji.slack-edge.com/T03U1SY4X/fonze/6031cfd4bd068320.png
https://emoji.slack-edge.com/T03U1SY4X/hammertime/23c8b8e789160b8c.gif
https://emoji.slack-edge.com/T03U1SY4X/success/64a31bf8c7b93614.png
https://emoji.slack-edge.com/T03U1SY4X/giggity/26b4ef980974fa0a.gif
].sample
}]
end
end
desc "Notify Slack #devbots channel of a failed deployment"
task :notify_failure do
run_locally do
info "notifying slack of deploy failure"
capbot "Deploy to #{fetch(:stage)} failed or cancelled by #{fetch(:deployer)}", attachments: [{
color: "danger", thumb_url: %w[
https://emojis.slackmojis.com/emojis/images/1492807538/2109/duckhunt.gif
https://emojis.slackmojis.com/emojis/images/1507155739/2982/thisisfine_blink.gif
https://emojis.slackmojis.com/emojis/images/1526049257/3887/bushes.gif
https://emojis.slackmojis.com/emojis/images/1520809041/3645/please_no.gif
https://emojis.slackmojis.com/emojis/images/1472329131/1120/nuclear-bomb.gif
https://emoji.slack-edge.com/T03U1SY4X/crying-jordan/e9adc81033f85211.png
https://emoji.slack-edge.com/T03U1SY4X/garbagefire/677609a9c28d8d74.gif
https://emoji.slack-edge.com/T03U1SY4X/kevin/c0c70ae4013841d8.jpg
https://emoji.slack-edge.com/T03U1SY4X/picard-facepalm/a96d4db97b25e6ce.png
].sample
}]
end
end
end
# FIXME(low priority): clean this up using jBuilder
def capbot(message, options = {}) # rubocop: disable CyclomaticComplexity, PerceivedComplexity
defaults = {
channel: "#devbots",
username: "CapBot",
icon_emoji: ":capistrano:",
attachments: [{
fallback: message,
color: "#ccc",
author_name: fetch(:nginx_server_name),
author_link: "https://#{fetch(:nginx_server_name)}",
author_icon: "https://app.cognoa.com/favicon-128.png",
title: message,
title_link: (fetch(:ci) ?
ENV["CIRCLE_BUILD_URL"] :
"https://github.com/cognoa/cognoa/tree/#{`git rev-parse HEAD`[0..6]}"
),
fields: [
{ title: "Environment", value: "<#{fetch(:nginx_server_name)}|#{fetch(:stage)}>", short: true },
{ title: "Hosts", value: release_roles(:all).map(&:hostname).join("\n"), short: true },
(fetch(:ci) ?
{ title: "Build", value: "<#{ENV['CIRCLE_BUILD_URL']}|#{ENV['CIRCLE_BRANCH']} ##{ENV['CIRCLE_BUILD_NUM']}>", short: true } :
{ title: "Branch", value: "<https://github.com/cognoa/cognoa/tree/#{fetch(:branch)}|#{fetch(:branch)}>", short: true }),
(fetch(:ci) ?
{ title: "Changes", value: "<#{ENV['CIRCLE_COMPARE_URL']}|#{ENV['CIRCLE_COMPARE_URL'].split('/').last}>", short: true } :
{ title: "Commit", value: "<https://github.com/cognoa/cognoa/tree/#{`git rev-parse HEAD`[0..6]}|#{`git rev-parse HEAD`[0..6]}>", short: true }),
(fetch(:deploy_tag) ?
{ title: "Deploy Tag", value: "<https://github.com/cognoa/cognoa/tree/#{fetch(:deploy_tag)}|#{fetch(:deploy_tag)}>", short: false } : {}
)
],
footer: (fetch(:ci) ?
"triggered automatically via CircleCI" :
"triggered manually by #{local_user.strip} via Capistrano"
),
footer_icon: (fetch(:ci) ?
"https://assets.brandfolder.com/otz5mn-bw4j2w-6jzqo8/view.png" :
"https://emojis.slackmojis.com/emojis/images/1493816710/2165/capistrano.png?1493816710"
),
ts: Time.now.to_i
}]
}
options[:attachments][0] = defaults[:attachments][0].deep_merge(options[:attachments][0]) if options.has_key?(:attachments)
Slack::Notifier.new("https://hooks.slack.com/services/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").post(defaults.deep_merge(options))
end
Capistrano::DSL.stages.each do |stage|
unless ENV["SNEAKY_DEPLOY"]
before "deploy:starting", "slack:notify_started"
after "deploy:finished", "slack:notify_success"
after "deploy:failed", "slack:notify_failure"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment