From 34cbc51e238a6b1626ef3d656b2dc80db6463971 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 25 Aug 2016 20:01:10 +0800 Subject: Add a new pipeline email service. TODO: email templates and tests --- app/mailers/emails/pipelines.rb | 38 +++++++++ app/mailers/notify.rb | 1 + .../project_services/pipelines_email_service.rb | 93 ++++++++++++++++++++++ app/workers/pipeline_email_worker.rb | 39 +++++++++ 4 files changed, 171 insertions(+) create mode 100644 app/mailers/emails/pipelines.rb create mode 100644 app/models/project_services/pipelines_email_service.rb create mode 100644 app/workers/pipeline_email_worker.rb (limited to 'app') diff --git a/app/mailers/emails/pipelines.rb b/app/mailers/emails/pipelines.rb new file mode 100644 index 00000000000..7fdba850219 --- /dev/null +++ b/app/mailers/emails/pipelines.rb @@ -0,0 +1,38 @@ +module Emails + module Pipelines + def pipeline_succeeded_email(params, to) + pipeline_mail(params, to, 'succeeded') # TODO: missing template + end + + def pipeline_failed_email(params, to) + pipeline_mail(params, to, 'failed') # TODO: missing template + end + + private + + def pipeline_mail(params, to, status) + @params = params + add_headers + + mail(to: to, subject: pipeline_subject('failed')) + end + + def add_headers + @project = @params.project # `add_project_headers` needs this + add_project_headers + add_pipeline_headers(@params.pipeline) + end + + def add_pipeline_headers(pipeline) + headers['X-GitLab-Pipeline-Id'] = pipeline.id + headers['X-GitLab-Pipeline-Ref'] = pipeline.ref + headers['X-GitLab-Pipeline-Status'] = pipeline.status + end + + def pipeline_subject(status) + subject( + "Pipeline #{status} for #{@params.project.name}", + @params.pipeline.short_sha) + end + end +end diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index 0cc709f68e4..fef8149b325 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -7,6 +7,7 @@ class Notify < BaseMailer include Emails::Projects include Emails::Profile include Emails::Builds + include Emails::Pipelines include Emails::Members add_template_helper MergeRequestsHelper diff --git a/app/models/project_services/pipelines_email_service.rb b/app/models/project_services/pipelines_email_service.rb new file mode 100644 index 00000000000..1f852445c1c --- /dev/null +++ b/app/models/project_services/pipelines_email_service.rb @@ -0,0 +1,93 @@ +class PipelinesEmailService < Service + prop_accessor :recipients + boolean_accessor :add_pusher + boolean_accessor :notify_only_broken_pipelines + validates :recipients, + presence: true, + if: ->(s) { s.activated? && !s.add_pusher? } + + def initialize_properties + self.properties ||= { notify_only_broken_builds: true } + end + + def title + 'Pipelines emails' + end + + def description + 'Email the pipelines status to a list of recipients.' + end + + def to_param + 'pipelines_email' + end + + def supported_events + %w[pipeline] + end + + def execute(data, force = false) + return unless supported_events.include?(data[:object_kind]) + return unless force || should_build_be_notified?(data) + + all_recipients = retrieve_recipients(data) + + return unless all_recipients.any? + + PipelineEmailWorker.perform_async(data, all_recipients) + end + + def can_test? + project.pipelines.count > 0 + end + + def disabled_title + 'Please setup a pipeline on your repository.' + end + + def test_data(project, user) + data = Gitlab::DataBuilder::Pipeline.build(project.pipelines.last) + data[:user] = user.hook_attrs + data + end + + def fields + [ + { type: 'textarea', + name: 'recipients', + placeholder: 'Emails separated by comma' }, + { type: 'checkbox', + name: 'add_pusher', + label: 'Add pusher to recipients list' }, + { type: 'checkbox', + name: 'notify_only_broken_pipelines' }, + ] + end + + def test(data) + result = execute(data, true) + + { success: true, result: result } + rescue StandardError => error + { success: false, result: error } + end + + def should_build_be_notified?(data) + case data[:object_attributes][:status] + when 'success' + !notify_only_broken_pipelines? + else + false + end + end + + def retrieve_recipients(data) + all_recipients = recipients.to_s.split(',').reject(&:blank?) + + if add_pusher? && data[:user].try(:[], :email) + all_recipients << data[:user][:email] + else + all_recipients + end + end +end diff --git a/app/workers/pipeline_email_worker.rb b/app/workers/pipeline_email_worker.rb new file mode 100644 index 00000000000..2160207c901 --- /dev/null +++ b/app/workers/pipeline_email_worker.rb @@ -0,0 +1,39 @@ +class PipelineEmailWorker + include Sidekiq::Worker + + ParamsStruct = Struct.new(:pipeline, :project, :email_template) + class Params < ParamsStruct + def initialize(pipeline_id) + self.pipeline = Ci::Pipeline.find(pipeline_id) + self.project = pipeline.project + self.email_template = case pipeline.status + when 'success' + :pipeline_succeeded_email + when 'failed' + :pipeline_failed_email + end + end + end + + def perform(data, recipients) + params = Params.new(data['object_attributes']['id']) + + return unless params.email_template + + recipients.each do |to| + deliver(params, to) do + Notify.public_send(params.email_template, params, to).deliver_now + end + end + end + + private + + def deliver(params, to) + yield + # These are input errors and won't be corrected even if Sidekiq retries + rescue Net::SMTPFatalError, Net::SMTPSyntaxError => e + project_name = params.project.path_with_namespace + logger.info("Failed to send email for #{project_name} to #{to}: #{e}") + end +end -- cgit v1.2.3