gitlab-qa 10.4.1 → 11.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitlab/changelog_config.yml +13 -0
- data/.gitlab/merge_request_templates/Release.md +9 -36
- data/.rubocop_todo.yml +0 -12
- data/Dangerfile +1 -5
- data/Gemfile.lock +4 -4
- data/README.md +1 -2
- data/docs/running_against_remote_grid.md +39 -2
- data/gitlab-qa.gemspec +1 -1
- data/lib/gitlab/qa/component/gitaly_cluster.rb +0 -1
- data/lib/gitlab/qa/component/gitlab.rb +10 -9
- data/lib/gitlab/qa/component/selenoid.rb +8 -3
- data/lib/gitlab/qa/runtime/env.rb +21 -41
- data/lib/gitlab/qa/scenario/test/instance/airgapped.rb +0 -2
- data/lib/gitlab/qa/scenario/test/integration/gitaly_cluster.rb +0 -2
- data/lib/gitlab/qa/scenario/test/integration/mtls.rb +0 -1
- data/lib/gitlab/qa/scenario/test/integration/praefect.rb +0 -2
- data/lib/gitlab/qa/scenario/test/integration/registry_with_cdn.rb +2 -2
- data/lib/gitlab/qa/version.rb +1 -1
- data/lib/gitlab/qa.rb +0 -1
- data/support/data/admin_access_token_seed.rb +4 -1
- metadata +5 -39
- data/bin/slack +0 -14
- data/exe/gitlab-qa-report +0 -10
- data/lib/gitlab/qa/report/base_test_results.rb +0 -39
- data/lib/gitlab/qa/report/find_set_dri.rb +0 -43
- data/lib/gitlab/qa/report/generate_test_session.rb +0 -275
- data/lib/gitlab/qa/report/gitlab_issue_client.rb +0 -190
- data/lib/gitlab/qa/report/gitlab_issue_dry_client.rb +0 -28
- data/lib/gitlab/qa/report/j_unit_test_results.rb +0 -27
- data/lib/gitlab/qa/report/json_test_results.rb +0 -29
- data/lib/gitlab/qa/report/prepare_stage_reports.rb +0 -86
- data/lib/gitlab/qa/report/relate_failure_issue.rb +0 -374
- data/lib/gitlab/qa/report/report_as_issue.rb +0 -176
- data/lib/gitlab/qa/report/report_results.rb +0 -64
- data/lib/gitlab/qa/report/results_in_issues.rb +0 -126
- data/lib/gitlab/qa/report/results_in_testcases.rb +0 -111
- data/lib/gitlab/qa/report/results_reporter_shared.rb +0 -70
- data/lib/gitlab/qa/report/summary_table.rb +0 -43
- data/lib/gitlab/qa/report/test_result.rb +0 -184
- data/lib/gitlab/qa/report/update_screenshot_path.rb +0 -63
- data/lib/gitlab/qa/reporter.rb +0 -131
- data/lib/gitlab/qa/runtime/token_finder.rb +0 -44
- data/lib/gitlab/qa/slack/post_to_slack.rb +0 -30
- data/lib/gitlab/qa/system_logs/finders/json_log_finder.rb +0 -65
- data/lib/gitlab/qa/system_logs/finders/rails/api_log_finder.rb +0 -21
- data/lib/gitlab/qa/system_logs/finders/rails/application_log_finder.rb +0 -21
- data/lib/gitlab/qa/system_logs/finders/rails/exception_log_finder.rb +0 -21
- data/lib/gitlab/qa/system_logs/finders/rails/graphql_log_finder.rb +0 -21
- data/lib/gitlab/qa/system_logs/log_types/log.rb +0 -38
- data/lib/gitlab/qa/system_logs/log_types/rails/api_log.rb +0 -34
- data/lib/gitlab/qa/system_logs/log_types/rails/application_log.rb +0 -27
- data/lib/gitlab/qa/system_logs/log_types/rails/exception_log.rb +0 -23
- data/lib/gitlab/qa/system_logs/log_types/rails/graphql_log.rb +0 -30
- data/lib/gitlab/qa/system_logs/shared_fields.rb +0 -29
- data/lib/gitlab/qa/system_logs/system_logs_formatter.rb +0 -65
| @@ -1,184 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require 'active_support/core_ext/object/blank'
         | 
| 4 | 
            -
             | 
| 5 | 
            -
            module Gitlab
         | 
| 6 | 
            -
              module QA
         | 
| 7 | 
            -
                module Report
         | 
| 8 | 
            -
                  class TestResult
         | 
| 9 | 
            -
                    def self.from_json(report)
         | 
| 10 | 
            -
                      JsonTestResult.new(report)
         | 
| 11 | 
            -
                    end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                    def self.from_junit(report)
         | 
| 14 | 
            -
                      JUnitTestResult.new(report)
         | 
| 15 | 
            -
                    end
         | 
| 16 | 
            -
             | 
| 17 | 
            -
                    attr_accessor :report, :failures
         | 
| 18 | 
            -
             | 
| 19 | 
            -
                    def initialize(report)
         | 
| 20 | 
            -
                      self.report = report
         | 
| 21 | 
            -
                      self.failures = failures_from_exceptions
         | 
| 22 | 
            -
                    end
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                    def stage
         | 
| 25 | 
            -
                      @stage ||= file[%r{(?:api|browser_ui)/(?:(?:\d+_)?(\w+))}, 1]
         | 
| 26 | 
            -
                    end
         | 
