June 9th/ Quick & Dirty: Add changesets since last deploy for your Rails apps with Lighthouse
One of the most common problems for me is knowing when I last deployed my apps, and what I’ve done since then. I often will leave an app on the side for a month or two at a time, and there’s a good chance I implemented some new fix, waited until a low-traffic time to deploy and subsequently forgot. I end up in a state of not really knowing what’s up with the deploy.
So I finally sat down and hacked together a quick ‘System’ screen in my apps. Here’s an example:
System screen for Clipgarden (rendered in Fluid.app)
It tells me:
- How long it’s been since the last deploy
- The currently deployed revision
- Changes committed to the repository that are not yet deployed
This has been pretty awesome for me so far as it’s a really easy way to figure out if a certain fix or feature has been deployed yet (ever given the answer “well it’s committed, but not deployed”?).
Setup
In order for this code to work, you’ll need a Lighthouse account, and set it up to pull in changesets from your repository. I personally do this through Github since it’s just so damn easy. I won’t go into how to do this since there’s plenty of tutorials out there.
The Code
You’ll need to add the lighthouse api to your lib/ folder and then the following code should get you going. This code also assumes you’re using Capistrano or Vlad for your deployment so that a REVISION file is created in your root folder. If it doesn’t exist, there’s no harm done — but you won’t be getting any results.
Note that this also makes this fix a little hard to test in development since the whole idea is that it’s deploy-centric. I’d recommend copying down an old REVISION and throwing it in your rails root (but don’t commit!) for testing.
New model: system.rb
class System
@@lighthouse_account = "ACCOUNT_NAME[CHANGE]”
@@lighthouse_token = “BEACON_TOKEN[CHANGE]”
@@lighthouse_project_id = PROJECT_ID[CHANGE]
@@revision = nil
def self.revision
return @@revision unless @@revision.nil?
if File.exists?(RAILS_ROOT + “/REVISION”)
@@revision = IO.read(RAILS_ROOT + “/REVISION”).to_s.strip
else
@@revision = “unknown (in development mode)”
end
@@revision
end
@@deployed_at = nil
def self.deployed_at
return @@deployed_at unless @@deployed_at.nil?
if File.exists?(RAILS_ROOT + “/REVISION”)
@@deployed_at = File.mtime(RAILS_ROOT + “/REVISION”)
else
@@deployed_at = Time.now
end
@@deployed_at
end
@@lighthouse_project = nil
def self.lighthouse_project
return @@lighthouse_project unless @@lighthouse_project.nil?
Lighthouse.account = @@lighthouse_account
Lighthouse.token = @@lighthouse_token
@@lighthouse_project = Lighthouse::Project.find(@@lighthouse_project_id)
@@lighthouse_project
end
def self.changesets_since_deploy
return [] if self.revision == “unknown (in development mode)”
all_changesets = lighthouse_project.changesets
changesets_since = []
all_changesets.each do |changeset|
break if changeset.revision == self.revision
changesets_since << changeset
end
changesets_since
end
end
Get the changesets in your controller
def index
@changesets = System.changesets_since_deploy
end
Build a simple view
<h1>System Status</h1>
<p>The system was last updated <%= distance_of_time_in_words(System.deployed_at, Time.now) %> ago with revision <%= System.revision %></p>
<% unless @changesets.empty? %>
<h2>Changes made but not deployed</h2>
<div class="metabar">
<p class="secondary"><%= @changesets.size %> changeset<%='s' unless @changesets.size == 1%></p>
</div><!-- /.metabar -->
<table>
<tr>
<th>Change</th>
<th>Commited At</th>
</tr>
<% for changeset in @changesets %>
<tr>
<td class="small">
<%= changeset.body_html %>
</td>
<td>
<%= changeset.changed_at.to_formatted_s(:admin) %>
</td>
</tr>
<% end %>
</table>
<div class="metabar">
<p class="secondary"><%= @changesets.size %> changeset<%='s' unless @changesets.size == 1%></p>
</div><!-- /.metabar -->
<% else %>
<p>No changes!</p>
<% end %>
Downfalls
There are a few downfalls to this quick & dirty approach. The most noticeable is that each and every one of your requests to this system page make an API call to Lighthouse. However, so long as you put this page in the admin section of your site, you should be fine. In a perfect world, we’d cache the results locally.
Have fun and let me know if you have any improvements, or any way to add this into some kind of useful distributed form (plugin?).
5 Comments
Make a Comment
don’t be afraid, it’s just text

Warpspire is the place that web professional Kyle Neath writes about the web. 


June 9th | #
Nicely done. I have to say though, I’m much more impressed with your admin page for Clipgarden–usually admin pages for homegrown software are ugly and unusable, but it looks like you actually took the time to make yours look nice!
June 9th | #
Yeah, I do have a thing for designing administrative interfaces (I really love them), plus I find the more effort I put into the admin system, the more I end up caring about the site. Hopefully one of these days I’ll finish off some of my thoughts about building admin interfaces into an article.
June 30th | #
<%= pluralize(@changesets.size, ‘changeset’) %>
July 7th | #
yeah… thanks for the post dude it helps a lot…
July 17th | #
Very good post, your idea is great! I also have problem with remembering what I had done since I last deployed an application but you found a good way of solving that:)