Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/pipeline/create_test_failure_issues.rb')
-rwxr-xr-xscripts/pipeline/create_test_failure_issues.rb263
1 files changed, 0 insertions, 263 deletions
diff --git a/scripts/pipeline/create_test_failure_issues.rb b/scripts/pipeline/create_test_failure_issues.rb
deleted file mode 100755
index e4bcabb6223..00000000000
--- a/scripts/pipeline/create_test_failure_issues.rb
+++ /dev/null
@@ -1,263 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-require 'optparse'
-require 'json'
-require 'httparty'
-
-require_relative '../api/create_issue'
-require_relative '../api/find_issues'
-require_relative '../api/update_issue'
-
-class CreateTestFailureIssues
- DEFAULT_OPTIONS = {
- project: nil,
- tests_report_file: 'tests_report.json',
- issue_json_folder: 'tmp/issues/'
- }.freeze
-
- def initialize(options)
- @options = options
- end
-
- def execute
- puts "[CreateTestFailureIssues] No failed tests!" if failed_tests.empty?
-
- failed_tests.each_with_object([]) do |failed_test, existing_issues|
- CreateTestFailureIssue.new(options.dup).upsert(failed_test, existing_issues).tap do |issue|
- existing_issues << issue
- File.write(File.join(options[:issue_json_folder], "issue-#{issue.iid}.json"), JSON.pretty_generate(issue.to_h))
- end
- end
- end
-
- private
-
- attr_reader :options
-
- def failed_tests
- @failed_tests ||=
- if File.exist?(options[:tests_report_file])
- JSON.parse(File.read(options[:tests_report_file]))
- else
- puts "[CreateTestFailureIssues] #{options[:tests_report_file]} doesn't exist!"
- []
- end
- end
-end
-
-class CreateTestFailureIssue
- MAX_TITLE_LENGTH = 255
- WWW_GITLAB_COM_SITE = 'https://about.gitlab.com'
- WWW_GITLAB_COM_GROUPS_JSON = "#{WWW_GITLAB_COM_SITE}/groups.json".freeze
- WWW_GITLAB_COM_CATEGORIES_JSON = "#{WWW_GITLAB_COM_SITE}/categories.json".freeze
- FEATURE_CATEGORY_METADATA_REGEX = /(?<=feature_category: :)\w+/
- DEFAULT_LABELS = ['type::maintenance', 'test'].freeze
-
- def self.server_host
- @server_host ||= ENV.fetch('CI_SERVER_HOST', 'gitlab.com')
- end
-
- def self.project_path
- @project_path ||= ENV.fetch('CI_PROJECT_PATH', 'gitlab-org/gitlab')
- end
-
- def self.file_base_url
- @file_base_url ||= "https://#{server_host}/#{project_path}/-/blob/master/"
- end
-
- def self.report_item_regex
- @report_item_regex ||= %r{^1\. \d{4}-\d{2}-\d{2}: https://#{server_host}/#{project_path}/-/jobs/.+$}
- end
-
- def initialize(options)
- @project = options.delete(:project)
- @api_token = options.delete(:api_token)
- end
-
- def upsert(failed_test, existing_issues = [])
- existing_issue = find(failed_test, existing_issues)
-
- if existing_issue
- update_reports(existing_issue, failed_test)
- existing_issue
- else
- create(failed_test)
- end
- end
-
- private
-
- attr_reader :project, :api_token
-
- def find(failed_test, existing_issues = [])
- test_hash = failed_test_hash(failed_test)
- issue_from_existing_issues = existing_issues.find { |issue| issue.title.include?(test_hash) }
- issue_from_issue_tracker = FindIssues
- .new(project: project, api_token: api_token)
- .execute(state: :opened, search: test_hash, in: :title, per_page: 1)
- .first
-
- existing_issue = issue_from_existing_issues || issue_from_issue_tracker
-
- return unless existing_issue
-
- puts "[CreateTestFailureIssue] Found issue '#{existing_issue.title}': #{existing_issue.web_url}!"
-
- existing_issue
- end
-
- def update_reports(existing_issue, failed_test)
- # We count the number of existing reports.
- reports_count = existing_issue.description
- .scan(self.class.report_item_regex)
- .size.to_i + 1
-
- # We include the number of reports in the header, for visibility.
- issue_description = existing_issue.description.sub(/^### Reports.*$/, "### Reports (#{reports_count})")
-
- # We add the current failure to the list of reports.
- issue_description = "#{issue_description}\n#{report_list_item(failed_test)}"
-
- UpdateIssue
- .new(project: project, api_token: api_token)
- .execute(
- existing_issue.iid,
- description: issue_description,
- weight: reports_count
- )
- puts "[CreateTestFailureIssue] Added a report in '#{existing_issue.title}': #{existing_issue.web_url}!"
- end
-
- def create(failed_test)
- payload = {
- title: failed_test_issue_title(failed_test),
- description: failed_test_issue_description(failed_test),
- labels: failed_test_issue_labels(failed_test),
- weight: 1
- }
-
- CreateIssue.new(project: project, api_token: api_token).execute(payload).tap do |issue|
- puts "[CreateTestFailureIssue] Created issue '#{issue.title}': #{issue.web_url}!"
- end
- end
-
- def failed_test_hash(failed_test)
- Digest::SHA256.hexdigest(failed_test['file'] + failed_test['name'])[0...12]
- end
-
- def failed_test_issue_title(failed_test)
- title = "#{failed_test['file']} [test-hash:#{failed_test_hash(failed_test)}]"
-
- raise "Title is too long!" if title.size > MAX_TITLE_LENGTH
-
- title
- end
-
- def test_file_link(failed_test)
- "[`#{failed_test['file']}`](#{self.class.file_base_url}#{failed_test['file']})"
- end
-
- def report_list_item(failed_test)
- "1. #{Time.new.utc.strftime('%F')}: #{failed_test['job_url']} (#{ENV['CI_PIPELINE_URL']})"
- end
-
- def failed_test_issue_description(failed_test)
- <<~DESCRIPTION
- ### Test description
-
- `#{search_safe(failed_test['name'])}`
-
- ### Test file path
-
- #{test_file_link(failed_test)}
-
- <!-- Don't add anything after the report list since it's updated automatically -->
- ### Reports (1)
-
- #{report_list_item(failed_test)}
- DESCRIPTION
- end
-
- def failed_test_issue_labels(failed_test)
- labels = DEFAULT_LABELS + category_and_group_labels_for_test_file(failed_test['file'])
-
- # make sure we don't spam people who are notified to actual labels
- labels.map { |label| "wip-#{label}" }
- end
-
- def category_and_group_labels_for_test_file(test_file)
- feature_categories = File.open(File.expand_path(File.join('..', '..', test_file), __dir__))
- .read
- .scan(FEATURE_CATEGORY_METADATA_REGEX)
-
- category_labels = feature_categories.filter_map { |category| categories_mapping.dig(category, 'label') }.uniq
-
- groups = feature_categories.filter_map { |category| categories_mapping.dig(category, 'group') }
- group_labels = groups.map { |group| groups_mapping.dig(group, 'label') }.uniq
-
- (category_labels + [group_labels.first]).compact
- end
-
- def categories_mapping
- @categories_mapping ||= self.class.fetch_json(WWW_GITLAB_COM_CATEGORIES_JSON)
- end
-
- def groups_mapping
- @groups_mapping ||= self.class.fetch_json(WWW_GITLAB_COM_GROUPS_JSON)
- end
-
- def search_safe(value)
- value.delete('"')
- end
-
- def self.fetch_json(json_url)
- json = with_retries { HTTParty.get(json_url, format: :plain) } # rubocop:disable Gitlab/HTTParty
- JSON.parse(json)
- end
-
- def self.with_retries(attempts: 3)
- yield
- rescue Errno::ECONNRESET, OpenSSL::SSL::SSLError, Net::OpenTimeout
- retry if (attempts -= 1) > 0
- raise
- end
- private_class_method :with_retries
-end
-
-if $PROGRAM_NAME == __FILE__
- options = CreateTestFailureIssues::DEFAULT_OPTIONS.dup
-
- OptionParser.new do |opts|
- opts.on("-p", "--project PROJECT", String,
- "Project where to create the issue (defaults to " \
- "`#{CreateTestFailureIssues::DEFAULT_OPTIONS[:project]}`)") do |value|
- options[:project] = value
- end
-
- opts.on("-r", "--tests-report-file file_path", String,
- "Path to a JSON file which contains the current pipeline's tests report (defaults to " \
- "`#{CreateTestFailureIssues::DEFAULT_OPTIONS[:tests_report_file]}`)"
- ) do |value|
- options[:tests_report_file] = value
- end
-
- opts.on("-f", "--issues-json-folder file_path", String,
- "Path to a folder where to save the issues JSON data (defaults to " \
- "`#{CreateTestFailureIssues::DEFAULT_OPTIONS[:issue_json_folder]}`)") do |value|
- options[:issue_json_folder] = value
- end
-
- opts.on("-t", "--api-token API_TOKEN", String,
- "A valid Project token with the `Reporter` role and `api` scope to create the issue") do |value|
- options[:api_token] = value
- end
-
- opts.on("-h", "--help", "Prints this help") do
- puts opts
- exit
- end
- end.parse!
-
- CreateTestFailureIssues.new(options).execute
-end