| 27 | 
            -
             | 
| 28 | 
            -
                    def name
         | 
| 29 | 
            -
                      raise NotImplementedError
         | 
| 30 | 
            -
                    end
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                    def file
         | 
| 33 | 
            -
                      raise NotImplementedError
         | 
| 34 | 
            -
                    end
         | 
| 35 | 
            -
             | 
| 36 | 
            -
                    def skipped
         | 
| 37 | 
            -
                      raise NotImplementedError
         | 
| 38 | 
            -
                    end
         | 
| 39 | 
            -
             | 
| 40 | 
            -
                    private
         | 
| 41 | 
            -
             | 
| 42 | 
            -
                    def failures_from_exceptions
         | 
| 43 | 
            -
                      raise NotImplementedError
         | 
| 44 | 
            -
                    end
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                    class JsonTestResult < TestResult
         | 
| 47 | 
            -
                      def name
         | 
| 48 | 
            -
                        report['full_description']
         | 
| 49 | 
            -
                      end
         | 
| 50 | 
            -
             | 
| 51 | 
            -
                      def file
         | 
| 52 | 
            -
                        report['file_path']
         | 
| 53 | 
            -
                      end
         | 
| 54 | 
            -
             | 
| 55 | 
            -
                      def status
         | 
| 56 | 
            -
                        report['status']
         | 
| 57 | 
            -
                      end
         | 
| 58 | 
            -
             | 
| 59 | 
            -
                      def ci_job_url
         | 
| 60 | 
            -
                        report['ci_job_url']
         | 
| 61 | 
            -
                      end
         | 
| 62 | 
            -
             | 
| 63 | 
            -
                      def skipped
         | 
| 64 | 
            -
                        status == 'pending'
         | 
| 65 | 
            -
                      end
         | 
| 66 | 
            -
             | 
| 67 | 
            -
                      def testcase
         | 
| 68 | 
            -
                        report['testcase']
         | 
| 69 | 
            -
                      end
         | 
| 70 | 
            -
             | 
| 71 | 
            -
                      def testcase=(new_testcase)
         | 
| 72 | 
            -
                        report['testcase'] = new_testcase
         | 
| 73 | 
            -
                      end
         | 
| 74 | 
            -
             | 
| 75 | 
            -
                      def failure_issue
         | 
| 76 | 
            -
                        report['failure_issue']
         | 
| 77 | 
            -
                      end
         | 
| 78 | 
            -
             | 
| 79 | 
            -
                      def failure_issue=(new_failure_issue)
         | 
| 80 | 
            -
                        report['failure_issue'] = new_failure_issue
         | 
| 81 | 
            -
                      end
         | 
| 82 | 
            -
             | 
| 83 | 
            -
                      def quarantine?
         | 
| 84 | 
            -
                        # The value for 'quarantine' could be nil, a hash, a string,
         | 
| 85 | 
            -
                        # or true (if the test just has the :quarantine tag)
         | 
| 86 | 
            -
                        # But any non-nil or false value should means the test is in quarantine
         | 
| 87 | 
            -
                        report['quarantine'].present?
         | 
| 88 | 
            -
                      end
         | 
| 89 | 
            -
             | 
| 90 | 
            -
                      def quarantine_type
         | 
| 91 | 
            -
                        report['quarantine']['type'] if quarantine?
         | 
| 92 | 
            -
                      end
         | 
| 93 | 
            -
             | 
| 94 | 
            -
                      def quarantine_issue
         | 
| 95 | 
            -
                        report['quarantine']['issue'] if quarantine?
         | 
| 96 | 
            -
                      end
         | 
| 97 | 
            -
             | 
| 98 | 
            -
                      def screenshot?
         | 
| 99 | 
            -
                        report['screenshot'].present?
         | 
| 100 | 
            -
                      end
         | 
| 101 | 
            -
             | 
| 102 | 
            -
                      def failure_screenshot
         | 
| 103 | 
            -
                        report['screenshot']['image'] if screenshot?
         | 
| 104 | 
            -
                      end
         | 
| 105 | 
            -
             | 
| 106 | 
            -
                      def product_group?
         | 
| 107 | 
            -
                        report['product_group'].present?
         | 
| 108 | 
            -
                      end
         | 
| 109 | 
            -
             | 
| 110 | 
            -
                      def product_group
         | 
| 111 | 
            -
                        report['product_group'] if product_group?
         | 
| 112 | 
            -
                      end
         | 
| 113 | 
            -
             | 
| 114 | 
            -
                      private
         | 
| 115 | 
            -
             | 
| 116 | 
            -
                      # rubocop:disable Metrics/AbcSize
         | 
| 117 | 
            -
                      def failures_from_exceptions
         | 
| 118 | 
            -
                        return [] unless report.key?('exceptions')
         | 
| 119 | 
            -
             | 
| 120 | 
            -
                        report['exceptions'].map do |exception|
         | 
| 121 | 
            -
                          spec_file_first_index = exception['backtrace'].rindex do |line|
         | 
| 122 | 
            -
                            line.include?(File.basename(report['file_path']))
         | 
| 123 | 
            -
                          end
         | 
| 124 | 
            -
             | 
| 125 | 
            -
                          exception['message'].gsub!(/(private_token=)[\w-]+/, '********')
         | 
| 126 | 
            -
                          Array(exception['message_lines']).each { |line| line.gsub!(/(private_token=)([\w-]+)/, '********') }
         | 
| 127 | 
            -
             | 
| 128 | 
            -
                          {
         | 
| 129 | 
            -
                            'message' => "#{exception['class']}: #{exception['message']}",
         | 
| 130 | 
            -
                            'message_lines' => exception['message_lines'],
         | 
| 131 | 
            -
                            'stacktrace' => "#{format_message_lines(exception['message_lines'])}\n#{exception['backtrace'].slice(0..spec_file_first_index).join("\n")}",
         | 
| 132 | 
            -
                            'correlation_id' => exception['correlation_id']
         | 
| 133 | 
            -
                          }
         | 
| 134 | 
            -
                        end
         | 
| 135 | 
            -
                      end
         | 
| 136 | 
            -
             | 
| 137 | 
            -
                      def format_message_lines(message_lines)
         | 
| 138 | 
            -
                        message_lines.is_a?(Array) ? message_lines.join("\n") : message_lines
         | 
| 139 | 
            -
                      end
         | 
| 140 | 
            -
                      # rubocop:enable Metrics/AbcSize
         | 
| 141 | 
            -
                    end
         | 
| 142 | 
            -
             | 
| 143 | 
            -
                    class JUnitTestResult < TestResult
         | 
| 144 | 
            -
                      def name
         | 
| 145 | 
            -
                        report['name']
         | 
| 146 | 
            -
                      end
         | 
| 147 | 
            -
             | 
| 148 | 
            -
                      def file
         | 
| 149 | 
            -
                        report['file']
         | 
| 150 | 
            -
                      end
         | 
| 151 | 
            -
             | 
| 152 | 
            -
                      def skipped
         | 
| 153 | 
            -
                        report.search('skipped').any?
         | 
| 154 | 
            -
                      end
         | 
| 155 | 
            -
             | 
| 156 | 
            -
                      attr_accessor :testcase # Ignore it for now
         | 
| 157 | 
            -
             | 
| 158 | 
            -
                      private
         | 
| 159 | 
            -
             | 
| 160 | 
            -
                      # rubocop:disable Metrics/AbcSize
         | 
| 161 | 
            -
                      def failures_from_exceptions
         | 
| 162 | 
            -
                        failures = report.search('failure')
         | 
| 163 | 
            -
                        return [] if failures.empty?
         | 
| 164 | 
            -
             | 
| 165 | 
            -
                        failures.map do |exception|
         | 
| 166 | 
            -
                          trace = exception.content.split("\n").map(&:strip)
         | 
| 167 | 
            -
                          spec_file_first_index = trace.rindex do |line|
         | 
| 168 | 
            -
                            line.include?(File.basename(report['file']))
         | 
| 169 | 
            -
                          end
         | 
| 170 | 
            -
             | 
| 171 | 
            -
                          exception['message'].gsub!(/(private_token=)[\w-]+/, '********')
         | 
| 172 | 
            -
             | 
| 173 | 
            -
                          {
         | 
| 174 | 
            -
                            'message' => "#{exception['type']}: #{exception['message']}",
         | 
| 175 | 
            -
                            'stacktrace' => trace.slice(0..spec_file_first_index).join("\n")
         | 
| 176 | 
            -
                          }
         | 
| 177 | 
            -
                        end
         | 
| 178 | 
            -
                      end
         | 
| 179 | 
            -
                      # rubocop:enable Metrics/AbcSize
         | 
| 180 | 
            -
                    end
         | 
| 181 | 
            -
                  end
         | 
| 182 | 
            -
                end
         | 
| 183 | 
            -
              end
         | 
| 184 | 
            -
            end
         | 
| @@ -1,63 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require 'nokogiri'
         | 
| 4 | 
            -
            require 'json'
         | 
| 5 | 
            -
            require 'active_support/core_ext/object/blank'
         | 
| 6 | 
            -
             | 
| 7 | 
            -
            module Gitlab
         | 
| 8 | 
            -
              module QA
         | 
| 9 | 
            -
                module Report
         | 
| 10 | 
            -
                  class UpdateScreenshotPath
         | 
| 11 | 
            -
                    def initialize(files:)
         | 
| 12 | 
            -
                      @files = files
         | 
| 13 | 
            -
                    end
         | 
| 14 | 
            -
             | 
| 15 | 
            -
                    REGEX = %r{(?<gitlab_qa_run>gitlab-qa-run-.*?(?=\/))\/(?<gitlab_ce_ee_qa>gitlab-(ee|ce)-qa-.*?(?=\/))}
         | 
| 16 | 
            -
                    CONTAINER_PATH = File.join(Docker::Volumes::QA_CONTAINER_WORKDIR, 'tmp').freeze
         | 
| 17 | 
            -
             | 
| 18 | 
            -
                    def invoke!
         | 
| 19 | 
            -
                      Dir.glob(@files).each do |rspec_report_file|
         | 
| 20 | 
            -
                        match_data = rspec_report_file.match(REGEX)
         | 
| 21 | 
            -
                        artifact_path = "#{match_data[:gitlab_qa_run]}/#{match_data[:gitlab_ce_ee_qa]}"
         | 
| 22 | 
            -
             | 
| 23 | 
            -
                        xml_report = rewrite_each_xml_screenshot_path(rspec_report_file, artifact_path)
         | 
| 24 | 
            -
             | 
| 25 | 
            -
                        File.write(rspec_report_file, xml_report)
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                        puts "Saved #{rspec_report_file}"
         | 
| 28 | 
            -
             | 
