diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb index 0e40450bfeed47f29c7786b86f2c0ed59cdc5c03..df21d7b5b0245150a872de299a0c7d27c381d046 100644 --- a/app/mailers/emails/projects.rb +++ b/app/mailers/emails/projects.rb @@ -13,5 +13,15 @@ module Emails mail(to: @user.email, subject: subject("Project was moved")) end + + def repository_push_email(project_id, recipient, author_id, branch, compare) + @project = Project.find(project_id) + @author = User.find(author_id) + @commits = Commit.decorate(compare.commits) + @diffs = compare.diffs + @branch = branch + + mail(to: recipient, subject: subject("New push to repository")) + end end end diff --git a/app/models/project.rb b/app/models/project.rb index 1bfc27d723df0c014719ffb8990cddeb8f5f5112..51f7e9495351d80c519b80d0a39121f3003c320f 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -48,6 +48,7 @@ class Project < ActiveRecord::Base has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event', foreign_key: 'project_id' has_one :gitlab_ci_service, dependent: :destroy has_one :campfire_service, dependent: :destroy + has_one :emails_on_push_service, dependent: :destroy has_one :pivotaltracker_service, dependent: :destroy has_one :hipchat_service, dependent: :destroy has_one :flowdock_service, dependent: :destroy @@ -237,7 +238,7 @@ class Project < ActiveRecord::Base end def available_services_names - %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla) + %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push) end def gitlab_ci? diff --git a/app/models/assembla_service.rb b/app/models/project_services/assembla_service.rb similarity index 100% rename from app/models/assembla_service.rb rename to app/models/project_services/assembla_service.rb diff --git a/app/models/campfire_service.rb b/app/models/project_services/campfire_service.rb similarity index 100% rename from app/models/campfire_service.rb rename to app/models/project_services/campfire_service.rb diff --git a/app/models/project_services/emails_on_push_service.rb b/app/models/project_services/emails_on_push_service.rb new file mode 100644 index 0000000000000000000000000000000000000000..2a46eff7846eddcfc7d32014464e4d7f743ff074 --- /dev/null +++ b/app/models/project_services/emails_on_push_service.rb @@ -0,0 +1,44 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# token :string(255) +# project_id :integer not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean default(FALSE), not null +# project_url :string(255) +# subdomain :string(255) +# room :string(255) +# + +class EmailsOnPushService < Service + attr_accessible :recipients + + validates :recipients, presence: true, if: :activated? + + def title + 'Emails on push' + end + + def description + 'Email the commits and diff of each push to a list of recipients.' + end + + def to_param + 'emails_on_push' + end + + def execute(push_data) + EmailsOnPushWorker.perform_async(project_id, recipients, push_data) + end + + def fields + [ + { type: 'textarea', name: 'recipients', placeholder: 'Emails separated by whitespace' }, + ] + end +end diff --git a/app/models/flowdock_service.rb b/app/models/project_services/flowdock_service.rb similarity index 100% rename from app/models/flowdock_service.rb rename to app/models/project_services/flowdock_service.rb diff --git a/app/models/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb similarity index 100% rename from app/models/gitlab_ci_service.rb rename to app/models/project_services/gitlab_ci_service.rb diff --git a/app/models/hipchat_service.rb b/app/models/project_services/hipchat_service.rb similarity index 100% rename from app/models/hipchat_service.rb rename to app/models/project_services/hipchat_service.rb diff --git a/app/models/pivotaltracker_service.rb b/app/models/project_services/pivotaltracker_service.rb similarity index 100% rename from app/models/pivotaltracker_service.rb rename to app/models/project_services/pivotaltracker_service.rb diff --git a/app/views/notify/repository_push_email.html.haml b/app/views/notify/repository_push_email.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..d0b30c08338d43ee2047ee82b6323fcd8d42015f --- /dev/null +++ b/app/views/notify/repository_push_email.html.haml @@ -0,0 +1,23 @@ +%h3 #{@author.name} pushed to #{@branch} at #{@project.name_with_namespace} + +%h4 Commits: + +%ul + - @commits.each do |commit| + %li + #{commit.short_id} - #{commit.title} + +%h4 Diff: +- @diffs.each do |diff| + %li + %strong + - if diff.old_path == diff.new_path + = diff.new_path + - elsif diff.new_path && diff.old_path + #{diff.old_path} → #{diff.new_path} + - else + = diff.new_path || diff.old_path + %hr + %pre + = diff.diff + %br diff --git a/app/views/notify/repository_push_email.text.haml b/app/views/notify/repository_push_email.text.haml new file mode 100644 index 0000000000000000000000000000000000000000..6718ca683598c104efa846bc1ab60429ca7b7f65 --- /dev/null +++ b/app/views/notify/repository_push_email.text.haml @@ -0,0 +1,20 @@ +#{@author.name} pushed to #{@branch} at #{@project.name_with_namespace} + +\ +Commits: +- @commits.each do |commit| + #{commit.short_id} - #{truncate(commit.title, length: 40)} +\ +\ +Diff: +- @diffs.each do |diff| + \ + \===================================== + - if diff.old_path == diff.new_path + = diff.new_path + - elsif diff.new_path && diff.old_path + #{diff.old_path} → #{diff.new_path} + - else + = diff.new_path || diff.old_path + \===================================== + = diff.diff diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index 202bf3272176e3aa88ed6afe6755d338012e51b7..34ba55fc4cddb874d45fefa4b3bef0fe8e054867 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -33,6 +33,8 @@ .controls - if type == 'text' = f.text_field name, class: "input-xlarge", placeholder: placeholder + - elsif type == 'textarea' + = f.text_area name, rows: 5, class: "input-xxlarge", placeholder: placeholder - elsif type == 'checkbox' = f.check_box name diff --git a/app/views/projects/services/index.html.haml b/app/views/projects/services/index.html.haml index 190aa69dab7f5f8a1c7a1a83131fe83758e3ab7c..9543d97b7f7a059015be95960a60b94c2d72ca17 100644 --- a/app/views/projects/services/index.html.haml +++ b/app/views/projects/services/index.html.haml @@ -3,7 +3,7 @@ %hr %ul.bordered-list - - @services.each do |service| + - @services.sort_by(&:title).each do |service| %li %h4 = link_to edit_project_service_path(@project, service.to_param) do diff --git a/app/workers/emails_on_push_worker.rb b/app/workers/emails_on_push_worker.rb new file mode 100644 index 0000000000000000000000000000000000000000..9982b362a1043a4b9af17503ae5fad99703c9fa1 --- /dev/null +++ b/app/workers/emails_on_push_worker.rb @@ -0,0 +1,25 @@ +class EmailsOnPushWorker + include Sidekiq::Worker + + def perform(project_id, recipients, push_data) + project = Project.find(project_id) + before_sha = push_data["before"] + after_sha = push_data["after"] + branch = push_data["ref"] + author_id = push_data["user_id"] + + if before_sha =~ /^000000/ || after_sha =~ /^000000/ + # skip if new branch was pushed or branch was removed + return true + end + + compare = Gitlab::Git::Compare.new(project.repository.raw_repository, before_sha, after_sha) + + # Do not send emails if git compare failed + return false unless compare && compare.commits.present? + + recipients.split(" ").each do |recipient| + Notify.delay.repository_push_email(project_id, recipient, author_id, branch, compare) + end + end +end diff --git a/config/application.rb b/config/application.rb index dec1940a4a3a8dda7492c3c1dd1fccee9588328d..1c91134f52408bfaef3cf23674b611d75efdce06 100644 --- a/config/application.rb +++ b/config/application.rb @@ -12,7 +12,7 @@ module Gitlab # -- all .rb files in that directory are automatically loaded. # Custom directories with classes and modules you want to be autoloadable. - config.autoload_paths += %W(#{config.root}/lib #{config.root}/app/models/concerns) + config.autoload_paths += %W(#{config.root}/lib #{config.root}/app/models/concerns #{config.root}/app/models/project_services) # Only load the plugins named here, in the order given (default is alphabetical). # :all can be used as a placeholder for all plugins not explicitly named. diff --git a/db/migrate/20131217102743_add_recipients_to_service.rb b/db/migrate/20131217102743_add_recipients_to_service.rb new file mode 100644 index 0000000000000000000000000000000000000000..9695c25135202edacea014e237179a4fb1ee62fa --- /dev/null +++ b/db/migrate/20131217102743_add_recipients_to_service.rb @@ -0,0 +1,5 @@ +class AddRecipientsToService < ActiveRecord::Migration + def change + add_column :services, :recipients, :text + end +end diff --git a/db/schema.rb b/db/schema.rb index 77d245913e248f608ac57e98adebfc73dea47f8b..cda5c3cf9460b8912e034327779064dade8e595f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20131214224427) do +ActiveRecord::Schema.define(version: 20131217102743) do create_table "broadcast_messages", force: true do |t| t.text "message", null: false @@ -219,6 +219,7 @@ ActiveRecord::Schema.define(version: 20131214224427) do t.string "project_url" t.string "subdomain" t.string "room" + t.text "recipients" end add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree diff --git a/features/project/service.feature b/features/project/service.feature index f8684f3b3b703a2ae92454271f3b2edf61c73451..46b983e8f9a36d0312d47357b892dafd3dec915b 100644 --- a/features/project/service.feature +++ b/features/project/service.feature @@ -35,4 +35,10 @@ Feature: Project Services When I visit project "Shop" services page And I click Assembla service link And I fill Assembla settings - Then I should see Assembla service settings saved \ No newline at end of file + Then I should see Assembla service settings saved + + Scenario: Activate email on push service + When I visit project "Shop" services page + And I click email on push service link + And I fill email on push settings + Then I should see email on push service settings saved diff --git a/features/steps/project/project_services.rb b/features/steps/project/project_services.rb index 2f248090831effeb26ea8f4aeb9a88f0b37fd5a2..549968dce55416b8d41c893afba25d775a7b7e18 100644 --- a/features/steps/project/project_services.rb +++ b/features/steps/project/project_services.rb @@ -3,11 +3,11 @@ class ProjectServices < Spinach::FeatureSteps include SharedProject include SharedPaths - When 'I visit project "Shop" services page' do + step 'I visit project "Shop" services page' do visit project_services_path(@project) end - Then 'I should see list of available services' do + step 'I should see list of available services' do page.should have_content 'Services' page.should have_content 'Campfire' page.should have_content 'Hipchat' @@ -15,76 +15,89 @@ class ProjectServices < Spinach::FeatureSteps page.should have_content 'Assembla' end - And 'I click gitlab-ci service link' do + step 'I click gitlab-ci service link' do click_link 'GitLab CI' end - And 'I fill gitlab-ci settings' do + step 'I fill gitlab-ci settings' do check 'Active' fill_in 'Project url', with: 'http://ci.gitlab.org/projects/3' fill_in 'Token', with: 'verySecret' click_button 'Save' end - Then 'I should see service settings saved' do + step 'I should see service settings saved' do find_field('Project url').value.should == 'http://ci.gitlab.org/projects/3' end - And 'I click hipchat service link' do + step 'I click hipchat service link' do click_link 'Hipchat' end - And 'I fill hipchat settings' do + step 'I fill hipchat settings' do check 'Active' fill_in 'Room', with: 'gitlab' fill_in 'Token', with: 'verySecret' click_button 'Save' end - Then 'I should see hipchat service settings saved' do + step 'I should see hipchat service settings saved' do find_field('Room').value.should == 'gitlab' end - And 'I click pivotaltracker service link' do + step 'I click pivotaltracker service link' do click_link 'PivotalTracker' end - And 'I fill pivotaltracker settings' do + step 'I fill pivotaltracker settings' do check 'Active' fill_in 'Token', with: 'verySecret' click_button 'Save' end - Then 'I should see pivotaltracker service settings saved' do + step 'I should see pivotaltracker service settings saved' do find_field('Token').value.should == 'verySecret' end - And 'I click Flowdock service link' do + step 'I click Flowdock service link' do click_link 'Flowdock' end - And 'I fill Flowdock settings' do + step 'I fill Flowdock settings' do check 'Active' fill_in 'Token', with: 'verySecret' click_button 'Save' end - Then 'I should see Flowdock service settings saved' do + step 'I should see Flowdock service settings saved' do find_field('Token').value.should == 'verySecret' end - And 'I click Assembla service link' do + step 'I click Assembla service link' do click_link 'Assembla' end - And 'I fill Assembla settings' do + step 'I fill Assembla settings' do check 'Active' fill_in 'Token', with: 'verySecret' click_button 'Save' end - Then 'I should see Assembla service settings saved' do + step 'I should see Assembla service settings saved' do find_field('Token').value.should == 'verySecret' end + + step 'I click email on push service link' do + click_link 'Emails on push' + end + + step 'I fill email on push settings' do + fill_in 'Recipients', with: 'qa@company.name' + click_button 'Save' + end + + step 'I should see email on push service settings saved' do + find_field('Recipients').value.should == 'qa@company.name' + end end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index d287239cfe3eb0a915e23753fb97a6c71776bea7..b1e5348681686452639f0bb1e9a3d9947631872d 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -391,4 +391,28 @@ describe Notify do should have_body_text /#{example_site_path}/ end end + + describe 'email on push' do + let(:example_site_path) { root_path } + let(:user) { create(:user) } + let(:compare) { Gitlab::Git::Compare.new(project.repository.raw_repository, 'cd5c4bac', 'b1e6a9db') } + + subject { Notify.repository_push_email(project.id, 'devs@company.name', user.id, 'master', compare) } + + it 'is sent to recipient' do + should deliver_to 'devs@company.name' + end + + it 'has the correct subject' do + should have_subject /New push to repository/ + end + + it 'includes commits list' do + should have_body_text /tree css fixes/ + end + + it 'includes diffs' do + should have_body_text /Checkout wiki pages for installation information/ + end + end end