Commit c5315dfd authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 3e711ca69b41
......@@ -146,6 +146,7 @@ linters:
- 'app/views/notify/_failed_builds.html.haml'
- 'app/views/notify/_reassigned_issuable_email.html.haml'
- 'app/views/notify/_removal_notification.html.haml'
- 'app/views/notify/_successful_pipeline.html.haml'
- 'app/views/notify/autodevops_disabled_email.html.haml'
- 'app/views/notify/changed_milestone_email.html.haml'
- 'app/views/notify/import_issues_csv_email.html.haml'
......@@ -163,7 +164,6 @@ linters:
- 'app/views/notify/pages_domain_verification_failed_email.html.haml'
- 'app/views/notify/pages_domain_verification_succeeded_email.html.haml'
- 'app/views/notify/pipeline_failed_email.html.haml'
- 'app/views/notify/pipeline_success_email.html.haml'
- 'app/views/notify/project_was_exported_email.html.haml'
- 'app/views/notify/project_was_moved_email.html.haml'
- 'app/views/notify/project_was_not_exported_email.html.haml'
......
<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>
......
......@@ -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(() => {});
......
<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,
);
......
......@@ -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();
}
......
......@@ -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; }
......
......@@ -10,6 +10,10 @@ def pipeline_failed_email(pipeline, recipients)
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)
......
......@@ -145,6 +145,10 @@ def pipeline_failed_email
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
......
......@@ -63,6 +63,14 @@ class Pipeline < ApplicationRecord
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 @@ class Pipeline < ApplicationRecord
after_transition any => [:success, :failed] do |pipeline|
pipeline.run_after_commit do
PipelineNotificationWorker.perform_async(pipeline.id)
PipelineUpdateCiRefStatusWorker.perform_async(pipeline.id)
end
end
......
# 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
......@@ -52,7 +52,8 @@ def suitable_notification_level?
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 @@ def suitable_notification_level?
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?
......
......@@ -44,6 +44,7 @@ class NotificationSetting < ApplicationRecord
:reassign_merge_request,
:merge_merge_request,
:failed_pipeline,
:fixed_pipeline,
:success_pipeline
].freeze
......@@ -76,9 +77,9 @@ def self.find_or_create_for(source)
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 @@ def failed_pipeline
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
......
......@@ -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
......
......@@ -49,7 +49,7 @@ def execute(data, force: false)
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?
......
# 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
......@@ -9,15 +9,10 @@ def execute(merge_request)
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 @@ def can_create_pipeline_for?(merge_request)
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
......
......@@ -434,18 +434,19 @@ def project_not_exported(project, current_user, errors)
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)
......
- 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)}.
<%= 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) %>.
= render 'notify/successful_pipeline', title: 'Your pipeline has been fixed!'
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment