Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/diffs/components/compare_versions_dropdown.vue7
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_bundle.js4
-rw-r--r--app/assets/javascripts/profile/account/components/update_username.vue10
-rw-r--r--app/assets/javascripts/profile/gl_crop.js10
-rw-r--r--app/assets/stylesheets/utilities.scss1
-rw-r--r--app/mailers/emails/pipelines.rb4
-rw-r--r--app/mailers/previews/notify_preview.rb4
-rw-r--r--app/models/ci/pipeline.rb10
-rw-r--r--app/models/ci/ref.rb23
-rw-r--r--app/models/notification_recipient.rb11
-rw-r--r--app/models/notification_setting.rb14
-rw-r--r--app/models/project.rb1
-rw-r--r--app/models/project_services/pipelines_email_service.rb2
-rw-r--r--app/services/ci/update_ci_ref_status_service.rb65
-rw-r--r--app/services/merge_requests/create_pipeline_service.rb23
-rw-r--r--app/services/notification_service.rb7
-rw-r--r--app/views/notify/_successful_pipeline.html.haml118
-rw-r--r--app/views/notify/_successful_pipeline.text.erb32
-rw-r--r--app/views/notify/pipeline_fixed_email.html.haml1
-rw-r--r--app/views/notify/pipeline_fixed_email.text.erb1
-rw-r--r--app/views/notify/pipeline_success_email.html.haml118
-rw-r--r--app/views/notify/pipeline_success_email.text.erb33
-rw-r--r--app/workers/all_queues.yml7
-rw-r--r--app/workers/pipeline_notification_worker.rb14
-rw-r--r--app/workers/pipeline_update_ci_ref_status_worker.rb17
25 files changed, 355 insertions, 182 deletions
diff --git a/app/assets/javascripts/diffs/components/compare_versions_dropdown.vue b/app/assets/javascripts/diffs/components/compare_versions_dropdown.vue
index 1dcdb65d5c7..cc4b2dacab3 100644
--- a/app/assets/javascripts/diffs/components/compare_versions_dropdown.vue
+++ b/app/assets/javascripts/diffs/components/compare_versions_dropdown.vue
@@ -1,6 +1,7 @@
<script>
import Icon from '~/vue_shared/components/icon.vue';
import { n__, __, sprintf } from '~/locale';
+import { getParameterByName, parseBoolean } from '~/lib/utils/common_utils';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
export default {
@@ -94,6 +95,9 @@ export default {
}
return version.versionIndex === -1;
},
+ isHead() {
+ return parseBoolean(getParameterByName('diff_head'));
+ },
isLatest(version) {
return (
this.mergeRequestVersion && version.version_index === this.targetVersions[0].version_index
@@ -121,7 +125,8 @@ export default {
<div>
<strong>
{{ versionName(version) }}
- <template v-if="isBase(version)">{{
+ <template v-if="isHead()">{{ s__('DiffsCompareBaseBranch|(HEAD)') }}</template>
+ <template v-else-if="isBase(version)">{{
s__('DiffsCompareBaseBranch|(base)')
}}</template>
</strong>
diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
index ffcb0f24cc6..c901971be50 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
@@ -134,6 +134,10 @@ export default () => {
axios
.get(dataset.testReportsCountEndpoint)
.then(({ data }) => {
+ if (!data.total_count) {
+ return;
+ }
+
document.querySelector('.js-test-report-badge-counter').innerHTML = data.total_count;
})
.catch(() => {});
diff --git a/app/assets/javascripts/profile/account/components/update_username.vue b/app/assets/javascripts/profile/account/components/update_username.vue
index 72867ecd709..fa09e063552 100644
--- a/app/assets/javascripts/profile/account/components/update_username.vue
+++ b/app/assets/javascripts/profile/account/components/update_username.vue
@@ -1,5 +1,5 @@
<script>
-import _ from 'underscore';
+import { escape as esc } from 'lodash';
import axios from '~/lib/utils/axios_utils';
import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue';
import { s__, sprintf } from '~/locale';
@@ -43,10 +43,10 @@ You are going to change the username %{currentUsernameBold} to %{newUsernameBold
Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group.
Please update your Git repository remotes as soon as possible.`),
{
- currentUsernameBold: `<strong>${_.escape(this.username)}</strong>`,
- newUsernameBold: `<strong>${_.escape(this.newUsername)}</strong>`,
- currentUsername: _.escape(this.username),
- newUsername: _.escape(this.newUsername),
+ currentUsernameBold: `<strong>${esc(this.username)}</strong>`,
+ newUsernameBold: `<strong>${esc(this.newUsername)}</strong>`,
+ currentUsername: esc(this.username),
+ newUsername: esc(this.newUsername),
},
false,
);
diff --git a/app/assets/javascripts/profile/gl_crop.js b/app/assets/javascripts/profile/gl_crop.js
index 880e1a88975..55bc9fb8955 100644
--- a/app/assets/javascripts/profile/gl_crop.js
+++ b/app/assets/javascripts/profile/gl_crop.js
@@ -2,7 +2,7 @@
import $ from 'jquery';
import 'cropper';
-import _ from 'underscore';
+import { isString } from 'lodash';
(() => {
// Matches everything but the file name
@@ -29,7 +29,7 @@ import _ from 'underscore';
this.onModalShow = this.onModalShow.bind(this);
this.onPickImageClick = this.onPickImageClick.bind(this);
this.fileInput = $(input);
- this.modalCropImg = _.isString(this.modalCropImg) ? $(this.modalCropImg) : this.modalCropImg;
+ this.modalCropImg = isString(this.modalCropImg) ? $(this.modalCropImg) : this.modalCropImg;
this.fileInput
.attr('name', `${this.fileInput.attr('name')}-trigger`)
.attr('id', `${this.fileInput.attr('id')}-trigger`);
@@ -47,9 +47,9 @@ import _ from 'underscore';
this.filename = this.getElement(filename);
this.previewImage = this.getElement(previewImage);
this.pickImageEl = this.getElement(pickImageEl);
- this.modalCrop = _.isString(modalCrop) ? $(modalCrop) : modalCrop;
- this.uploadImageBtn = _.isString(uploadImageBtn) ? $(uploadImageBtn) : uploadImageBtn;
- this.modalCropImg = _.isString(modalCropImg) ? $(modalCropImg) : modalCropImg;
+ this.modalCrop = isString(modalCrop) ? $(modalCrop) : modalCrop;
+ this.uploadImageBtn = isString(uploadImageBtn) ? $(uploadImageBtn) : uploadImageBtn;
+ this.modalCropImg = isString(modalCropImg) ? $(modalCropImg) : modalCropImg;
this.cropActionsBtn = this.modalCrop.find('[data-method]');
this.bindEvents();
}
diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss
index e27ec571531..dabbcf0eac1 100644
--- a/app/assets/stylesheets/utilities.scss
+++ b/app/assets/stylesheets/utilities.scss
@@ -65,6 +65,7 @@
// Classes using mixins coming from @gitlab-ui
// can be removed once https://gitlab.com/gitlab-org/gitlab/merge_requests/19021 has been merged
+.gl-bg-blue-50 { @include gl-bg-blue-50; }
.gl-bg-red-100 { @include gl-bg-red-100; }
.gl-bg-orange-100 { @include gl-bg-orange-100; }
.gl-bg-gray-100 { @include gl-bg-gray-100; }
diff --git a/app/mailers/emails/pipelines.rb b/app/mailers/emails/pipelines.rb
index 773b9fead3a..f2538d28a1a 100644
--- a/app/mailers/emails/pipelines.rb
+++ b/app/mailers/emails/pipelines.rb
@@ -10,6 +10,10 @@ module Emails
pipeline_mail(pipeline, recipients, 'failed')
end
+ def pipeline_fixed_email(pipeline, recipients)
+ pipeline_mail(pipeline, recipients, 'been fixed')
+ end
+
private
def pipeline_mail(pipeline, recipients, status)
diff --git a/app/mailers/previews/notify_preview.rb b/app/mailers/previews/notify_preview.rb
index 381a4f54d9e..114737eb232 100644
--- a/app/mailers/previews/notify_preview.rb
+++ b/app/mailers/previews/notify_preview.rb
@@ -145,6 +145,10 @@ class NotifyPreview < ActionMailer::Preview
Notify.pipeline_failed_email(pipeline, pipeline.user.try(:email))
end
+ def pipeline_fixed_email
+ Notify.pipeline_fixed_email(pipeline, pipeline.user.try(:email))
+ end
+
def autodevops_disabled_email
Notify.autodevops_disabled_email(pipeline, user.email).message
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 869a2e8da20..e07abc20dcf 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -63,6 +63,14 @@ module Ci
has_many :sourced_pipelines, class_name: 'Ci::Sources::Pipeline', foreign_key: :source_pipeline_id
has_one :source_pipeline, class_name: 'Ci::Sources::Pipeline', inverse_of: :pipeline
+
+ has_one :ref_status, ->(pipeline) {
+ # We use .read_attribute to save 1 extra unneeded query to load the :project.
+ unscope(:where)
+ .where(project_id: pipeline.read_attribute(:project_id), ref: pipeline.ref, tag: pipeline.tag)
+ # Sadly :inverse_of is not supported (yet) by Rails for composite PKs.
+ }, class_name: 'Ci::Ref', inverse_of: :pipelines
+
has_one :chat_data, class_name: 'Ci::PipelineChatData'
has_many :triggered_pipelines, through: :sourced_pipelines, source: :pipeline
@@ -227,7 +235,7 @@ module Ci
after_transition any => [:success, :failed] do |pipeline|
pipeline.run_after_commit do
- PipelineNotificationWorker.perform_async(pipeline.id)
+ PipelineUpdateCiRefStatusWorker.perform_async(pipeline.id)
end
end
diff --git a/app/models/ci/ref.rb b/app/models/ci/ref.rb
new file mode 100644
index 00000000000..a0782bc0444
--- /dev/null
+++ b/app/models/ci/ref.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Ci
+ class Ref < ApplicationRecord
+ extend Gitlab::Ci::Model
+
+ STATUSES = %w[success failed fixed].freeze
+
+ belongs_to :project
+ belongs_to :last_updated_by_pipeline, foreign_key: :last_updated_by_pipeline_id, class_name: 'Ci::Pipeline'
+ # ActiveRecord doesn't support composite FKs for this reason we have to do the 'unscope(:where)'
+ # hack.
+ has_many :pipelines, ->(ref) {
+ # We use .read_attribute to save 1 extra unneeded query to load the :project.
+ unscope(:where)
+ .where(ref: ref.ref, project_id: ref.read_attribute(:project_id), tag: ref.tag)
+ # Sadly :inverse_of is not supported (yet) by Rails for composite PKs.
+ }, inverse_of: :ref_status
+
+ validates :status, inclusion: { in: STATUSES }
+ validates :last_updated_by_pipeline, presence: true
+ end
+end
diff --git a/app/models/notification_recipient.rb b/app/models/notification_recipient.rb
index 8e44e3d8e17..107d00d055a 100644
--- a/app/models/notification_recipient.rb
+++ b/app/models/notification_recipient.rb
@@ -52,7 +52,8 @@ class NotificationRecipient
when :mention
@type == :mention
when :participating
- @custom_action == :failed_pipeline || %i[participating mention].include?(@type)
+ %i[failed_pipeline fixed_pipeline].include?(@custom_action) ||
+ %i[participating mention].include?(@type)
when :custom
custom_enabled? || %i[participating mention].include?(@type)
when :watch
@@ -63,7 +64,13 @@ class NotificationRecipient
end
def custom_enabled?
- @custom_action && notification_setting&.event_enabled?(@custom_action)
+ return false unless @custom_action
+ return false unless notification_setting
+
+ notification_setting.event_enabled?(@custom_action) ||
+ # fixed_pipeline is a subset of success_pipeline event
+ (@custom_action == :fixed_pipeline &&
+ notification_setting.event_enabled?(:success_pipeline))
end
def unsubscribed?
diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb
index e2c362538eb..38bd95e6a20 100644
--- a/app/models/notification_setting.rb
+++ b/app/models/notification_setting.rb
@@ -44,6 +44,7 @@ class NotificationSetting < ApplicationRecord
:reassign_merge_request,
:merge_merge_request,
:failed_pipeline,
+ :fixed_pipeline,
:success_pipeline
].freeze
@@ -76,9 +77,9 @@ class NotificationSetting < ApplicationRecord
setting
end
- # Allow people to receive failed pipeline notifications if they already have
- # custom notifications enabled, as these are more like mentions than the other
- # custom settings.
+ # Allow people to receive both failed pipeline/fixed pipeline notifications
+ # if they already have custom notifications enabled,
+ # as these are more like mentions than the other custom settings.
def failed_pipeline
bool = super
@@ -86,6 +87,13 @@ class NotificationSetting < ApplicationRecord
end
alias_method :failed_pipeline?, :failed_pipeline
+ def fixed_pipeline
+ bool = super
+
+ bool.nil? || bool
+ end
+ alias_method :fixed_pipeline?, :fixed_pipeline
+
def event_enabled?(event)
respond_to?(event) && !!public_send(event) # rubocop:disable GitlabSecurity/PublicSend
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 0f61d32eb8d..41c56fe6931 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -267,6 +267,7 @@ class Project < ApplicationRecord
class_name: 'Ci::Pipeline',
inverse_of: :project
has_many :stages, class_name: 'Ci::Stage', inverse_of: :project
+ has_many :ci_refs, class_name: 'Ci::Ref'
# Ci::Build objects store data on the file system such as artifact files and
# build traces. Currently there's no efficient way of removing this data in
diff --git a/app/models/project_services/pipelines_email_service.rb b/app/models/project_services/pipelines_email_service.rb
index 65bf8535d2a..c3ed958242b 100644
--- a/app/models/project_services/pipelines_email_service.rb
+++ b/app/models/project_services/pipelines_email_service.rb
@@ -49,7 +49,7 @@ class PipelinesEmailService < Service
return unless all_recipients.any?
pipeline_id = data[:object_attributes][:id]
- PipelineNotificationWorker.new.perform(pipeline_id, all_recipients)
+ PipelineNotificationWorker.new.perform(pipeline_id, recipients: all_recipients)
end
def can_test?
diff --git a/app/services/ci/update_ci_ref_status_service.rb b/app/services/ci/update_ci_ref_status_service.rb
new file mode 100644
index 00000000000..e5e5b94b629
--- /dev/null
+++ b/app/services/ci/update_ci_ref_status_service.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+module Ci
+ class UpdateCiRefStatusService
+ include Gitlab::OptimisticLocking
+
+ attr_reader :pipeline
+
+ def initialize(pipeline)
+ @pipeline = pipeline
+ end
+
+ def call
+ save.tap { |success| after_save if success }
+ end
+
+ private
+
+ def save
+ might_insert = ref.new_record?
+
+ begin
+ retry_optimistic_lock(ref) do
+ next false if ref.persisted? &&
+ (ref.last_updated_by_pipeline_id || 0) >= pipeline.id
+
+ ref.update(status: next_status(ref.status, pipeline.status),
+ last_updated_by_pipeline: pipeline)
+ end
+ rescue ActiveRecord::RecordNotUnique
+ if might_insert
+ @ref = pipeline.reset.ref_status
+ might_insert = false
+ retry
+ else
+ raise
+ end
+ end
+ end
+
+ def next_status(ref_status, pipeline_status)
+ if ref_status == 'failed' && pipeline_status == 'success'
+ 'fixed'
+ else
+ pipeline_status
+ end
+ end
+
+ def after_save
+ enqueue_pipeline_notification
+ end
+
+ def enqueue_pipeline_notification
+ PipelineNotificationWorker.perform_async(pipeline.id, ref_status: ref.status)
+ end
+
+ def ref
+ @ref ||= pipeline.ref_status || build_ref
+ end
+
+ def build_ref
+ Ci::Ref.new(ref: pipeline.ref, project: pipeline.project, tag: pipeline.tag)
+ end
+ end
+end
diff --git a/app/services/merge_requests/create_pipeline_service.rb b/app/services/merge_requests/create_pipeline_service.rb
index 8258efba6bf..f802aa44487 100644
--- a/app/services/merge_requests/create_pipeline_service.rb
+++ b/app/services/merge_requests/create_pipeline_service.rb
@@ -9,15 +9,10 @@ module MergeRequests
end
def create_detached_merge_request_pipeline(merge_request)
- if can_use_merge_request_ref?(merge_request)
- Ci::CreatePipelineService.new(merge_request.source_project, current_user,
- ref: merge_request.ref_path)
- .execute(:merge_request_event, merge_request: merge_request)
- else
- Ci::CreatePipelineService.new(merge_request.source_project, current_user,
- ref: merge_request.source_branch)
- .execute(:merge_request_event, merge_request: merge_request)
- end
+ Ci::CreatePipelineService.new(merge_request.source_project,
+ current_user,
+ ref: pipeline_ref_for_detached_merge_request_pipeline(merge_request))
+ .execute(:merge_request_event, merge_request: merge_request)
end
def can_create_pipeline_for?(merge_request)
@@ -33,6 +28,16 @@ module MergeRequests
def allow_duplicate
params[:allow_duplicate]
end
+
+ private
+
+ def pipeline_ref_for_detached_merge_request_pipeline(merge_request)
+ if can_use_merge_request_ref?(merge_request)
+ merge_request.ref_path
+ else
+ merge_request.source_branch
+ end
+ end
end
end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index ac7ef6fb970..6f2bfa8169b 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -434,18 +434,19 @@ class NotificationService
mailer.project_was_not_exported_email(current_user, project, errors).deliver_later
end
- def pipeline_finished(pipeline, recipients = nil)
+ def pipeline_finished(pipeline, ref_status: nil, recipients: nil)
# Must always check project configuration since recipients could be a list of emails
# from the PipelinesEmailService integration.
return if pipeline.project.emails_disabled?
- email_template = "pipeline_#{pipeline.status}_email"
+ ref_status ||= pipeline.status
+ email_template = "pipeline_#{ref_status}_email"
return unless mailer.respond_to?(email_template)
recipients ||= notifiable_users(
[pipeline.user], :watch,
- custom_action: :"#{pipeline.status}_pipeline",
+ custom_action: :"#{ref_status}_pipeline",
target: pipeline
).map do |user|
user.notification_email_for(pipeline.project.group)
diff --git a/app/views/notify/_successful_pipeline.html.haml b/app/views/notify/_successful_pipeline.html.haml
new file mode 100644
index 00000000000..231df2e9206
--- /dev/null
+++ b/app/views/notify/_successful_pipeline.html.haml
@@ -0,0 +1,118 @@
+- title = local_assigns[:title]
+%tr.table-success
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#31af64;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
+ %tbody
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" }
+ %img{ alt: "✓", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif'), style: "display:block;", width: "13" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
+ = title
+%tr.spacer
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
+ &nbsp;
+%tr.section
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
+ %table.table-info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
+ %tbody
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } Project
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;" }
+ - namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
+ - namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
+ %a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" }
+ = namespace_name
+ \/
+ %a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
+ = @project.name
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Branch
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
+ %tbody
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
+ %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
+ %a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" }
+ = @pipeline.source_ref
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:400;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
+ %tbody
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
+ %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
+ %a{ href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;" }
+ = @pipeline.short_sha
+ - if @merge_request
+ in
+ %a{ href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;" }
+ = @merge_request.to_reference
+ .commit{ style: "color:#5c5c5c;font-weight:300;" }
+ = @pipeline.git_commit_message.truncate(50)
+ - commit = @pipeline.commit
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit Author
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
+ %tbody
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
+ %img.avatar{ height: "24", src: avatar_icon_for(commit.author, commit.author_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
+ - if commit.author
+ %a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" }
+ = commit.author.name
+ - else
+ %span
+ = commit.author_name
+ - if commit.different_committer?
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Committed by
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
+ %tbody
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
+ %img.avatar{ height: "24", src: avatar_icon_for(commit.committer, commit.committer_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
+ - if commit.committer
+ %a.muted{ href: user_url(commit.committer), style: "color:#333333;text-decoration:none;" }
+ = commit.committer.name
+ - else
+ %span
+ = commit.committer_name
+
+%tr.spacer
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
+ &nbsp;
+%tr.success-message
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 5px 0 5px;text-align:center;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
+ %tbody
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;font-weight:500;line-height:1.4;vertical-align:baseline;" }
+ Pipeline
+ %a{ href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;" }
+ = "\##{@pipeline.id}"
+ triggered by
+ - if @pipeline.user
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;padding-left:5px", width: "24" }
+ %img.avatar{ height: "24", src: avatar_icon_for_user(@pipeline.user, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;font-weight:500;line-height:1.4;vertical-align:baseline;" }
+ %a.muted{ href: user_url(@pipeline.user), style: "color:#333333;text-decoration:none;" }
+ = @pipeline.user.name
+ - else
+ %td{ style: "font-family:'Menlo','Liberation Mono','Consolas','DejaVu Sans Mono','Ubuntu Mono','Courier New','andale mono','lucida console',monospace;font-size:14px;line-height:1.4;vertical-align:baseline;padding:0 5px;" }
+ API
+%tr
+ %td{ colspan: 2, style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:300;line-height:1.4;padding:15px 5px;text-align:center;" }
+ - job_count = @pipeline.total_size
+ - stage_count = @pipeline.stages_count
+ successfully completed
+ #{job_count} #{'job'.pluralize(job_count)}
+ in
+ #{stage_count} #{'stage'.pluralize(stage_count)}.
diff --git a/app/views/notify/_successful_pipeline.text.erb b/app/views/notify/_successful_pipeline.text.erb
new file mode 100644
index 00000000000..628976e2dda
--- /dev/null
+++ b/app/views/notify/_successful_pipeline.text.erb
@@ -0,0 +1,32 @@
+<%= local_assigns[:title] %>
+
+Project: <%= @project.name %> ( <%= project_url(@project) %> )
+Branch: <%= @pipeline.source_ref %> ( <%= commits_url(@pipeline) %> )
+<% if @merge_request -%>
+Merge Request: <%= @merge_request.to_reference %> ( <%= merge_request_url(@merge_request) %> )
+<% end -%>
+
+Commit: <%= @pipeline.short_sha %> ( <%= commit_url(@pipeline) %> )
+Commit Message: <%= @pipeline.git_commit_message.truncate(50) %>
+<% commit = @pipeline.commit -%>
+<% if commit.author -%>
+Commit Author: <%= sanitize_name(commit.author.name) %> ( <%= user_url(commit.author) %> )
+<% else -%>
+Commit Author: <%= commit.author_name %>
+<% end -%>
+<% if commit.different_committer? -%>
+<% if commit.committer -%>
+Committed by: <%= sanitize_name(commit.committer.name) %> ( <%= user_url(commit.committer) %> )
+<% else -%>
+Committed by: <%= commit.committer_name %>
+<% end -%>
+<% end -%>
+
+<% job_count = @pipeline.total_size -%>
+<% stage_count = @pipeline.stages_count -%>
+<% if @pipeline.user -%>
+Pipeline #<%= @pipeline.id %> ( <%= pipeline_url(@pipeline) %> ) triggered by <%= sanitize_name(@pipeline.user.name) %> ( <%= user_url(@pipeline.user) %> )
+<% else -%>
+Pipeline #<%= @pipeline.id %> ( <%= pipeline_url(@pipeline) %> ) triggered by API
+<% end -%>
+successfully completed <%= job_count %> <%= 'job'.pluralize(job_count) %> in <%= stage_count %> <%= 'stage'.pluralize(stage_count) %>.
diff --git a/app/views/notify/pipeline_fixed_email.html.haml b/app/views/notify/pipeline_fixed_email.html.haml
new file mode 100644
index 00000000000..05c0027a6fc
--- /dev/null
+++ b/app/views/notify/pipeline_fixed_email.html.haml
@@ -0,0 +1 @@
+= render 'notify/successful_pipeline', title: 'Your pipeline has been fixed!'
diff --git a/app/views/notify/pipeline_fixed_email.text.erb b/app/views/notify/pipeline_fixed_email.text.erb
new file mode 100644
index 00000000000..75268531bdc
--- /dev/null
+++ b/app/views/notify/pipeline_fixed_email.text.erb
@@ -0,0 +1 @@
+<%= render 'notify/successful_pipeline', title: 'Your pipeline has been fixed!' -%>
diff --git a/app/views/notify/pipeline_success_email.html.haml b/app/views/notify/pipeline_success_email.html.haml
index e575a5569fa..c34e02b5fee 100644
--- a/app/views/notify/pipeline_success_email.html.haml
+++ b/app/views/notify/pipeline_success_email.html.haml
@@ -1,117 +1 @@
-%tr.table-success
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#31af64;" }
- %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
- %tbody
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" }
- %img{ alt: "✓", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif'), style: "display:block;", width: "13" }/
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
- Your pipeline has passed.
-%tr.spacer
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
- &nbsp;
-%tr.section
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
- %table.table-info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
- %tbody
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } Project
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;" }
- - namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
- - namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
- %a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" }
- = namespace_name
- \/
- %a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
- = @project.name
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Branch
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
- %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
- %tbody
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
- %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "" }/
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
- %a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" }
- = @pipeline.source_ref
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:400;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
- %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
- %tbody
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
- %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "" }/
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
- %a{ href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;" }
- = @pipeline.short_sha
- - if @merge_request
- in
- %a{ href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;" }
- = @merge_request.to_reference
- .commit{ style: "color:#5c5c5c;font-weight:300;" }
- = @pipeline.git_commit_message.truncate(50)
- - commit = @pipeline.commit
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit Author
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
- %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
- %tbody
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
- %img.avatar{ height: "24", src: avatar_icon_for(commit.author, commit.author_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
- - if commit.author
- %a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" }
- = commit.author.name
- - else
- %span
- = commit.author_name
- - if commit.different_committer?
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Committed by
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
- %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
- %tbody
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
- %img.avatar{ height: "24", src: avatar_icon_for(commit.committer, commit.committer_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
- - if commit.committer
- %a.muted{ href: user_url(commit.committer), style: "color:#333333;text-decoration:none;" }
- = commit.committer.name
- - else
- %span
- = commit.committer_name
-
-%tr.spacer
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
- &nbsp;
-%tr.success-message
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 5px 0 5px;text-align:center;" }
- %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
- %tbody
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;font-weight:500;line-height:1.4;vertical-align:baseline;" }
- Pipeline
- %a{ href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;" }
- = "\##{@pipeline.id}"
- triggered by
- - if @pipeline.user
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;padding-left:5px", width: "24" }
- %img.avatar{ height: "24", src: avatar_icon_for_user(@pipeline.user, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;font-weight:500;line-height:1.4;vertical-align:baseline;" }
- %a.muted{ href: user_url(@pipeline.user), style: "color:#333333;text-decoration:none;" }
- = @pipeline.user.name
- - else
- %td{ style: "font-family:'Menlo','Liberation Mono','Consolas','DejaVu Sans Mono','Ubuntu Mono','Courier New','andale mono','lucida console',monospace;font-size:14px;line-height:1.4;vertical-align:baseline;padding:0 5px;" }
- API
-%tr
- %td{ colspan: 2, style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:300;line-height:1.4;padding:15px 5px;text-align:center;" }
- - job_count = @pipeline.total_size
- - stage_count = @pipeline.stages_count
- successfully completed
- #{job_count} #{'job'.pluralize(job_count)}
- in
- #{stage_count} #{'stage'.pluralize(stage_count)}.
+= render 'notify/successful_pipeline', title: 'Your pipeline has passed.'
diff --git a/app/views/notify/pipeline_success_email.text.erb b/app/views/notify/pipeline_success_email.text.erb
index 4005158dc9e..b554bffc908 100644
--- a/app/views/notify/pipeline_success_email.text.erb
+++ b/app/views/notify/pipeline_success_email.text.erb
@@ -1,32 +1 @@
-Your pipeline has passed.
-
-Project: <%= @project.name %> ( <%= project_url(@project) %> )
-Branch: <%= @pipeline.source_ref %> ( <%= commits_url(@pipeline) %> )
-<% if @merge_request -%>
-Merge Request: <%= @merge_request.to_reference %> ( <%= merge_request_url(@merge_request) %> )
-<% end -%>
-
-Commit: <%= @pipeline.short_sha %> ( <%= commit_url(@pipeline) %> )
-Commit Message: <%= @pipeline.git_commit_message.truncate(50) %>
-<% commit = @pipeline.commit -%>
-<% if commit.author -%>
-Commit Author: <%= sanitize_name(commit.author.name) %> ( <%= user_url(commit.author) %> )
-<% else -%>
-Commit Author: <%= commit.author_name %>
-<% end -%>
-<% if commit.different_committer? -%>
-<% if commit.committer -%>
-Committed by: <%= sanitize_name(commit.committer.name) %> ( <%= user_url(commit.committer) %> )
-<% else -%>
-Committed by: <%= commit.committer_name %>
-<% end -%>
-<% end -%>
-
-<% job_count = @pipeline.total_size -%>
-<% stage_count = @pipeline.stages_count -%>
-<% if @pipeline.user -%>
-Pipeline #<%= @pipeline.id %> ( <%= pipeline_url(@pipeline) %> ) triggered by <%= sanitize_name(@pipeline.user.name) %> ( <%= user_url(@pipeline.user) %> )
-<% else -%>
-Pipeline #<%= @pipeline.id %> ( <%= pipeline_url(@pipeline) %> ) triggered by API
-<% end -%>
-successfully completed <%= job_count %> <%= 'job'.pluralize(job_count) %> in <%= stage_count %> <%= 'stage'.pluralize(stage_count) %>.
+<%= render 'notify/successful_pipeline', title: 'Your pipeline has passed.' -%>
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index e8682769720..0b7add65d94 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -668,6 +668,13 @@
:resource_boundary: :cpu
:weight: 3
:idempotent:
+- :name: pipeline_default:pipeline_update_ci_ref_status
+ :feature_category: :continuous_integration
+ :has_external_dependencies:
+ :latency_sensitive: true
+ :resource_boundary: :cpu
+ :weight: 3
+ :idempotent:
- :name: pipeline_hooks:build_hooks
:feature_category: :continuous_integration
:has_external_dependencies:
diff --git a/app/workers/pipeline_notification_worker.rb b/app/workers/pipeline_notification_worker.rb
index e9081cc416f..72663fa19ae 100644
--- a/app/workers/pipeline_notification_worker.rb
+++ b/app/workers/pipeline_notification_worker.rb
@@ -8,12 +8,20 @@ class PipelineNotificationWorker # rubocop:disable Scalability/IdempotentWorker
worker_resource_boundary :cpu
# rubocop: disable CodeReuse/ActiveRecord
- def perform(pipeline_id, recipients = nil)
- pipeline = Ci::Pipeline.find_by(id: pipeline_id)
+ def perform(pipeline_id, args = {})
+ case args
+ when Hash
+ ref_status = args[:ref_status]
+ recipients = args[:recipients]
+ else # TODO: backward compatible interface, can be removed in 12.10
+ recipients = args
+ ref_status = nil
+ end
+ pipeline = Ci::Pipeline.find_by(id: pipeline_id)
return unless pipeline
- NotificationService.new.pipeline_finished(pipeline, recipients)
+ NotificationService.new.pipeline_finished(pipeline, ref_status: ref_status, recipients: recipients)
end
# rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/pipeline_update_ci_ref_status_worker.rb b/app/workers/pipeline_update_ci_ref_status_worker.rb
new file mode 100644
index 00000000000..3d6a0d30e9c
--- /dev/null
+++ b/app/workers/pipeline_update_ci_ref_status_worker.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class PipelineUpdateCiRefStatusWorker # rubocop:disable Scalability/IdempotentWorker
+ include ApplicationWorker
+ include PipelineQueue
+
+ latency_sensitive_worker!
+ worker_resource_boundary :cpu
+
+ def perform(pipeline_id)
+ pipeline = Ci::Pipeline.find_by_id(pipeline_id)
+
+ return unless pipeline
+
+ Ci::UpdateCiRefStatusService.new(pipeline).call
+ end
+end