lib/release_tools/security/merge_requests_merger.rb (97 lines of code) (raw):
# frozen_string_literal: true
module ReleaseTools
module Security
# Merging valid security merge requests
class MergeRequestsMerger
include ::SemanticLogger::Loggable
# @param [ReleaseTools::Security::Client] client
def initialize(merge_default: false)
@client = ReleaseTools::Security::Client.new
@merge_default = merge_default
@status = nil
end
# Processes valid security merge requests:
#
# 1. Fetches security implementation issues that are ready
# to be processed.
# 2. Iterates over security implementation issues
# 3. If `merge_default`, only the merge request targeting the default branch is processed.
# Else, the backports and pending merge requests are processed.
def execute
security_implementation_issues.each do |security_issue|
process_merge_requests(security_issue)
end
post_security_issues_table
update_patch_release_status_metric
update_status(:success)
send_slack_notification
rescue StandardError => ex
logger.fatal('There has been an error. Check the logs for which MRs were not processed correctly.', error: ex)
update_status(:failed)
send_slack_notification
raise
end
private
def security_implementation_issues
Security::IssueCrawler
.new
.upcoming_security_issues_and_merge_requests
end
def process_merge_requests(security_issue)
if @merge_default
process_mr_targeting_default(security_issue)
else
process_pending_merge_requests(security_issue)
end
end
# Processes merge request targeting the default branch for GitLab Security
# issues by triggering a new pipeline and setting MWPS
def process_mr_targeting_default(security_issue)
return unless security_issue.allowed_to_early_merge?
logger.info('Processing merge request targeting default branch', issue: security_issue.web_url)
mr_default = security_issue.merge_request_targeting_default_branch
ReleaseTools::Security::MergeWhenPipelineSucceedsService
.new(@client, mr_default)
.execute
end
# Processes pending merge requests:
#
# - For GitLab Security issues, the backports are processed.
# - For the rest of the projects, all security merge requests
# are processed.
def process_pending_merge_requests(security_issue)
unless security_issue.default_merge_request_handled?
logger.fatal('Issue skipped. Default merge request has not been merged', issue: security_issue.web_url)
update_status(:failed)
return
end
logger.info('Merging pending merge_requests', issue: security_issue.web_url)
security_issue.pending_merge_requests.each do |merge_request|
merge_merge_request(merge_request)
end
end
def merge_merge_request(merge_request)
logger.trace(__method__, merge_request: merge_request.web_url)
return if SharedStatus.dry_run? || merge_request.state == 'merged'
merged_result = @client.accept_merge_request(
merge_request.project_id,
merge_request.iid,
squash: true,
should_remove_source_branch: true
)
if merged_result.respond_to?(:merge_commit_sha) && merged_result.merge_commit_sha.present?
logger.info("Merged security merge request", url: merge_request.web_url)
else
logger.fatal("Merge request #{merge_request.web_url} couldn't be merged")
update_status(:failed)
end
end
def post_security_issues_table
IssueTable::Service.new(@client).execute
end
def update_patch_release_status_metric
return if SharedStatus.dry_run?
ReleaseTools::Metrics::PatchReleaseStatus.new(status: :closed).execute
end
def update_status(value)
return if @status == :failed
@status = value
end
def send_slack_notification
job_type = if @merge_default
"Default merge"
else
"Merge"
end
ReleaseTools::Slack::ReleaseJobEndNotifier.new(
job_type: job_type,
status: @status,
release_type: :patch
).send_notification
end
end
end
end