| 29 | 
            -
                        json_rspec_report_file = rspec_report_file.gsub('.xml', '.json')
         | 
| 30 | 
            -
                        json_report = rewrite_each_json_screenshot_path(json_rspec_report_file, artifact_path)
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                        File.write(json_rspec_report_file, json_report)
         | 
| 33 | 
            -
             | 
| 34 | 
            -
                        puts "Saved #{json_rspec_report_file}"
         | 
| 35 | 
            -
                      end
         | 
| 36 | 
            -
                    end
         | 
| 37 | 
            -
             | 
| 38 | 
            -
                    private
         | 
| 39 | 
            -
             | 
| 40 | 
            -
                    def rewrite_each_xml_screenshot_path(rspec_report_file, artifact_path)
         | 
| 41 | 
            -
                      report = Nokogiri::XML(File.open(rspec_report_file))
         | 
| 42 | 
            -
             | 
| 43 | 
            -
                      report.xpath('//system-out').each do |system_out|
         | 
| 44 | 
            -
                        system_out.content = system_out.content.gsub(CONTAINER_PATH, artifact_path)
         | 
| 45 | 
            -
                      end
         | 
| 46 | 
            -
             | 
| 47 | 
            -
                      report.to_s
         | 
| 48 | 
            -
                    end
         | 
| 49 | 
            -
             | 
| 50 | 
            -
                    def rewrite_each_json_screenshot_path(json_rspec_report_file, artifact_path)
         | 
| 51 | 
            -
                      report = JSON.parse(File.read(json_rspec_report_file))
         | 
| 52 | 
            -
                      examples = report['examples']
         | 
| 53 | 
            -
             | 
| 54 | 
            -
                      examples.each do |example|
         | 
| 55 | 
            -
                        example['screenshot']['image'] = example['screenshot']['image'].gsub(CONTAINER_PATH, artifact_path) if example['screenshot'].present?
         | 
| 56 | 
            -
                      end
         | 
| 57 | 
            -
             | 
| 58 | 
            -
                      JSON.pretty_generate(report)
         | 
| 59 | 
            -
                    end
         | 
| 60 | 
            -
                  end
         | 
| 61 | 
            -
                end
         | 
| 62 | 
            -
              end
         | 
| 63 | 
            -
            end
         | 
    
        data/lib/gitlab/qa/reporter.rb
    DELETED
    
    | @@ -1,131 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require 'optparse'
         | 
| 4 | 
            -
             | 
| 5 | 
            -
            module Gitlab
         | 
| 6 | 
            -
              module QA
         | 
| 7 | 
            -
                class Reporter
         | 
| 8 | 
            -
                  # rubocop:disable Metrics/AbcSize
         | 
| 9 | 
            -
                  # rubocop:disable Metrics/PerceivedComplexity
         | 
| 10 | 
            -
                  # rubocop:disable Metrics/CyclomaticComplexity
         | 
| 11 | 
            -
                  def self.invoke(args)
         | 
| 12 | 
            -
                    report_options = {}
         | 
| 13 | 
            -
                    slack_options = {}
         | 
| 14 | 
            -
             | 
| 15 | 
            -
                    options = OptionParser.new do |opts|
         | 
| 16 | 
            -
                      opts.banner = 'Usage: gitlab-qa-reporter [options]'
         | 
| 17 | 
            -
             | 
| 18 | 
            -
                      opts.on('--prepare-stage-reports FILES', 'Prepare separate reports for each Stage from the provided JUnit XML files') do |files|
         | 
| 19 | 
            -
                        report_options[:prepare_stage_reports] = true
         | 
| 20 | 
            -
                        report_options[:input_files] = files if files
         | 
| 21 | 
            -
                      end
         | 
| 22 | 
            -
             | 
| 23 | 
            -
                      opts.on('--report-results FILES', String, 'Report test results from JUnit XML files in GitLab test cases and results issues') do |files|
         | 
| 24 | 
            -
                        report_options[:report_results] = true
         | 
| 25 | 
            -
                        report_options[:input_files] = files if files
         | 
| 26 | 
            -
                      end
         | 
| 27 | 
            -
             | 
| 28 | 
            -
                      opts.on('--relate-failure-issue FILES', String, 'Relate test failures to failure issues from RSpec JSON files') do |files|
         | 
| 29 | 
            -
                        report_options[:relate_failure_issue] = true
         | 
| 30 | 
            -
                        report_options[:input_files] = files if files
         | 
| 31 | 
            -
                      end
         | 
| 32 | 
            -
             | 
| 33 | 
            -
                      opts.on('--max-diff-ratio DIFF_RATO', Float, 'Max stacktrace diff ratio for QA failure issues detection. Used by with --relate-failure-issue') do |value|
         | 
| 34 | 
            -
                        report_options[:max_diff_ratio] = value
         | 
| 35 | 
            -
                      end
         | 
| 36 | 
            -
             | 
| 37 | 
            -
                      opts.on('-p', '--project PROJECT_ID', String, 'A valid project ID. Can be an integer or a group/project string. Required by --relate-failure-issue') do |value|
         | 
| 38 | 
            -
                        report_options[:project] = value
         | 
| 39 | 
            -
                      end
         | 
| 40 | 
            -
             | 
| 41 | 
            -
                      opts.on('--results-issue-project RESULTS_ISSUE_PROJECT_ID', String, 'A valid project ID. Can be an integer or a group/project string. Required by --report-results') do |value|
         | 
