# frozen_string_literal: true require_relative './utils/constants' module Dri class Report # rubocop:disable Metrics/ClassLength include Dri::Utils::Constants::Triage::Labels using Refinements attr_reader :header, :failures, :labels, :labels_incidents, :incidents def initialize(config) @labels = ['Title', 'Issue', 'Pipelines', 'Stack Trace', 'Actions'] @labels_incidents = %w[Incident Service Status URL] @failures = [] @incidents = [] @date = Date.today @today = Date.today.strftime("%Y-%m-%d") @weekday = Date.today.strftime("%A") @header = nil @api_client = ApiClient.new(config) end def set_header(timezone, username) @header = "# #{timezone}, #{@weekday} - #{@date}\n posted by: @#{username}" end def add_incident(incident) title = incident["title"] url = incident["web_url"] labels = incident["labels"] status = 'N/A' service = 'N/A' labels.each do |label| status = label.gsub!(INCIDENT, ' ').to_s if label.include? INCIDENT service = label.gsub!(SERVICE, ' ').to_s if label.include? SERVICE end @incidents << [title, service, status, url] end def add_failure(failure, actions_opts = []) project_id = failure["project_id"] iid = failure["iid"] title = format_title(failure["title"]) link = failure["web_url"] labels = failure["labels"] description = failure["description"] related_mrs = @api_client.fetch_related_mrs(project_id, iid) emoji = classify_failure_emoji(failure["created_at"]) emojified_link = "#{emoji} #{link}" stack_blob = if description.empty? "No stack trace found" else description.split("### Stack trace").last.gsub(/\n|`|!|\[|\]/, '').squeeze(" ")[0...250] end stack_trace = ":link:[`#{stack_blob}...`](#{link}#stack-trace)" failure_type = filter_failure_type_labels(labels) assigned_status = assigned?(failure["assignees"]) pipelines = filter_pipeline_labels(labels) linked_pipelines = link_pipelines(project_id, iid, pipelines, description) actions_status = actions_status_template(failure_type, assigned_status, actions_opts) actions_fixes = actions_fixes_template(related_mrs) @failures << [title, emojified_link, linked_pipelines, stack_trace, "#{actions_status}#{actions_fixes}"] end private def link_pipelines(project_id, iid, pipelines, description) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength linked = [] label_pipeline_map = { 'gitlab.com' => '/quality/production', 'canary.gitlab.com' => '/quality/canary', 'canary.staging.gitlab.com' => '/quality/staging-canary', 'main' => '/gitlab-org/gitlab-qa-mirror', 'master' => '/gitlab-org/gitlab-qa-mirror', 'nightly' => '/quality/nightly', 'pre.gitlab.com' => '/quality/preprod', 'staging-ref' => '/quality/staging-ref', 'staging.gitlab.com' => '/quality/staging', 'release' => '/quality/release' } failure_notes = @api_client.fetch_failure_notes(project_id, iid) return if pipelines.empty? pipelines.each do |pipeline| next unless label_pipeline_map.has_key?(pipeline) pipeline_in_notes_found = false pipeline_link = '' pipeline_markdown = pipeline.gsub(/.gitlab.com/, '') failure_notes.each do |note| next unless note.body.include?(label_pipeline_map.fetch(pipeline)) pipeline_in_notes_found = true pipeline_link = URI.extract(note.body, %w[https]) end unless pipeline_in_notes_found links_description = URI.extract(description, %w[https]) pipeline_link = links_description.select { |link| link.include? label_pipeline_map.fetch(pipeline) } end unless pipeline_link.empty? pipeline_link_sanitized = pipeline_link.join.strip pipeline_markdown = "[#{pipeline_markdown}](#{pipeline_link_sanitized})" end linked << pipeline_markdown end linked.join(', ') end def actions_status_template(failure_type, assigned_status, actions_opts) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity/MethodLength quarantined = '' reproduced = '' transient = '' active_investigation = '' blocking_pipelines = '' wait_for_fix = '' notified_team = '' feature_flag = '' notified_set = '
  • [x] notified SET
  • ' if actions_opts.include? 'pinged SET' quarantined = '
  • [x] quarantined
  • ' if actions_opts.include? 'quarantined' reproduced = '
  • [x] reproduced
  • ' if actions_opts.include? 'reproduced' transient = '
  • [x] transient
  • ' if actions_opts.include? 'transient' active_investigation = '
  • [x] active investigation
  • ' if actions_opts.include? 'active investigation' blocking_pipelines = '
  • [x] is blocking pipelines
  • ' if actions_opts.include? 'blocking pipelines' wait_for_fix = '
  • [x] waiting for fix to merge
  • ' if actions_opts.include? 'awaiting for a fix to merge' notified_team = '
  • [x] notified the team
  • ' if actions_opts.include? 'notified the team' feature_flag = '
  • [x] due to feature flag
  • ' if actions_opts.include? 'due to feature flag' action_status = ["Status: