Commit 5d56e80523035f6895d568ff4a737d588fbf14ce

Authored by Jared Pace
1 parent 2cecdbaa
Exists in master and in 1 other branch production

Add endpoint for deploys and deliver notifications on deploy

app/controllers/deploys_controller.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +class DeploysController < ApplicationController
  2 +
  3 + def create
  4 + @project = Project.find_by_api_key!(params[:api_key])
  5 + @deploy = @project.deploys.create!({
  6 + :username => params[:deploy][:local_username],
  7 + :environment => params[:deploy][:rails_env],
  8 + :repository => params[:deploy][:scm_repository],
  9 + :revision => params[:deploy][:scm_revision]
  10 + })
  11 + render :xml => @deploy
  12 + end
  13 +
  14 +end
0 \ No newline at end of file 15 \ No newline at end of file
app/mailers/mailer.rb
@@ -7,8 +7,18 @@ class Mailer &lt; ActionMailer::Base @@ -7,8 +7,18 @@ class Mailer &lt; ActionMailer::Base
7 @project = notice.err.project 7 @project = notice.err.project
8 8
9 mail({ 9 mail({
10 - :to => @project.watchers.map(&:email),  
11 - :subject => "[#{@project.name}] #{@notice.err.message}" 10 + :to => @project.watchers.map(&:email),
  11 + :subject => "[#{@project.name}] #{@notice.err.message}"
  12 + })
  13 + end
  14 +
  15 + def deploy_notification(deploy)
  16 + @deploy = deploy
  17 + @project = deploy.project
  18 +
  19 + mail({
  20 + :to => @project.watchers.map(&:email),
  21 + :subject => "[#{@project.name}] Deployed to #{@deploy.environment} by #{@deploy.username}"
12 }) 22 })
13 end 23 end
14 24
app/models/deploy.rb
@@ -9,6 +9,18 @@ class Deploy @@ -9,6 +9,18 @@ class Deploy
9 9
10 embedded_in :project, :inverse_of => :deploys 10 embedded_in :project, :inverse_of => :deploys
11 11
  12 + after_create :deliver_notification, :if => :should_notify?
  13 +
12 validates_presence_of :username, :environment 14 validates_presence_of :username, :environment
13 15
  16 + def deliver_notification
  17 + Mailer.deploy_notification(self).deliver
  18 + end
  19 +
  20 + protected
  21 +
  22 + def should_notify?
  23 + project.watchers.any?
  24 + end
  25 +