| 42 | 
            -
                        report_options[:results_issue_project] = value
         | 
| 43 | 
            -
                      end
         | 
| 44 | 
            -
             | 
| 45 | 
            -
                      opts.on('--test-case-project TEST_CASE_PROJECT_ID', String, 'A valid project ID. Can be an integer or a group/project string. Required by --report-results') do |value|
         | 
| 46 | 
            -
                        report_options[:test_case_project] = value
         | 
| 47 | 
            -
                      end
         | 
| 48 | 
            -
             | 
| 49 | 
            -
                      opts.on('--generate-test-session FILES', String, 'Generate test session report') do |files|
         | 
| 50 | 
            -
                        report_options[:generate_test_session] = true
         | 
| 51 | 
            -
                        report_options[:input_files] = files if files
         | 
| 52 | 
            -
                      end
         | 
| 53 | 
            -
             | 
| 54 | 
            -
                      opts.on('-t', '--token ACCESS_TOKEN', String, 'A valid access token. Required by --report-results and --relate-failure-issue') do |value|
         | 
| 55 | 
            -
                        report_options[:token] = value
         | 
| 56 | 
            -
                      end
         | 
| 57 | 
            -
             | 
| 58 | 
            -
                      opts.on('--post-to-slack MSG', 'Post message to slack') do |msg|
         | 
| 59 | 
            -
                        slack_options[:post_to_slack] = true
         | 
| 60 | 
            -
                        slack_options[:message] = msg
         | 
| 61 | 
            -
                      end
         | 
| 62 | 
            -
             | 
| 63 | 
            -
                      opts.on('--include-summary-table FILES', 'Create a results summary table to post to slack. To be used with --post-to-slack.') do |files|
         | 
| 64 | 
            -
                        raise 'This option should be used with --post-to-slack.' unless slack_options[:post_to_slack]
         | 
| 65 | 
            -
             | 
| 66 | 
            -
                        slack_options[:message] = slack_options[:message] + "\n\n" + Gitlab::QA::Report::SummaryTable.create(input_files: files)
         | 
| 67 | 
            -
                      end
         | 
| 68 | 
            -
             | 
| 69 | 
            -
                      opts.on('--include-system-log-errors FILES', String, 'Include errors from system logs in failure issues. To be used with --relate-failure-issue') do |files|
         | 
| 70 | 
            -
                        raise 'This option should be used with --relate-failure-issue.' unless report_options[:relate_failure_issue]
         | 
| 71 | 
            -
             | 
| 72 | 
            -
                        report_options[:system_logs] = files if files
         | 
| 73 | 
            -
                      end
         | 
| 74 | 
            -
             | 
| 75 | 
            -
                      opts.on('--update-screenshot-path FILES', "Update the path to screenshots to container's host") do |files|
         | 
| 76 | 
            -
                        report_options[:update_screenshot_path] = true
         | 
| 77 | 
            -
                        report_options[:files] = files
         | 
| 78 | 
            -
                      end
         | 
| 79 | 
            -
             | 
| 80 | 
            -
                      opts.on('--dry-run', "Perform a dry-run (don't create or update issues or test cases)") do |files|
         | 
| 81 | 
            -
                        report_options[:dry_run] = true
         | 
| 82 | 
            -
                      end
         | 
| 83 | 
            -
             | 
| 84 | 
            -
                      opts.on_tail('-v', '--version', 'Show the version') do
         | 
| 85 | 
            -
                        require 'gitlab/qa/version'
         | 
| 86 | 
            -
                        puts "#{$PROGRAM_NAME} : #{VERSION}"
         | 
| 87 | 
            -
                        exit
         | 
| 88 | 
            -
                      end
         | 
| 89 | 
            -
             | 
| 90 | 
            -
                      opts.on_tail('-h', '--help', 'Show the usage') do
         | 
| 91 | 
            -
                        puts opts
         | 
| 92 | 
            -
                        exit
         | 
| 93 | 
            -
                      end
         | 
| 94 | 
            -
             | 
| 95 | 
            -
                      opts.parse(args)
         | 
| 96 | 
            -
                    end
         | 
| 97 | 
            -
             | 
| 98 | 
            -
                    if args.any?
         | 
| 99 | 
            -
                      if report_options.delete(:prepare_stage_reports)
         | 
| 100 | 
            -
                        Gitlab::QA::Report::PrepareStageReports.new(**report_options).invoke!
         | 
| 101 | 
            -
             | 
| 102 | 
            -
                      elsif report_options.delete(:relate_failure_issue)
         | 
| 103 | 
            -
                        report_options[:token] = Runtime::TokenFinder.find_token!(report_options[:token])
         | 
| 104 | 
            -
                        Gitlab::QA::Report::RelateFailureIssue.new(**report_options).invoke!
         | 
| 105 | 
            -
             | 
| 106 | 
            -
                      elsif report_options.delete(:report_results)
         | 
| 107 | 
            -
                        report_options[:token] = Runtime::TokenFinder.find_token!(report_options[:token])
         | 
| 108 | 
            -
                        Gitlab::QA::Report::ReportResults.new(**report_options).invoke!
         | 
| 109 | 
            -
             | 
| 110 | 
            -
                      elsif report_options.delete(:generate_test_session)
         | 
| 111 | 
            -
                        report_options[:token] = Runtime::TokenFinder.find_token!(report_options[:token])
         | 
| 112 | 
            -
                        Gitlab::QA::Report::GenerateTestSession.new(**report_options).invoke!
         | 
