Skip to content

Instantly share code, notes, and snippets.

@mmrwoods
Created April 6, 2011 16:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mmrwoods/906027 to your computer and use it in GitHub Desktop.
Save mmrwoods/906027 to your computer and use it in GitHub Desktop.
Capistrano Lighthouse integration script
#!/usr/bin/env ruby
# this simple script takes two arguments, the rails environment we're deploying to and a commit range to be passed to git-log
# The commit range MUST be in the format <full sha of previous revision>..<full sha of current revision>, otherwise the world will end
# It looks at git commit messages between the previous and current revision, extracts lighthouse ticket numbers where they are provided
# in the commit message; updates the ticket in lighthouse, changing the state to 'resolved' for deploys to production, both changing
# state to 'uat' and re-assigning the ticket to the creator for deploys to stage; then sends an email listing the deployed tickets.
# Note: if rails environment is edge, does a dummy run as if it was deploying to stage, but doesn't save updates to tickets or send the email
LH_TOKEN = YOUR-LIGHTHOUSE-API-TOKEN
LH_ACCOUNT = YOUR-LIGHTHOUSE-ACCOUNT-NAME
LH_PROJECT_ID = nil # set to project id if account has more than one project (we just get the first by default)
MAIL_TO = NOTIFICATION-EMAIL
QA_MAIL_TO = nil # set if you need QA notifications sent to a different address
SMTP_SERVER = "localhost"
puts "Executing script/deploy_lighthouse #{ARGV.join(' ')}"
if ARGV.length < 2
puts "ERROR: deploy_lighthouse script needs two arguments, the rails environment and the commit range (previous_revision_sha..current_revision_sha)"
exit # be nice to cap, and exit 0
end
unless %w{ edge stage production }.include? ARGV[0]
puts "Rails environment isn't edge, stage or production, nothing to do"
exit # be nice to cap, and exit 0
end
begin
require File.dirname(__FILE__) + "/../config/init" # override rubygems and use aspire's gem bundling
require 'fileutils'
require 'lighthouse'
Lighthouse.token = LH_TOKEN
Lighthouse.account = LH_ACCOUNT
rails_env = ARGV[0]
git_commit_range = ARGV[1]
# only tickets with one of these previous states will be updated
# note: sequence important, we have to loop over this array and get tickets at each state
previous_states = %w{deploy uat qa coding-done query}
# new state after we update the ticket depends on environment
if %w{ edge stage }.include? rails_env
new_state = 'qa'
mail_to = QA_MAIL_TO || MAIL_TO
else
new_state = 'resolved'
mail_to = MAIL_TO
end
puts "=== Rails env is edge - dummy run, changes to tickets will not be saved ===" if rails_env == 'edge'
# ensure we are in root of the project
Dir.chdir(File.join(File.dirname(__FILE__), ".."))
# extract ticket numbers from commit messages between previous and current revision
puts "extracting ticket numbers from commit messages between previous and current revision ..."
ticket_numbers = []
messages = `git log #{git_commit_range} --pretty=format:%s`.split("\n")
messages.each { |message| ticket_numbers << message.match(/(\[#)(\d+)/)[2].to_i if message =~ /(\[#)(\d+)/ }
ticket_numbers.uniq!
puts "Found #{messages.size} log messages containing #{ticket_numbers.size} ticket numbers"
puts "Ticket numbers are #{ticket_numbers.inspect}"
# update tickets in lighthouse...
puts "updating tickets in lighthouse..."
project = LH_PROJECT_ID.to_s.match(/^[0-9]+$/) ? Lighthouse::Project.find(LH_PROJECT_ID.to_i) : Lighthouse::Project.find(:first)
ticket_info = {}
previous_states.each do |previous_state|
project.tickets(:q => "state:#{previous_state}").each do |ticket|
next unless ticket_numbers.include? ticket.number
ticket.state = new_state
ticket_info[ticket.number] = "#{ticket.title} -> state:#{ticket.state}" + ( new_state != 'resolved' ? ", responsible:#{ticket.user_name}" : '')
puts "Saving ticket ##{ticket.number} #{ticket_info[ticket.number]}"
begin
ticket.save unless rails_env == 'edge'
rescue Exception => e
puts "ERROR #{e}, failed to save ticket ##{ticket.number}, please update this ticket manually"
ticket_info.delete(ticket.number)
next
end
end
end
if rails_env != 'edge' && ticket_info.size > 0 && !mail_to.nil? && mail_to.length > 0 # um, is there a need to be any smarter here?
puts "Sending summary of lighthouse updates to #{mail_to}"
begin
require 'socket'
require 'net/smtp'
host_name = Socket.gethostbyname(Socket.gethostname).first rescue Socket.gethostname
mail_from = "deploy@#{host_name}"
subject = "#{rails_env.capitalize} deploy lighthouse tickets"
headers = <<-HEADERS_END
From: #{project.name} <#{mail_from}>
To: #{mail_to}
Subject: #{subject}
Content-type: text/html
HEADERS_END
headers.gsub!(/^(\s+)(.*)$/,'\2') # trash the leading spaces
ticket_base_url = "https://#{LH_ACCOUNT}.lighthouseapp.com/projects/#{project.id}/tickets/"
message = "<p>The following items have been deployed to #{rails_env}...</p><ul>"
ticket_info.each do |k, v|
message << "<li><a href='#{ticket_base_url}#{k}'>#{k}</a> - #{v}</li>"
end
message << "</ul>"
# add footer if new state is uat or qa
if new_state == 'uat'
message << %q{
<p>
<strong>If you are responsible for a ticket, according to the above list (that is, you're the person who created the ticket)...</strong><br>
We'd like you to work out who should do the UAT and make sure it gets done. That's probably the person who asked you to create the ticket, or it might be you.
</p>
<p>
<strong>If you are a stakeholder (of the new feature) or reporter (of the bug)...</strong><br>
Someone will be in touch soon to get you involved in the UAT. Please review the changes on stage as soon as you can. If you need any assistance, are unsure of what has been fixed or are unhappy with the work, please let us know.
</p>
<p>
<strong>If you're not a stakeholder, BUT you or the team you manage might be impacted by the changes...</strong><br>
Please make time to review the changes on stage (with developer help if required).
</p>
<p>
Thanks
</p>
}
elsif new_state == 'qa'
message << %q{
<p>
Tickets are now in QA guys, you know what to do.
</p>
<p>
Thanks, Robot
</p>
}
end
Net::SMTP.start(SMTP_SERVER) do |smtp|
smtp.send_message headers + "\n" + message, mail_from, mail_to
end
puts "Message has been passed to #{SMTP_SERVER} for delivery"
rescue Exception => e
puts "ERROR #{e}, sending output via email failed, it's time to copy 'n' paste"
end
end
rescue Exception => e
puts "Exception #{e}..."
puts e.backtrace
exit # note: a non-zero exit code results in capistrano raising an exception, which we don't want
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment