gitlab-org--gitlab-foss/scripts/generate-failed-pipeline-slack-message.rb

175 lines
4.5 KiB
Ruby
Executable file

#!/usr/bin/env ruby
# frozen_string_literal: true
require 'optparse'
require 'json'
require_relative 'api/pipeline_failed_jobs'
class GenerateFailedPipelineSlackMessage
DEFAULT_OPTIONS = {
failed_pipeline_slack_message_file: 'failed_pipeline_slack_message.json',
incident_json_file: 'incident.json'
}.freeze
def initialize(options)
@incident_json_file = options.delete(:incident_json_file)
end
def execute
{
channel: ENV['SLACK_CHANNEL'],
username: "Failed pipeline reporter",
icon_emoji: ":boom:",
text: "*#{title}*",
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: "*#{title}*"
},
accessory: {
type: "button",
text: {
type: "plain_text",
text: incident_button_text
},
url: incident_button_link
}
},
{
type: "section",
text: {
type: "mrkdwn",
text: "*Branch*: #{branch_link}"
}
},
{
type: "section",
text: {
type: "mrkdwn",
text: "*Commit*: #{commit_link}"
}
},
{
type: "section",
text: {
type: "mrkdwn",
text: "*Triggered by* #{triggered_by_link} • *Source:* #{source} • *Duration:* #{pipeline_duration} minutes"
}
},
{
type: "section",
text: {
type: "mrkdwn",
text: "*Failed jobs (#{failed_jobs.size}):* #{failed_jobs_list}"
}
}
]
}
end
private
attr_reader :incident_json_file
def failed_jobs
@failed_jobs ||= PipelineFailedJobs.new(API::DEFAULT_OPTIONS.dup.merge(exclude_allowed_to_fail_jobs: true)).execute
end
def title
"#{project_link} pipeline #{pipeline_link} failed"
end
def incident_exist?
return @incident_exist if defined?(@incident_exist)
@incident_exist = File.exist?(incident_json_file)
end
def incident_button_text
if incident_exist?
'View incident'
else
'Create incident'
end
end
def incident_button_link
if incident_exist?
JSON.parse(File.read(incident_json_file))['web_url']
else
"#{ENV['CI_SERVER_URL']}/#{ENV['BROKEN_MASTER_INCIDENTS_PROJECT']}/-/issues/new?" \
"issuable_template=incident&issue%5Bissue_type%5D=incident"
end
end
def pipeline_link
"<#{ENV['CI_PIPELINE_URL']}|##{ENV['CI_PIPELINE_ID']}>"
end
def branch_link
"<#{ENV['CI_PROJECT_URL']}/-/commits/#{ENV['CI_COMMIT_REF_NAME']}|`#{ENV['CI_COMMIT_REF_NAME']}`>"
end
def pipeline_duration
((Time.now - Time.parse(ENV['CI_PIPELINE_CREATED_AT'])) / 60.to_f).round(2)
end
def commit_link
"<#{ENV['CI_PROJECT_URL']}/-/commit/#{ENV['CI_COMMIT_SHA']}|#{ENV['CI_COMMIT_TITLE']}>"
end
def source
"`#{ENV['CI_PIPELINE_SOURCE']}#{schedule_type}`"
end
def schedule_type
ENV['CI_PIPELINE_SOURCE'] == 'schedule' ? ": #{ENV['SCHEDULE_TYPE']}" : ''
end
def project_link
"<#{ENV['CI_PROJECT_URL']}|#{ENV['CI_PROJECT_PATH']}>"
end
def triggered_by_link
"<#{ENV['CI_SERVER_URL']}/#{ENV['GITLAB_USER_LOGIN']}|#{ENV['GITLAB_USER_NAME']}>"
end
def failed_jobs_list
failed_jobs.map { |job| "<#{job.web_url}|#{job.name}>" }.join(', ')
end
end
if $PROGRAM_NAME == __FILE__
options = GenerateFailedPipelineSlackMessage::DEFAULT_OPTIONS.dup
OptionParser.new do |opts|
opts.on("-i", "--incident-json-file file_path", String, "Path to a file where the incident JSON data "\
"can be found (defaults to "\
"`#{GenerateFailedPipelineSlackMessage::DEFAULT_OPTIONS[:incident_json_file]}`)") do |value|
options[:incident_json_file] = value
end
opts.on("-f", "--failed-pipeline-slack-message-file file_path", String, "Path to a file where to save the Slack "\
"message (defaults to "\
"`#{GenerateFailedPipelineSlackMessage::DEFAULT_OPTIONS[:failed_pipeline_slack_message_file]}`)") do |value|
options[:failed_pipeline_slack_message_file] = value
end
opts.on("-h", "--help", "Prints this help") do
puts opts
exit
end
end.parse!
failed_pipeline_slack_message_file = options.delete(:failed_pipeline_slack_message_file)
GenerateFailedPipelineSlackMessage.new(options).execute.tap do |message_payload|
if failed_pipeline_slack_message_file
File.write(failed_pipeline_slack_message_file, JSON.pretty_generate(message_payload))
end
end
end