| 113 | 
            -
             | 
| 114 | 
            -
                      elsif slack_options.delete(:post_to_slack)
         | 
| 115 | 
            -
                        Gitlab::QA::Slack::PostToSlack.new(**slack_options).invoke!
         | 
| 116 | 
            -
             | 
| 117 | 
            -
                      elsif report_options.delete(:update_screenshot_path)
         | 
| 118 | 
            -
                        Gitlab::QA::Report::UpdateScreenshotPath.new(**report_options).invoke!
         | 
| 119 | 
            -
             | 
| 120 | 
            -
                      end
         | 
| 121 | 
            -
                    else
         | 
| 122 | 
            -
                      puts options
         | 
| 123 | 
            -
                      exit 1
         | 
| 124 | 
            -
                    end
         | 
| 125 | 
            -
                  end
         | 
| 126 | 
            -
                  # rubocop:enable Metrics/CyclomaticComplexity
         | 
| 127 | 
            -
                  # rubocop:enable Metrics/PerceivedComplexity
         | 
| 128 | 
            -
                  # rubocop:enable Metrics/AbcSize
         | 
| 129 | 
            -
                end
         | 
| 130 | 
            -
              end
         | 
| 131 | 
            -
            end
         | 
| @@ -1,44 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module Gitlab
         | 
| 4 | 
            -
              module QA
         | 
| 5 | 
            -
                module Runtime
         | 
| 6 | 
            -
                  class TokenFinder
         | 
| 7 | 
            -
                    def self.find_token!(token, suffix: nil)
         | 
| 8 | 
            -
                      new(token, suffix).find_token!
         | 
| 9 | 
            -
                    end
         | 
| 10 | 
            -
             | 
| 11 | 
            -
                    attr_reader :token, :suffix
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                    def initialize(token, suffix)
         | 
| 14 | 
            -
                      @token = token
         | 
| 15 | 
            -
                      @suffix = suffix
         | 
| 16 | 
            -
                    end
         | 
| 17 | 
            -
             | 
| 18 | 
            -
                    def find_token!
         | 
| 19 | 
            -
                      find_token_from_attrs || find_token_from_env || find_token_from_file
         | 
| 20 | 
            -
                    end
         | 
| 21 | 
            -
             | 
| 22 | 
            -
                    def find_token_from_attrs
         | 
| 23 | 
            -
                      token
         | 
| 24 | 
            -
                    end
         | 
| 25 | 
            -
             | 
| 26 | 
            -
                    def find_token_from_env
         | 
| 27 | 
            -
                      Env.qa_access_token
         | 
| 28 | 
            -
                    end
         | 
| 29 | 
            -
             | 
| 30 | 
            -
                    def find_token_from_file
         | 
| 31 | 
            -
                      @token_from_file ||= File.read(token_file_path).strip
         | 
| 32 | 
            -
                    rescue Errno::ENOENT
         | 
| 33 | 
            -
                      fail "Please provide a valid access token with the `-t/--token` option, the `GITLAB_QA_ACCESS_TOKEN` environment variable, or in the `#{token_file_path}` file!"
         | 
| 34 | 
            -
                    end
         | 
| 35 | 
            -
             | 
| 36 | 
            -
                    private
         | 
| 37 | 
            -
             | 
| 38 | 
            -
                    def token_file_path
         | 
