Improve pipeline status Slack notifications

This commit adds some formatting to the Slack notifications for pipeline
statuses, as well as adds information about the stage and jobs
that failed in the case of pipeline failure.
This commit is contained in:
Nathan Friend 2019-07-25 11:26:29 -03:00
parent 6dcde68b6d
commit 7b5fb754f8
No known key found for this signature in database
GPG Key ID: E010A0869C9F35D9
7 changed files with 674 additions and 107 deletions

View File

@ -1,24 +1,47 @@
# frozen_string_literal: true
require 'slack-notifier'
module ChatMessage
class PipelineMessage < BaseMessage
MAX_VISIBLE_JOBS = 10
attr_reader :user
attr_reader :ref_type
attr_reader :ref
attr_reader :status
attr_reader :detailed_status
attr_reader :duration
attr_reader :finished_at
attr_reader :pipeline_id
attr_reader :failed_stages
attr_reader :failed_jobs
attr_reader :project
attr_reader :commit
attr_reader :committer
attr_reader :pipeline
def initialize(data)
super
@user = data[:user]
@user_name = data.dig(:user, :username) || 'API'
pipeline_attributes = data[:object_attributes]
@ref_type = pipeline_attributes[:tag] ? 'tag' : 'branch'
@ref = pipeline_attributes[:ref]
@status = pipeline_attributes[:status]
@detailed_status = pipeline_attributes[:detailed_status]
@duration = pipeline_attributes[:duration].to_i
@finished_at = pipeline_attributes[:finished_at] ? Time.parse(pipeline_attributes[:finished_at]).to_i : nil
@pipeline_id = pipeline_attributes[:id]
@failed_jobs = Array(data[:builds]).select { |b| b[:status] == 'failed' }.reverse # Show failed jobs from oldest to newest
@failed_stages = @failed_jobs.map { |j| j[:stage] }.uniq
@project = Project.find(data[:project][:id])
@commit = project.commit_by(oid: data[:commit][:id])
@committer = commit.committer
@pipeline = Ci::Pipeline.find(pipeline_id)
end
def pretext
@ -28,38 +51,145 @@ module ChatMessage
def attachments
return message if markdown
[{ text: format(message), color: attachment_color }]
return [{ text: format(message), color: attachment_color }] unless fancy_notifications?
[{
fallback: format(message),
color: attachment_color,
author_name: user_combined_name,
author_icon: user_avatar,
author_link: author_url,
title: s_("ChatMessage|Pipeline #%{pipeline_id} %{humanized_status} in %{duration}") %
{
pipeline_id: pipeline_id,
humanized_status: humanized_status,
duration: pretty_duration(duration)
},
title_link: pipeline_url,
fields: attachments_fields,
footer: project.name,
footer_icon: project.avatar_url,
ts: finished_at
}]
end
def activity
{
title: "Pipeline #{pipeline_link} of #{ref_type} #{branch_link} by #{user_combined_name} #{humanized_status}",
subtitle: "in #{project_link}",
text: "in #{pretty_duration(duration)}",
title: s_("ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{branch_link} by %{user_combined_name} %{humanized_status}") %
{
pipeline_link: pipeline_link,
ref_type: ref_type,
branch_link: branch_link,
user_combined_name: user_combined_name,
humanized_status: humanized_status
},
subtitle: s_("ChatMessage|in %{project_link}") % { project_link: project_link },
text: s_("ChatMessage|in %{duration}") % { duration: pretty_duration(duration) },
image: user_avatar || ''
}
end
private
def fancy_notifications?
Feature.enabled?(:fancy_pipeline_slack_notifications, default_enabled: true)
end
def failed_stages_field
{
title: s_("ChatMessage|Failed stage").pluralize(failed_stages.length),
value: Slack::Notifier::LinkFormatter.format(failed_stages_links),
short: true
}
end
def failed_jobs_field
{
title: s_("ChatMessage|Failed job").pluralize(failed_jobs.length),
value: Slack::Notifier::LinkFormatter.format(failed_jobs_links),
short: true
}
end
def yaml_error_field
{
title: s_("ChatMessage|Invalid CI config YAML file"),
value: pipeline.yaml_errors,
short: false
}
end
def attachments_fields
fields = [
{
title: ref_type == "tag" ? s_("ChatMessage|Tag") : s_("ChatMessage|Branch"),
value: Slack::Notifier::LinkFormatter.format(ref_name_link),
short: true
},
{
title: s_("ChatMessage|Commit"),
value: Slack::Notifier::LinkFormatter.format(commit_link),
short: true
}
]
fields << failed_stages_field if failed_stages.any?
fields << failed_jobs_field if failed_jobs.any?
fields << yaml_error_field if pipeline.has_yaml_errors?
fields
end
def message
"#{project_link}: Pipeline #{pipeline_link} of #{ref_type} #{branch_link} by #{user_combined_name} #{humanized_status} in #{pretty_duration(duration)}"
s_("ChatMessage|%{project_link}: Pipeline %{pipeline_link} of %{ref_type} %{branch_link} by %{user_combined_name} %{humanized_status} in %{duration}") %
{
project_link: project_link,
pipeline_link: pipeline_link,
ref_type: ref_type,
branch_link: branch_link,
user_combined_name: user_combined_name,
humanized_status: humanized_status,
duration: pretty_duration(duration)
}
end
def humanized_status
case status
when 'success'
'passed'
if fancy_notifications?
case status
when 'success'
detailed_status == "passed with warnings" ? s_("ChatMessage|has passed with warnings") : s_("ChatMessage|has passed")
when 'failed'
s_("ChatMessage|has failed")
else
status
end
else
status
case status
when 'success'
s_("ChatMessage|passed")
when 'failed'
s_("ChatMessage|failed")
else
status
end
end
end
def attachment_color
if status == 'success'
'good'
if fancy_notifications?
case status
when 'success'
detailed_status == 'passed with warnings' ? 'warning' : 'good'
else
'danger'
end
else
'danger'
case status
when 'success'
'good'
else
'danger'
end
end
end
@ -71,16 +201,83 @@ module ChatMessage
"[#{ref}](#{branch_url})"
end
def project_url
project.web_url
end
def project_link
"[#{project_name}](#{project_url})"
"[#{project.name}](#{project_url})"
end
def pipeline_failed_jobs_url
"#{project_url}/pipelines/#{pipeline_id}/failures"
end
def pipeline_url
"#{project_url}/pipelines/#{pipeline_id}"
if fancy_notifications? && failed_jobs.any?
pipeline_failed_jobs_url
else
"#{project_url}/pipelines/#{pipeline_id}"
end
end
def pipeline_link
"[##{pipeline_id}](#{pipeline_url})"
end
def job_url(job)
"#{project_url}/-/jobs/#{job[:id]}"
end
def job_link(job)
"[#{job[:name]}](#{job_url(job)})"
end
def failed_jobs_links
failed = failed_jobs.slice(0, MAX_VISIBLE_JOBS)
truncated = failed_jobs.slice(MAX_VISIBLE_JOBS, failed_jobs.size)
failed_links = failed.map { |job| job_link(job) }
unless truncated.blank?
failed_links << s_("ChatMessage|and [%{count} more](%{pipeline_failed_jobs_url})") % {
count: truncated.size,
pipeline_failed_jobs_url: pipeline_failed_jobs_url
}
end
failed_links.join(I18n.translate(:'support.array.words_connector'))
end
def stage_link(stage)
# All stages link to the pipeline page
"[#{stage}](#{pipeline_url})"
end
def failed_stages_links
failed_stages.map { |s| stage_link(s) }.join(I18n.translate(:'support.array.words_connector'))
end
def commit_url
Gitlab::UrlBuilder.build(commit)
end
def commit_link
"[#{commit.title}](#{commit_url})"
end
def commits_page_url
"#{project_url}/commits/#{ref}"
end
def ref_name_link
"[#{ref}](#{commits_page_url})"
end
def author_url
return unless user && committer
Gitlab::UrlBuilder.build(committer)
end
end
end