14 end 26 end
app/models/err.rb
@@ -19,7 +19,7 @@ class Err @@ -19,7 +19,7 @@ class Err
19 19
20 def self.for(attrs) 20 def self.for(attrs)
21 project = attrs.delete(:project) 21 project = attrs.delete(:project)
22 - project.errs.unresolved.where(attrs).first || project.errs.create(attrs) 22 + project.errs.unresolved.where(attrs).first || project.errs.create!(attrs)
23 end 23 end
24 24
25 def resolve! 25 def resolve!
app/models/notice.rb
@@ -28,7 +28,7 @@ class Notice @@ -28,7 +28,7 @@ class Notice
28 :environment => hoptoad_notice['server-environment']['environment-name'] 28 :environment => hoptoad_notice['server-environment']['environment-name']
29 }) 29 })
30 30
31 - error.notices.create({ 31 + error.notices.create!({
32 :backtrace => hoptoad_notice['error']['backtrace']['line'], 32 :backtrace => hoptoad_notice['error']['backtrace']['line'],
33 :server_environment => hoptoad_notice['server-environment'], 33 :server_environment => hoptoad_notice['server-environment'],
34 :request => hoptoad_notice['request'], 34 :request => hoptoad_notice['request'],
app/views/mailer/_signature.text.erb 0 → 100644
@@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
  1 +All Glory,
  2 +Hypnotoad
0 \ No newline at end of file 3 \ No newline at end of file
app/views/mailer/deploy_notification.text.erb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +<%= @project.name %> was just deployed to <%= @deploy.environment %> by <%= @deploy.username %>.
  2 +
  3 +Details:
  4 +
  5 + What: <%= @project.name %><%= "@#{@deploy.revision}" unless @deploy.revision.blank? %>
  6 + When: <%= @deploy.created_at.to_s %>
  7 + From: <%= @deploy.repository.blank? ? 'n/a' : @deploy.repository %>
  8 +
  9 +<%= render :partial => 'signature' %>
0 \ No newline at end of file 10 \ No newline at end of file
app/views/mailer/error_notification.text.erb
@@ -4,5 +4,4 @@ This error has occurred &lt;%= pluralize @notice.err.notices.count, &#39;time&#39; %&gt;. You @@ -4,5 +4,4 @@ This error has occurred &lt;%= pluralize @notice.err.notices.count, &#39;time&#39; %&gt;. You
4 4
5 <%= error_notice_url(@notice.err, @notice) %> 5 <%= error_notice_url(@notice.err, @notice) %>
6 6
7 -Your loyal servant,  
8 -Hypnotoad  
9 \ No newline at end of file 7 \ No newline at end of file
  8 +<%= render :partial => 'signature' %>
10 \ No newline at end of file 9 \ No newline at end of file
config/routes.rb
@@ -2,9 +2,10 @@ Hypnotoad::Application.routes.draw do @@ -2,9 +2,10 @@ Hypnotoad::Application.routes.draw do
2 2
3 # Hoptoad Notifier Routes 3 # Hoptoad Notifier Routes
4 match '/notifier_api/v2/notices' => 'notices#create' 4 match '/notifier_api/v2/notices' => 'notices#create'
5 - # match '/deploys.txt' => 'deploys#create' 5 + match '/deploys.txt' => 'deploys#create'
6 6
7 - resources :notices 7 + resources :notices, :only => [:show]
  8 + resources :deploys, :only => [:show]
8 resources :errors do 9 resources :errors do
9 resources :notices 10 resources :notices
10 end 11 end
spec/controllers/deploys_controller_spec.rb 0 → 100644
@@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
  1 +require 'spec_helper'
  2 +
  3 +describe DeploysController do
  4 +
  5 + context 'POST #create' do
  6 + before do
  7 + @params = {
  8 + 'local_username' => 'john.doe',
  9 + 'scm_repository' => 'git@github.com/jdpace/hypnotoad.git',
  10 + 'rails_env' => 'production',
  11 + 'scm_revision' => '19d77837eef37902cf5df7e4445c85f392a8d0d5'
  12 + }
  13 + @project = Factory(:project_with_watcher, :api_key => 'ALLGLORYTOTHEHYPNOTOAD')
  14 + end
  15 +
  16 + it 'finds the project via the api key' do
  17 + Project.should_receive(:find_by_api_key!).with('ALLGLORYTOTHEHYPNOTOAD').and_return(@project)
  18 + post :create, :deploy => @params, :api_key => 'ALLGLORYTOTHEHYPNOTOAD'
  19 + end
  20 +
  21 + it 'creates a deploy' do
  22 + Project.stub(:find_by_api_key!).and_return(@project)
  23 + @project.deploys.should_receive(:create!).
  24 + with({
  25 + :username => 'john.doe',
  26 + :environment => 'production',
  27 + :repository => 'git@github.com/jdpace/hypnotoad.git',
  28 + :revision => '19d77837eef37902cf5df7e4445c85f392a8d0d5'
  29 + }).and_return(Factory(:deploy))
  30 + post :create, :deploy => @params, :api_key => 'ALLGLORYTOTHEHYPNOTOAD'
  31 + end
  32 +
  33 + it 'sends an email notification', :focused => true do
  34 + post :create, :deploy => @params, :api_key => 'ALLGLORYTOTHEHYPNOTOAD'
  35 + email = ActionMailer::Base.deliveries.last
  36 + email.to.should include(@project.watchers.first.email)
  37 + email.subject.should == "[#{@project.name}] Deployed to production by john.doe"
  38 + end
  39 +
  40 + end
  41 +
  42 +end
0 \ No newline at end of file 43 \ No newline at end of file
spec/controllers/notices_controller_spec.rb
@@ -5,7 +5,8 @@ describe NoticesController do @@ -5,7 +5,8 @@ describe NoticesController do
5 context 'POST[XML] notices#create' do 5 context 'POST[XML] notices#create' do
6 before do 6 before do
7 @xml = Rails.root.join('spec','fixtures','hoptoad_test_notice.xml').read 7 @xml = Rails.root.join('spec','fixtures','hoptoad_test_notice.xml').read
8 - Project.stub(:find_by_api_key!).and_return(Factory.build(:project)) 8 + @project = Factory(:project_with_watcher)
  9 + Project.stub(:find_by_api_key!).and_return(@project)
9 @notice = Notice.from_xml(@xml) 10 @notice = Notice.from_xml(@xml)
10 11
11 request.env['Content-type'] = 'text/xml' 12 request.env['Content-type'] = 'text/xml'
@@ -17,6 +18,13 @@ describe NoticesController do @@ -17,6 +18,13 @@ describe NoticesController do
17 Notice.should_receive(:from_xml).with(@xml).and_return(@notice) 18 Notice.should_receive(:from_xml).with(@xml).and_return(@notice)
18 post :create 19 post :create
19 end 20 end
  21 +
  22 + it "sends a notification email" do
  23 + post :create
  24 + email = ActionMailer::Base.deliveries.last
  25 + email.to.should include(@project.watchers.first.email)
  26 + email.subject.should include(@notice.err.message)
  27 + end
20 end 28 end
21 29
22 end 30 end
spec/factories/project_factories.rb
@@ -5,6 +5,12 @@ Factory.define(:project) do |p| @@ -5,6 +5,12 @@ Factory.define(:project) do |p|
5 p.name { Factory.next :project_name } 5 p.name { Factory.next :project_name }
6 end 6 end
7 7
  8 +Factory.define(:project_with_watcher, :parent => :project) do |p|
  9 + p.after_create {|project|
  10 + Factory(:watcher, :project => project)
  11 + }
  12 +end
  13 +
8 Factory.define(:watcher) do |w| 14 Factory.define(:watcher) do |w|
9 w.project {|p| p.association :project} 15 w.project {|p| p.association :project}
10 w.email { Factory.next :email } 16 w.email { Factory.next :email }
spec/models/deploy_spec.rb
@@ -16,4 +16,12 @@ describe Deploy do @@ -16,4 +16,12 @@ describe Deploy do
16 end 16 end
17 end 17 end
18 18
  19 + context 'being created' do
  20 + it 'should send an email notification' do
  21 + Mailer.should_receive(:deploy_notification).
  22 + and_return(mock('email', :deliver => true))
  23 + Factory(:deploy, :project => Factory(:project_with_watcher))
  24 + end
  25 + end
  26 +
19 end 27 end
spec/models/notice_spec.rb
@@ -42,7 +42,7 @@ describe Notice do @@ -42,7 +42,7 @@ describe Notice do
42 :action => 'verify', 42 :action => 'verify',
43 :environment => 'development' 43 :environment => 'development'
44 }).and_return(err = Err.new) 44 }).and_return(err = Err.new)
45 - err.notices.stub(:create) 45 + err.notices.stub(:create!)
46 @notice = Notice.from_xml(@xml) 46 @notice = Notice.from_xml(@xml)
47 end 47 end
48 48
@@ -81,8 +81,8 @@ describe Notice do @@ -81,8 +81,8 @@ describe Notice do
81 81
82 describe "email notifications" do 82 describe "email notifications" do
83 before do 83 before do
84 - @watcher = Factory(:watcher)  
85 - @error = Factory(:err, :project => @watcher.project) 84 + @project = Factory(:project_with_watcher)
  85 + @error = Factory(:err, :project => @project)
86 end 86 end
87 87
88 App.email_at_notices.each do |threshold| 88 App.email_at_notices.each do |threshold|