| 39 | 
            -
                      @token_file_path ||= File.expand_path("../api_token#{"_#{suffix}" if suffix}", __dir__)
         | 
| 40 | 
            -
                    end
         | 
| 41 | 
            -
                  end
         | 
| 42 | 
            -
                end
         | 
| 43 | 
            -
              end
         | 
| 44 | 
            -
            end
         | 
| @@ -1,30 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module Gitlab
         | 
| 4 | 
            -
              module QA
         | 
| 5 | 
            -
                module Slack
         | 
| 6 | 
            -
                  class PostToSlack
         | 
| 7 | 
            -
                    def initialize(message:, icon_emoji: Runtime::Env.slack_icon_emoji, channel: Runtime::Env.slack_qa_channel)
         | 
| 8 | 
            -
                      @channel = channel
         | 
| 9 | 
            -
                      @message = message
         | 
| 10 | 
            -
                      @icon_emoji = icon_emoji
         | 
| 11 | 
            -
                    end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                    def invoke!
         | 
| 14 | 
            -
                      Runtime::Env.require_slack_qa_channel! unless @channel
         | 
| 15 | 
            -
                      Runtime::Env.require_ci_slack_webhook_url!
         | 
| 16 | 
            -
             | 
| 17 | 
            -
                      params = {}
         | 
| 18 | 
            -
                      params['channel'] = @channel
         | 
| 19 | 
            -
                      params['username'] = "GitLab QA Bot"
         | 
| 20 | 
            -
                      params['icon_emoji'] = @icon_emoji
         | 
| 21 | 
            -
                      params['text'] = @message
         | 
| 22 | 
            -
             | 
| 23 | 
            -
                      url = Runtime::Env.ci_slack_webhook_url
         | 
| 24 | 
            -
             | 
| 25 | 
            -
                      Support::HttpRequest.make_http_request(method: 'post', url: url, params: params, show_response: true)
         | 
| 26 | 
            -
                    end
         | 
| 27 | 
            -
                  end
         | 
| 28 | 
            -
                end
         | 
| 29 | 
            -
              end
         | 
| 30 | 
            -
            end
         | 
| @@ -1,65 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require 'json'
         | 
| 4 | 
            -
             | 
| 5 | 
            -
            module Gitlab
         | 
| 6 | 
            -
              module QA
         | 
| 7 | 
            -
                module SystemLogs
         | 
| 8 | 
            -
                  module Finders
         | 
| 9 | 
            -
                    class JsonLogFinder
         | 
| 10 | 
            -
                      def initialize(base_path, file_path)
         | 
| 11 | 
            -
                        @base_path = base_path
         | 
| 12 | 
            -
                        @file_path = file_path
         | 
| 13 | 
            -
                      end
         | 
| 14 | 
            -
             | 
| 15 | 
            -
                      def find(correlation_id)
         | 
| 16 | 
            -
                        log_file_path = "#{@base_path}/#{@file_path}"
         | 
| 17 | 
            -
                        logs = []
         | 
| 18 | 
            -
             | 
| 19 | 
            -
                        if File.exist?(log_file_path) && !correlation_id.nil?
         | 
| 20 | 
            -
                          File.foreach(log_file_path) do |line|
         | 
| 21 | 
            -
                            begin
         | 
| 22 | 
            -
                              json_line = JSON.parse(line, symbolize_names: true)
         | 
| 23 | 
            -
                            rescue JSON::ParserError
         | 
| 24 | 
            -
                              Runtime::Logger.debug("JsonLogFinder#find attempted to parse invalid JSON: #{line}")
         | 
| 25 | 
            -
             | 
| 26 | 
            -
                              next
         | 
| 27 | 
            -
                            end
         | 
| 28 | 
            -
             | 
| 29 | 
            -
                            if (json_line[:correlation_id])&.casecmp?(correlation_id)
         | 
| 30 | 
            -
                              normalized_line = normalize_keys(json_line)
         | 
| 31 | 
            -
                              logs << new_log(normalized_line)
         | 
| 32 | 
            -
                            end
         | 
| 33 | 
            -
                          end
         | 
| 34 | 
            -
                        end
         | 
| 35 | 
            -
             | 
| 36 | 
            -
                        logs
         | 
| 37 | 
            -
                      end
         | 
| 38 | 
            -
             | 
| 39 | 
            -
                      def new_log(data)
         | 
| 40 | 
            -
                        raise 'abstract method new_log must be defined!'
         | 
| 41 | 
            -
                      end
         | 
| 42 | 
            -
             | 
| 43 | 
            -
                      private
         | 
| 44 | 
            -
             | 
| 45 | 
            -
                      def normalize_keys(json_line)
         | 
| 46 | 
            -
                        normalized_hash = {}
         | 
| 47 | 
            -
             | 
| 48 | 
            -
                        json_line.each_key do |old_key|
         | 
| 49 | 
            -
                          key_string = old_key.to_s
         | 
| 50 | 
            -
             | 
| 51 | 
            -
                          if key_string.include?('.')
         | 
| 52 | 
            -
                            normalized_key = key_string.tr('.', '_').to_sym
         | 
| 53 | 
            -
                            normalized_hash[normalized_key] = json_line[old_key]
         | 
| 54 | 
            -
                          else
         | 
| 55 | 
            -
                            normalized_hash[old_key] = json_line[old_key]
         | 
| 56 | 
            -
                          end
         | 
| 57 | 
            -
                        end
         | 
| 58 | 
            -
             | 
| 59 | 
            -
                        normalized_hash
         | 
| 60 | 
            -
                      end
         | 
| 61 | 
            -
                    end
         | 
| 62 | 
            -
                  end
         | 
| 63 | 
            -
                end
         | 
| 64 | 
            -
              end
         | 
| 65 | 
            -
            end
         | 
| @@ -1,21 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module Gitlab
         | 
| 4 | 
            -
              module QA
         | 
| 5 | 
            -
                module SystemLogs
         | 
| 6 | 
            -
                  module Finders
         | 
| 7 | 
            -
                    module Rails
         | 
| 8 | 
            -
                      class ApiLogFinder < JsonLogFinder
         | 
| 9 | 
            -
                        def initialize(base_path, file_path = 'gitlab-rails/api_json.log')
         | 
| 10 | 
            -
                          super(base_path, file_path)
         | 
| 11 | 
            -
                        end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                        def new_log(data)
         | 
| 14 | 
            -
                          LogTypes::Rails::ApiLog.new(data)
         | 
| 15 | 
            -
                        end
         | 
| 16 | 
            -
                      end
         | 
| 17 | 
            -
                    end
         | 
| 18 | 
            -
                  end
         | 
| 19 | 
            -
                end
         | 
| 20 | 
            -
              end
         | 
| 21 | 
            -
            end
         | 
| @@ -1,21 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module Gitlab
         | 
| 4 | 
            -
              module QA
         | 
| 5 | 
            -
                module SystemLogs
         | 
| 6 | 
            -
                  module Finders
         | 
| 7 | 
            -
                    module Rails
         | 
| 8 | 
            -
                      class ApplicationLogFinder < JsonLogFinder
         | 
| 9 | 
            -
                        def initialize(base_path, file_path = 'gitlab-rails/application_json.log')
         | 
| 10 | 
            -
                          super(base_path, file_path)
         | 
| 11 | 
            -
                        end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                        def new_log(data)
         | 
| 14 | 
            -
                          LogTypes::Rails::ApplicationLog.new(data)
         | 
| 15 | 
            -
                        end
         | 
| 16 | 
            -
                      end
         | 
| 17 | 
            -
                    end
         | 
| 18 | 
            -
                  end
         | 
| 19 | 
            -
                end
         | 
| 20 | 
            -
              end
         | 
| 21 | 
            -
            end
         | 
| @@ -1,21 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module Gitlab
         | 
| 4 | 
            -
              module QA
         | 
| 5 | 
            -
                module SystemLogs
         | 
| 6 | 
            -
                  module Finders
         | 
| 7 | 
            -
                    module Rails
         | 
| 8 | 
            -
                      class ExceptionLogFinder < JsonLogFinder
         | 
| 9 | 
            -
                        def initialize(base_path, file_path = 'gitlab-rails/exceptions_json.log')
         | 
| 10 | 
            -
                          super(base_path, file_path)
         | 
| 11 | 
            -
                        end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                        def new_log(data)
         | 
| 14 | 
            -
                          LogTypes::Rails::ExceptionLog.new(data)
         | 
| 15 | 
            -
                        end
         | 
| 16 | 
            -
                      end
         | 
| 17 | 
            -
                    end
         | 
| 18 | 
            -
                  end
         | 
| 19 | 
            -
                end
         | 
| 20 | 
            -
              end
         | 
| 21 | 
            -
            end
         | 
| @@ -1,21 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module Gitlab
         | 
| 4 | 
            -
              module QA
         | 
| 5 | 
            -
                module SystemLogs
         | 
| 6 | 
            -
                  module Finders
         | 
| 7 | 
            -
                    module Rails
         | 
| 8 | 
            -
                      class GraphqlLogFinder < JsonLogFinder
         | 
| 9 | 
            -
                        def initialize(base_path, file_path = 'gitlab-rails/graphql_json.log')
         | 
| 10 | 
            -
                          super(base_path, file_path)
         | 
| 11 | 
            -
                        end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                        def new_log(data)
         | 
| 14 | 
            -
                          LogTypes::Rails::GraphqlLog.new(data)
         | 
| 15 | 
            -
                        end
         | 
| 16 | 
            -
                      end
         | 
| 17 | 
            -
                    end
         | 
| 18 | 
            -
                  end
         | 
| 19 | 
            -
                end
         | 
| 20 | 
            -
              end
         | 
| 21 | 
            -
            end
         | 
| @@ -1,38 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module Gitlab
         | 
| 4 | 
            -
              module QA
         | 
| 5 | 
            -
                module SystemLogs
         | 
| 6 | 
            -
                  module LogTypes
         | 
| 7 | 
            -
                    class Log
         | 
| 8 | 
            -
                      def initialize(name, data)
         | 
| 9 | 
            -
                        @name = name
         | 
| 10 | 
            -
                        @data = data
         | 
| 11 | 
            -
                      end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                      attr_reader :name, :data
         | 
| 14 | 
            -
             | 
| 15 | 
            -
                      def summary_fields
         | 
| 16 | 
            -
                        [
         | 
| 17 | 
            -
                          :severity,
         | 
| 18 | 
            -
                          :correlation_id,
         | 
| 19 | 
            -
                          :time,
         | 
| 20 | 
            -
                          :message
         | 
| 21 | 
            -
                        ]
         | 
| 22 | 
            -
                      end
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                      def summary
         | 
| 25 | 
            -
                        summary = {}
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                        summary_fields.each do |field|
         | 
| 28 | 
            -
                          value = data[field]
         | 
| 29 | 
            -
                          summary[field] = value unless value.nil?
         | 
| 30 | 
            -
                        end
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                        summary
         | 
| 33 | 
            -
                      end
         | 
| 34 | 
            -
                    end
         | 
| 35 | 
            -
                  end
         | 
| 36 | 
            -
                end
         | 
| 37 | 
            -
              end
         | 
| 38 | 
            -
            end
         | 
| @@ -1,34 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module Gitlab
         | 
| 4 | 
            -
              module QA
         | 
| 5 | 
            -
                module SystemLogs
         | 
| 6 | 
            -
                  module LogTypes
         | 
| 7 | 
            -
                    module Rails
         | 
| 8 | 
            -
                      class ApiLog < Log
         | 
| 9 | 
            -
                        include SharedFields::Exception
         | 
| 10 | 
            -
                        include SharedFields::Meta
         | 
| 11 | 
            -
             | 
| 12 | 
            -
                        def initialize(data)
         | 
| 13 | 
            -
                          super('Rails API', data)
         | 
| 14 | 
            -
                        end
         | 
| 15 | 
            -
             | 
| 16 | 
            -
                        def summary_fields
         | 
| 17 | 
            -
                          super.concat(
         | 
| 18 | 
            -
                            [
         | 
| 19 | 
            -
                              :method,
         | 
| 20 | 
            -
                              :path,
         | 
| 21 | 
            -
                              :status,
         | 
| 22 | 
            -
                              :params,
         | 
| 23 | 
            -
                              :api_error
         | 
| 24 | 
            -
                            ],
         | 
| 25 | 
            -
                            exception_fields,
         | 
| 26 | 
            -
                            meta_fields
         | 
| 27 | 
            -
                          )
         | 
| 28 | 
            -
                        end
         | 
| 29 | 
            -
                      end
         | 
| 30 | 
            -
                    end
         | 
| 31 | 
            -
                  end
         | 
| 32 | 
            -
                end
         | 
| 33 | 
            -
              end
         | 
| 34 | 
            -
            end
         |