View File

@ -0,0 +1,5 @@
---
title: Improve pipeline status Slack notifications
merge_request: 27683
author:
type: added

View File

@ -2011,6 +2011,57 @@ msgstr ""
msgid "Chat"
msgstr ""
msgid "ChatMessage|%{project_link}: Pipeline %{pipeline_link} of %{ref_type} %{branch_link} by %{user_combined_name} %{humanized_status} in %{duration}"
msgstr ""
msgid "ChatMessage|Branch"
msgstr ""
msgid "ChatMessage|Commit"
msgstr ""
msgid "ChatMessage|Failed job"
msgstr ""
msgid "ChatMessage|Failed stage"
msgstr ""
msgid "ChatMessage|Invalid CI config YAML file"
msgstr ""
msgid "ChatMessage|Pipeline #%{pipeline_id} %{humanized_status} in %{duration}"
msgstr ""
msgid "ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{branch_link} by %{user_combined_name} %{humanized_status}"
msgstr ""
msgid "ChatMessage|Tag"
msgstr ""
msgid "ChatMessage|and [%{count} more](%{pipeline_failed_jobs_url})"
msgstr ""
msgid "ChatMessage|failed"
msgstr ""
msgid "ChatMessage|has failed"
msgstr ""
msgid "ChatMessage|has passed"
msgstr ""
msgid "ChatMessage|has passed with warnings"
msgstr ""
msgid "ChatMessage|in %{duration}"
msgstr ""
msgid "ChatMessage|in %{project_link}"
msgstr ""
msgid "ChatMessage|passed"
msgstr ""
msgid "Check again"
msgstr ""

