# frozen_string_literal: true module GitlabQuality module TestTooling module TestMeta module Processor class AddToBlockingProcessor < MetaProcessor BLOCKING_METADATA = ", :blocking%{suffix}" class << self # Execute the processor # # @param [Hash] spec the spec to update # @param [TestMetaUpdater] context instance of TestMetaUpdater def execute(spec, context) # rubocop:disable Metrics/AbcSize @context = context @existing_mrs = nil @file_path = spec["file_path"] testcase = spec["testcase"] devops_stage = spec["stage"] product_group = spec["product_group"] @example_name = spec["name"] @mr_title = format("%{prefix} %{example_name}", prefix: '[PROMOTE TO BLOCKING]', example_name: example_name).truncate(72, omission: '') @file_contents = context.get_file_contents(file_path) new_content, @changed_line_no = add_blocking_metadata return unless proceed_with_merge_request? branch = context.create_branch("blocking-promotion-#{SecureRandom.hex(4)}", example_name, context.ref) context.commit_changes(branch, <<~COMMIT_MESSAGE, file_path, new_content) Promote end-to-end test to blocking #{"Promote to blocking: #{example_name}".truncate(72)} COMMIT_MESSAGE reviewer_id, assignee_handle = context.fetch_dri_id(product_group, devops_stage) gitlab_bot_user_id = context.user_id_for_username(Runtime::Env.gitlab_bot_username) merge_request = context.create_merge_request(mr_title, branch, gitlab_bot_user_id, [reviewer_id]) do <<~MARKDOWN ## What does this MR do? Promotes the test [`#{example_name}`](https://gitlab.com/#{context.project}/-/blob/#{context.ref}/#{file_path}#L#{changed_line_no + 1}) to the blocking bucket This test was identified in the reliable e2e test report: #{context.report_issue} [Testcase link](#{testcase}) [Spec metrics link](#{context.single_spec_metrics_link(example_name)}) /label ~"Quality" ~"QA" ~"type::maintenance" /label ~"devops::#{devops_stage}" #{context.label_from_product_group(product_group)}
(This MR was automatically generated by [`gitlab_quality-test_tooling`](https://gitlab.com/gitlab-org/ruby/gems/gitlab_quality-test_tooling) at #{Time.now.utc})
MARKDOWN end context.post_note_on_merge_request(<<~MARKDOWN, merge_request.iid) @#{assignee_handle} Please review this MR, approve and assign it to a maintainer. If you think this MR should not be merged, please close it and add a note of the reason to the blocking report: #{context.report_issue} MARKDOWN if merge_request context.add_processed_record({ file_path => changed_line_no }) Runtime::Logger.info(" Created MR for promotion to blocking: #{merge_request.web_url}") end merge_request end # Performs post processing. Takes a list of MRs and posts them in a note on report_issue # # @param [Gitlab::ObjectifiedHash] merge_requests def post_process(merge_requests) web_urls = merge_requests.compact.map { |mr| "- #{mr.web_url}\n" }.join return if web_urls.empty? context.post_note_on_report_issue(<<~ISSUE_NOTE) The following merge requests have been created to promote stable specs to blocking: #{web_urls} ISSUE_NOTE end private attr_reader :context, :file_path, :file_contents, :example_name, :mr_title, :changed_line_no # Checks if there is already an MR open # # @return [Boolean] def proceed_with_merge_request? # rubocop:disable Metrics/AbcSize if changed_line_no.negative? Runtime::Logger.info(" No lines were changed in #{file_path}. Will not proceed with creating MR.") return false elsif context.record_processed?(file_path, changed_line_no) Runtime::Logger.info(" Record already processed for #{file_path}:#{changed_line_no}. Will not proceed with creating MR.") return false elsif existing_mrs&.any? Runtime::Logger.info(" An open MR already exists for '#{example_name}': #{existing_mrs.first['web_url']}. Will not proceed with creating MR.") return false end true end # Add blocking metadata to the file content and replace it # # @return [Array] first value holds the new content, the second value holds the line number of the test def add_blocking_metadata # rubocop:disable Metrics/AbcSize matched_lines = context.find_example_match_lines(file_contents, example_name) if matched_lines.any? { |line| line[0].include?(':blocking') } puts "Example '#{example_name}' is already blocking" return [file_contents, -1] end context.update_matched_line(matched_lines.last, file_contents.dup) do |line| if line.sub(DESCRIPTION_REGEX, '').include?(',') line[line.index(',', end_of_description_index(line))] = format(BLOCKING_METADATA, suffix: ',') else line[line.rindex(' ')] = format(BLOCKING_METADATA, suffix: ' ') end line end end end end end end end end