View File

@ -1,12 +1,9 @@
# frozen_string_literal: true
require 'spec_helper'
describe ChatMessage::PipelineMessage do
subject { described_class.new(args) }
let(:user) { { name: "The Hacker", username: 'hacker' } }
let(:duration) { 7210 }
let(:args) do
{
object_attributes: {
@ -14,122 +11,437 @@ describe ChatMessage::PipelineMessage do
sha: '97de212e80737a608d939f648d959671fb0a0142',
tag: false,
ref: 'develop',
status: status,
duration: duration
status: 'success',
detailed_status: nil,
duration: 7210,
finished_at: "2019-05-27 11:56:36 -0300"
},
project: {
path_with_namespace: 'project_name',
web_url: 'http://example.gitlab.com'
id: 234,
name: "project_name",
path_with_namespace: 'group/project_name',
web_url: 'http://example.gitlab.com',
avatar_url: 'http://example.com/project_avatar'
},
user: user
user: {
id: 345,
name: "The Hacker",
username: "hacker",
email: "hacker@example.gitlab.com",
avatar_url: "http://example.com/avatar"
},
commit: {
id: "abcdef"
},
builds: nil,
markdown: false
}
end
let(:combined_name) { "The Hacker (hacker)" }
context 'without markdown' do
context 'pipeline succeeded' do
let(:status) { 'success' }
let(:color) { 'good' }
let(:message) { build_message('passed', combined_name) }
let(:has_yaml_errors) { false }
it 'returns a message with information about succeeded build' do
expect(subject.pretext).to be_empty
expect(subject.fallback).to eq(message)
expect(subject.attachments).to eq([text: message, color: color])
before do
test_commit = double("A test commit", committer: args[:user], title: "A test commit message")
test_project = double("A test project",
commit_by: test_commit, name: args[:project][:name],
web_url: args[:project][:web_url], avatar_url: args[:project][:avatar_url])
allow(Project).to receive(:find) { test_project }
test_pipeline = double("A test pipeline", has_yaml_errors?: has_yaml_errors,
yaml_errors: "yaml error description here")
allow(Ci::Pipeline).to receive(:find) { test_pipeline }
allow(Gitlab::UrlBuilder).to receive(:build).with(test_commit).and_return("http://example.com/commit")
allow(Gitlab::UrlBuilder).to receive(:build).with(args[:user]).and_return("http://example.gitlab.com/hacker")
end
context 'when the fancy_pipeline_slack_notifications feature flag is disabled' do
before do
stub_feature_flags(fancy_pipeline_slack_notifications: false)
end
it 'returns an empty pretext' do
expect(subject.pretext).to be_empty
end
it "returns the pipeline summary in the activity's title" do
expect(subject.activity[:title]).to eq(
"Pipeline [#123](http://example.gitlab.com/pipelines/123)" \
" of branch [develop](http://example.gitlab.com/commits/develop)" \
" by The Hacker (hacker) passed"
)
end
context "when the pipeline failed" do
before do
args[:object_attributes][:status] = 'failed'
end
it "returns the summary with a 'failed' status" do
expect(subject.activity[:title]).to eq(
"Pipeline [#123](http://example.gitlab.com/pipelines/123)" \
" of branch [develop](http://example.gitlab.com/commits/develop)" \
" by The Hacker (hacker) failed"
)
end
end
context 'pipeline failed' do
let(:status) { 'failed' }
let(:color) { 'danger' }
let(:message) { build_message(status, combined_name) }
it 'returns a message with information about failed build' do
expect(subject.pretext).to be_empty
expect(subject.fallback).to eq(message)
expect(subject.attachments).to eq([text: message, color: color])
context 'when no user is provided because the pipeline was triggered by the API' do
before do
args[:user] = nil
end
context 'when triggered by API therefore lacking user' do
let(:user) { nil }
let(:message) { build_message(status, 'API') }
it 'returns a message stating it is by API' do
expect(subject.pretext).to be_empty
expect(subject.fallback).to eq(message)
expect(subject.attachments).to eq([text: message, color: color])
end
it "returns the summary with 'API' as the username" do
expect(subject.activity[:title]).to eq(
"Pipeline [#123](http://example.gitlab.com/pipelines/123)" \
" of branch [develop](http://example.gitlab.com/commits/develop)" \
" by API passed"
)
end
end
def build_message(status_text = status, name = user[:name])
"<http://example.gitlab.com|project_name>:" \
" Pipeline <http://example.gitlab.com/pipelines/123|#123>" \
" of branch <http://example.gitlab.com/commits/develop|develop>" \
" by #{name} #{status_text} in 02:00:10"
it "returns a link to the project in the activity's subtitle" do
expect(subject.activity[:subtitle]).to eq("in [project_name](http://example.gitlab.com)")
end
it "returns the build duration in the activity's text property" do
expect(subject.activity[:text]).to eq("in 02:00:10")
end
it "returns the user's avatar image URL in the activity's image property" do
expect(subject.activity[:image]).to eq("http://example.com/avatar")
end
context 'when the user does not have an avatar' do
before do
args[:user][:avatar_url] = nil
end
it "returns an empty string in the activity's image property" do
expect(subject.activity[:image]).to be_empty
end
end
it "returns the pipeline summary as the attachment's text property" do
expect(subject.attachments.first[:text]).to eq(
"<http://example.gitlab.com|project_name>:" \
" Pipeline <http://example.gitlab.com/pipelines/123|#123>" \
" of branch <http://example.gitlab.com/commits/develop|develop>" \
" by The Hacker (hacker) passed in 02:00:10"
)
end
it "returns 'good' as the attachment's color property" do
expect(subject.attachments.first[:color]).to eq('good')
end
context "when the pipeline failed" do
before do
args[:object_attributes][:status] = 'failed'
end
it "returns 'danger' as the attachment's color property" do
expect(subject.attachments.first[:color]).to eq('danger')
end
end
context 'when rendering markdown' do
before do
args[:markdown] = true
end
it 'returns the pipeline summary as the attachments in markdown format' do
expect(subject.attachments).to eq(
"[project_name](http://example.gitlab.com):" \
" Pipeline [#123](http://example.gitlab.com/pipelines/123)" \
" of branch [develop](http://example.gitlab.com/commits/develop)" \
" by The Hacker (hacker) passed in 02:00:10"
)
end
end
end
context 'with markdown' do
context 'when the fancy_pipeline_slack_notifications feature flag is enabled' do
before do
args[:markdown] = true
stub_feature_flags(fancy_pipeline_slack_notifications: true)
end
context 'pipeline succeeded' do
let(:status) { 'success' }
let(:color) { 'good' }
let(:message) { build_markdown_message('passed', combined_name) }
it 'returns an empty pretext' do
expect(subject.pretext).to be_empty
end
it 'returns a message with information about succeeded build' do
expect(subject.pretext).to be_empty
expect(subject.attachments).to eq(message)
expect(subject.activity).to eq({
title: 'Pipeline [#123](http://example.gitlab.com/pipelines/123) of branch [develop](http://example.gitlab.com/commits/develop) by The Hacker (hacker) passed',
subtitle: 'in [project_name](http://example.gitlab.com)',
text: 'in 02:00:10',
image: ''
it "returns the pipeline summary in the activity's title" do
expect(subject.activity[:title]).to eq(
"Pipeline [#123](http://example.gitlab.com/pipelines/123)" \
" of branch [develop](http://example.gitlab.com/commits/develop)" \
" by The Hacker (hacker) has passed"
)
end
context "when the pipeline failed" do
before do
args[:object_attributes][:status] = 'failed'
end
it "returns the summary with a 'failed' status" do
expect(subject.activity[:title]).to eq(
"Pipeline [#123](http://example.gitlab.com/pipelines/123)" \
" of branch [develop](http://example.gitlab.com/commits/develop)" \
" by The Hacker (hacker) has failed"
)
end
end
context "when the pipeline passed with warnings" do
before do
args[:object_attributes][:detailed_status] = 'passed with warnings'
end
it "returns the summary with a 'passed with warnings' status" do
expect(subject.activity[:title]).to eq(
"Pipeline [#123](http://example.gitlab.com/pipelines/123)" \
" of branch [develop](http://example.gitlab.com/commits/develop)" \
" by The Hacker (hacker) has passed with warnings"
)
end
end
context 'when no user is provided because the pipeline was triggered by the API' do
before do
args[:user] = nil
end
it "returns the summary with 'API' as the username" do
expect(subject.activity[:title]).to eq(
"Pipeline [#123](http://example.gitlab.com/pipelines/123)" \
" of branch [develop](http://example.gitlab.com/commits/develop)" \
" by API has passed"
)
end
end
it "returns a link to the project in the activity's subtitle" do
expect(subject.activity[:subtitle]).to eq("in [project_name](http://example.gitlab.com)")
end
it "returns the build duration in the activity's text property" do
expect(subject.activity[:text]).to eq("in 02:00:10")
end
it "returns the user's avatar image URL in the activity's image property" do
expect(subject.activity[:image]).to eq("http://example.com/avatar")
end
context 'when the user does not have an avatar' do
before do
args[:user][:avatar_url] = nil
end
it "returns an empty string in the activity's image property" do
expect(subject.activity[:image]).to be_empty
end
end
it "returns the pipeline summary as the attachment's fallback property" do
expect(subject.attachments.first[:fallback]).to eq(
"<http://example.gitlab.com|project_name>:" \
" Pipeline <http://example.gitlab.com/pipelines/123|#123>" \
" of branch <http://example.gitlab.com/commits/develop|develop>" \
" by The Hacker (hacker) has passed in 02:00:10"
)
end
it "returns 'good' as the attachment's color property" do
expect(subject.attachments.first[:color]).to eq('good')
end
context "when the pipeline failed" do
before do
args[:object_attributes][:status] = 'failed'
end
it "returns 'danger' as the attachment's color property" do
expect(subject.attachments.first[:color]).to eq('danger')
end
end
context "when the pipeline passed with warnings" do
before do
args[:object_attributes][:detailed_status] = 'passed with warnings'
end
it "returns 'warning' as the attachment's color property" do
expect(subject.attachments.first[:color]).to eq('warning')
end
end
it "returns the committer's name and username as the attachment's author_name property" do
expect(subject.attachments.first[:author_name]).to eq('The Hacker (hacker)')
end
it "returns the committer's avatar URL as the attachment's author_icon property" do
expect(subject.attachments.first[:author_icon]).to eq('http://example.com/avatar')
end
it "returns the committer's GitLab profile URL as the attachment's author_link property" do
expect(subject.attachments.first[:author_link]).to eq('http://example.gitlab.com/hacker')
end
context 'when no user is provided because the pipeline was triggered by the API' do
before do
args[:user] = nil
end
it "returns the committer's name and username as the attachment's author_name property" do
expect(subject.attachments.first[:author_name]).to eq('API')
end
it "returns nil as the attachment's author_icon property" do
expect(subject.attachments.first[:author_icon]).to be_nil
end
it "returns nil as the attachment's author_link property" do
expect(subject.attachments.first[:author_link]).to be_nil
end
end
it "returns the pipeline ID, status, and duration as the attachment's title property" do
expect(subject.attachments.first[:title]).to eq("Pipeline #123 has passed in 02:00:10")
end
it "returns the pipeline URL as the attachment's title_link property" do
expect(subject.attachments.first[:title_link]).to eq("http://example.gitlab.com/pipelines/123")
end
it "returns two attachment fields" do
expect(subject.attachments.first[:fields].count).to eq(2)
end
it "returns the commit message as the attachment's second field property" do
expect(subject.attachments.first[:fields][0]).to eq({
title: "Branch",
value: "<http://example.gitlab.com/commits/develop|develop>",
short: true
})
end
it "returns the ref name and link as the attachment's second field property" do
expect(subject.attachments.first[:fields][1]).to eq({
title: "Commit",
value: "<http://example.com/commit|A test commit message>",
short: true
})
end
context "when a job in the pipeline fails" do
before do
args[:builds] = [
{ id: 1, name: "rspec", status: "failed", stage: "test" },
{ id: 2, name: "karma", status: "success", stage: "test" }
]
end
it "returns four attachment fields" do
expect(subject.attachments.first[:fields].count).to eq(4)
end
it "returns the stage name and link to the 'Failed jobs' tab on the pipeline's page as the attachment's third field property" do
expect(subject.attachments.first[:fields][2]).to eq({
title: "Failed stage",
value: "<http://example.gitlab.com/pipelines/123/failures|test>",
short: true
})
end
it "returns the job name and link as the attachment's fourth field property" do
expect(subject.attachments.first[:fields][3]).to eq({
title: "Failed job",
value: "<http://example.gitlab.com/-/jobs/1|rspec>",
short: true
})
end
end
context 'pipeline failed' do
let(:status) { 'failed' }
let(:color) { 'danger' }
let(:message) { build_markdown_message(status, combined_name) }
it 'returns a message with information about failed build' do
expect(subject.pretext).to be_empty
expect(subject.attachments).to eq(message)
expect(subject.activity).to eq({
title: 'Pipeline [#123](http://example.gitlab.com/pipelines/123) of branch [develop](http://example.gitlab.com/commits/develop) by The Hacker (hacker) failed',
subtitle: 'in [project_name](http://example.gitlab.com)',
text: 'in 02:00:10',
image: ''
})
end
context 'when triggered by API therefore lacking user' do
let(:user) { nil }
let(:message) { build_markdown_message(status, 'API') }
it 'returns a message stating it is by API' do
expect(subject.pretext).to be_empty
expect(subject.attachments).to eq(message)
expect(subject.activity).to eq({
title: 'Pipeline [#123](http://example.gitlab.com/pipelines/123) of branch [develop](http://example.gitlab.com/commits/develop) by API failed',
subtitle: 'in [project_name](http://example.gitlab.com)',
text: 'in 02:00:10',
image: ''
})
context "when lots of jobs across multiple stages fail" do
before do
args[:builds] = (1..25).map do |i|
{ id: i, name: "job-#{i}", status: "failed", stage: "stage-" + ((i % 3) + 1).to_s }
end
end
it "returns the stage names and links to the 'Failed jobs' tab on the pipeline's page as the attachment's third field property" do
expect(subject.attachments.first[:fields][2]).to eq({
title: "Failed stages",
value: "<http://example.gitlab.com/pipelines/123/failures|stage-2>, <http://example.gitlab.com/pipelines/123/failures|stage-1>, <http://example.gitlab.com/pipelines/123/failures|stage-3>",
short: true
})
end
it "returns the job names and links as the attachment's fourth field property" do
expected_jobs = 25.downto(16).map do |i|
"<http://example.gitlab.com/-/jobs/#{i}|job-#{i}>"
end
expected_jobs << "and <http://example.gitlab.com/pipelines/123/failures|15 more>"
expect(subject.attachments.first[:fields][3]).to eq({
title: "Failed jobs",
value: expected_jobs.join(", "),
short: true
})
end
end
def build_markdown_message(status_text = status, name = user[:name])
"[project_name](http://example.gitlab.com):" \
" Pipeline [#123](http://example.gitlab.com/pipelines/123)" \
" of branch [develop](http://example.gitlab.com/commits/develop)" \
" by #{name} #{status_text} in 02:00:10"
context "when the CI config file contains a YAML error" do
let(:has_yaml_errors) { true }
it "returns three attachment fields" do
expect(subject.attachments.first[:fields].count).to eq(3)
end
it "returns the YAML error deatils as the attachment's third field property" do
expect(subject.attachments.first[:fields][2]).to eq({
title: "Invalid CI config YAML file",
value: "yaml error description here",
short: false
})
end
end
it "returns the stage name and link as the attachment's second field property" do
expect(subject.attachments.first[:fields][1]).to eq({
title: "Commit",
value: "<http://example.com/commit|A test commit message>",
short: true
})
end
it "returns the project's name as the attachment's footer property" do
expect(subject.attachments.first[:footer]).to eq("project_name")
end
it "returns the project's avatar URL as the attachment's footer_icon property" do
expect(subject.attachments.first[:footer_icon]).to eq("http://example.com/project_avatar")
end
it "returns the pipeline's timestamp as the attachment's ts property" do
expected_ts = Time.parse(args[:object_attributes][:finished_at]).to_i
expect(subject.attachments.first[:ts]).to eq(expected_ts)
end
context 'when rendering markdown' do
before do
args[:markdown] = true
end
it 'returns the pipeline summary as the attachments in markdown format' do
expect(subject.attachments).to eq(
"[project_name](http://example.gitlab.com):" \
" Pipeline [#123](http://example.gitlab.com/pipelines/123)" \
" of branch [develop](http://example.gitlab.com/commits/develop)" \
" by The Hacker (hacker) has passed in 02:00:10"
)
end
end
end
end

View File

@ -292,7 +292,8 @@ describe MicrosoftTeamsService do
context 'when disabled' do
let(:pipeline) do
create(:ci_pipeline, :failed, project: project, ref: 'not-the-default-branch')
create(:ci_pipeline, :failed, project: project,
sha: project.commit.sha, ref: 'not-the-default-branch')
end
before do

View File

@ -220,7 +220,8 @@ shared_examples_for "chat service" do |service_name|
context "with not default branch" do
let(:pipeline) do
create(:ci_pipeline, project: project, status: "failed", ref: "not-the-default-branch")
create(:ci_pipeline, :failed, project: project,
sha: project.commit.sha, ref: "not-the-default-branch")
end
context "when notify_only_default_branch enabled" do

View File

@ -452,7 +452,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
context 'only notify for the default branch' do
context 'when enabled' do
let(:pipeline) do
create(:ci_pipeline, :failed, project: project, ref: 'not-the-default-branch')
create(:ci_pipeline, :failed, project: project, sha: project.commit.sha, ref: 'not-the-default-branch')
end
before do
@ -470,7 +470,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
context 'when disabled' do
let(:pipeline) do
create(:ci_pipeline, :failed, project: project, ref: 'not-the-default-branch')
create(:ci_pipeline, :failed, project: project, sha: project.commit.sha, ref: 'not-the-default-branch')
end
before do