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 'lib/gitlab/danger')
-rw-r--r--lib/gitlab/danger/base_linter.rb96
-rw-r--r--lib/gitlab/danger/changelog.rb92
-rw-r--r--lib/gitlab/danger/commit_linter.rb158
-rw-r--r--lib/gitlab/danger/emoji_checker.rb45
-rw-r--r--lib/gitlab/danger/helper.rb273
-rw-r--r--lib/gitlab/danger/merge_request_linter.rb36
-rw-r--r--lib/gitlab/danger/request_helper.rb23
-rw-r--r--lib/gitlab/danger/roulette.rb169
-rw-r--r--lib/gitlab/danger/sidekiq_queues.rb37
-rw-r--r--lib/gitlab/danger/teammate.rb117
-rw-r--r--lib/gitlab/danger/title_linting.rb23
-rw-r--r--lib/gitlab/danger/weightage.rb10
-rw-r--r--lib/gitlab/danger/weightage/maintainers.rb33
-rw-r--r--lib/gitlab/danger/weightage/reviewers.rb65
14 files changed, 0 insertions, 1177 deletions
diff --git a/lib/gitlab/danger/base_linter.rb b/lib/gitlab/danger/base_linter.rb
deleted file mode 100644
index 898434724bd..00000000000
--- a/lib/gitlab/danger/base_linter.rb
+++ /dev/null
@@ -1,96 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'title_linting'
-
-module Gitlab
- module Danger
- class BaseLinter
- MIN_SUBJECT_WORDS_COUNT = 3
- MAX_LINE_LENGTH = 72
-
- attr_reader :commit, :problems
-
- def self.problems_mapping
- {
- subject_too_short: "The %s must contain at least #{MIN_SUBJECT_WORDS_COUNT} words",
- subject_too_long: "The %s may not be longer than #{MAX_LINE_LENGTH} characters",
- subject_starts_with_lowercase: "The %s must start with a capital letter",
- subject_ends_with_a_period: "The %s must not end with a period"
- }
- end
-
- def self.subject_description
- 'commit subject'
- end
-
- def initialize(commit)
- @commit = commit
- @problems = {}
- end
-
- def failed?
- problems.any?
- end
-
- def add_problem(problem_key, *args)
- @problems[problem_key] = sprintf(self.class.problems_mapping[problem_key], *args)
- end
-
- def lint_subject
- if subject_too_short?
- add_problem(:subject_too_short, self.class.subject_description)
- end
-
- if subject_too_long?
- add_problem(:subject_too_long, self.class.subject_description)
- end
-
- if subject_starts_with_lowercase?
- add_problem(:subject_starts_with_lowercase, self.class.subject_description)
- end
-
- if subject_ends_with_a_period?
- add_problem(:subject_ends_with_a_period, self.class.subject_description)
- end
-
- self
- end
-
- private
-
- def subject
- TitleLinting.remove_draft_flag(message_parts[0])
- end
-
- def subject_too_short?
- subject.split(' ').length < MIN_SUBJECT_WORDS_COUNT
- end
-
- def subject_too_long?
- line_too_long?(subject)
- end
-
- def line_too_long?(line)
- line.length > MAX_LINE_LENGTH
- end
-
- def subject_starts_with_lowercase?
- return false if ('A'..'Z').cover?(subject[0])
-
- first_char = subject.sub(/\A(\[.+\]|\w+:)\s/, '')[0]
- first_char_downcased = first_char.downcase
- return true unless ('a'..'z').cover?(first_char_downcased)
-
- first_char.downcase == first_char
- end
-
- def subject_ends_with_a_period?
- subject.end_with?('.')
- end
-
- def message_parts
- @message_parts ||= commit.message.split("\n", 3)
- end
- end
- end
-end
diff --git a/lib/gitlab/danger/changelog.rb b/lib/gitlab/danger/changelog.rb
deleted file mode 100644
index 4b85775ed98..00000000000
--- a/lib/gitlab/danger/changelog.rb
+++ /dev/null
@@ -1,92 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'title_linting'
-
-module Gitlab
- module Danger
- module Changelog
- NO_CHANGELOG_LABELS = [
- 'tooling',
- 'tooling::pipelines',
- 'tooling::workflow',
- 'ci-build',
- 'meta'
- ].freeze
- NO_CHANGELOG_CATEGORIES = %i[docs none].freeze
- CREATE_CHANGELOG_COMMAND = 'bin/changelog -m %<mr_iid>s "%<mr_title>s"'
- CREATE_EE_CHANGELOG_COMMAND = 'bin/changelog --ee -m %<mr_iid>s "%<mr_title>s"'
- CHANGELOG_MODIFIED_URL_TEXT = "**CHANGELOG.md was edited.** Please remove the additions and create a CHANGELOG entry.\n\n"
- CHANGELOG_MISSING_URL_TEXT = "**[CHANGELOG missing](https://docs.gitlab.com/ee/development/changelog.html)**:\n\n"
-
- OPTIONAL_CHANGELOG_MESSAGE = <<~MSG
- If you want to create a changelog entry for GitLab FOSS, run the following:
-
- #{CREATE_CHANGELOG_COMMAND}
-
- If you want to create a changelog entry for GitLab EE, run the following instead:
-
- #{CREATE_EE_CHANGELOG_COMMAND}
-
- If this merge request [doesn't need a CHANGELOG entry](https://docs.gitlab.com/ee/development/changelog.html#what-warrants-a-changelog-entry), feel free to ignore this message.
- MSG
-
- REQUIRED_CHANGELOG_MESSAGE = <<~MSG
- To create a changelog entry, run the following:
-
- #{CREATE_CHANGELOG_COMMAND}
-
- This merge request requires a changelog entry because it [introduces a database migration](https://docs.gitlab.com/ee/development/changelog.html#what-warrants-a-changelog-entry).
- MSG
-
- def required?
- git.added_files.any? { |path| path =~ %r{\Adb/(migrate|post_migrate)/} }
- end
- alias_method :db_changes?, :required?
-
- def optional?
- categories_need_changelog? && without_no_changelog_label?
- end
-
- def found
- @found ||= git.added_files.find { |path| path =~ %r{\A(ee/)?(changelogs/unreleased)(-ee)?/} }
- end
-
- def ee_changelog?
- found.start_with?('ee/')
- end
-
- def modified_text
- CHANGELOG_MODIFIED_URL_TEXT +
- format(OPTIONAL_CHANGELOG_MESSAGE, mr_iid: mr_iid, mr_title: sanitized_mr_title)
- end
-
- def required_text
- CHANGELOG_MISSING_URL_TEXT +
- format(REQUIRED_CHANGELOG_MESSAGE, mr_iid: mr_iid, mr_title: sanitized_mr_title)
- end
-
- def optional_text
- CHANGELOG_MISSING_URL_TEXT +
- format(OPTIONAL_CHANGELOG_MESSAGE, mr_iid: mr_iid, mr_title: sanitized_mr_title)
- end
-
- private
-
- def mr_iid
- gitlab.mr_json["iid"]
- end
-
- def sanitized_mr_title
- TitleLinting.sanitize_mr_title(gitlab.mr_json["title"])
- end
-
- def categories_need_changelog?
- (helper.changes_by_category.keys - NO_CHANGELOG_CATEGORIES).any?
- end
-
- def without_no_changelog_label?
- (gitlab.mr_labels & NO_CHANGELOG_LABELS).empty?
- end
- end
- end
-end
diff --git a/lib/gitlab/danger/commit_linter.rb b/lib/gitlab/danger/commit_linter.rb
deleted file mode 100644
index e23f5900433..00000000000
--- a/lib/gitlab/danger/commit_linter.rb
+++ /dev/null
@@ -1,158 +0,0 @@
-# frozen_string_literal: true
-
-emoji_checker_path = File.expand_path('emoji_checker', __dir__)
-base_linter_path = File.expand_path('base_linter', __dir__)
-
-if defined?(Rails)
- require_dependency(base_linter_path)
- require_dependency(emoji_checker_path)
-else
- require_relative(base_linter_path)
- require_relative(emoji_checker_path)
-end
-
-module Gitlab
- module Danger
- class CommitLinter < BaseLinter
- MAX_CHANGED_FILES_IN_COMMIT = 3
- MAX_CHANGED_LINES_IN_COMMIT = 30
- SHORT_REFERENCE_REGEX = %r{([\w\-\/]+)?(?<!`)(#|!|&|%)\d+(?<!`)}.freeze
-
- def self.problems_mapping
- super.merge(
- {
- separator_missing: "The commit subject and body must be separated by a blank line",
- details_too_many_changes: "Commits that change #{MAX_CHANGED_LINES_IN_COMMIT} or more lines across " \
- "at least #{MAX_CHANGED_FILES_IN_COMMIT} files must describe these changes in the commit body",
- details_line_too_long: "The commit body should not contain more than #{MAX_LINE_LENGTH} characters per line",
- message_contains_text_emoji: "Avoid the use of Markdown Emoji such as `:+1:`. These add limited value " \
- "to the commit message, and are displayed as plain text outside of GitLab",
- message_contains_unicode_emoji: "Avoid the use of Unicode Emoji. These add no value to the commit " \
- "message, and may not be displayed properly everywhere",
- message_contains_short_reference: "Use full URLs instead of short references (`gitlab-org/gitlab#123` or " \
- "`!123`), as short references are displayed as plain text outside of GitLab"
- }
- )
- end
-
- def initialize(commit)
- super
-
- @linted = false
- end
-
- def fixup?
- commit.message.start_with?('fixup!', 'squash!')
- end
-
- def suggestion?
- commit.message.start_with?('Apply suggestion to')
- end
-
- def merge?
- commit.message.start_with?('Merge branch')
- end
-
- def revert?
- commit.message.start_with?('Revert "')
- end
-
- def multi_line?
- !details.nil? && !details.empty?
- end
-
- def lint
- return self if @linted
-
- @linted = true
- lint_subject
- lint_separator
- lint_details
- lint_message
-
- self
- end
-
- private
-
- def lint_separator
- return self unless separator && !separator.empty?
-
- add_problem(:separator_missing)
-
- self
- end
-
- def lint_details
- if !multi_line? && many_changes?
- add_problem(:details_too_many_changes)
- end
-
- details&.each_line do |line|
- line_without_urls = line.strip.gsub(%r{https?://\S+}, '')
-
- # If the line includes a URL, we'll allow it to exceed MAX_LINE_LENGTH characters, but
- # only if the line _without_ the URL does not exceed this limit.
- next unless line_too_long?(line_without_urls)
-
- add_problem(:details_line_too_long)
- break
- end
-
- self
- end
-
- def lint_message
- if message_contains_text_emoji?
- add_problem(:message_contains_text_emoji)
- end
-
- if message_contains_unicode_emoji?
- add_problem(:message_contains_unicode_emoji)
- end
-
- if message_contains_short_reference?
- add_problem(:message_contains_short_reference)
- end
-
- self
- end
-
- def files_changed
- commit.diff_parent.stats[:total][:files]
- end
-
- def lines_changed
- commit.diff_parent.stats[:total][:lines]
- end
-
- def many_changes?
- files_changed > MAX_CHANGED_FILES_IN_COMMIT && lines_changed > MAX_CHANGED_LINES_IN_COMMIT
- end
-
- def separator
- message_parts[1]
- end
-
- def details
- message_parts[2]&.gsub(/^Signed-off-by.*$/, '')
- end
-
- def message_contains_text_emoji?
- emoji_checker.includes_text_emoji?(commit.message)
- end
-
- def message_contains_unicode_emoji?
- emoji_checker.includes_unicode_emoji?(commit.message)
- end
-
- def message_contains_short_reference?
- commit.message.match?(SHORT_REFERENCE_REGEX)
- end
-
- def emoji_checker
- @emoji_checker ||= Gitlab::Danger::EmojiChecker.new
- end
- end
- end
-end
diff --git a/lib/gitlab/danger/emoji_checker.rb b/lib/gitlab/danger/emoji_checker.rb
deleted file mode 100644
index e31a6ae5011..00000000000
--- a/lib/gitlab/danger/emoji_checker.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-# frozen_string_literal: true
-
-require 'json'
-
-module Gitlab
- module Danger
- class EmojiChecker
- DIGESTS = File.expand_path('../../../fixtures/emojis/digests.json', __dir__)
- ALIASES = File.expand_path('../../../fixtures/emojis/aliases.json', __dir__)
-
- # A regex that indicates a piece of text _might_ include an Emoji. The regex
- # alone is not enough, as we'd match `:foo:bar:baz`. Instead, we use this
- # regex to save us from having to check for all possible emoji names when we
- # know one definitely is not included.
- LIKELY_EMOJI = /:[\+a-z0-9_\-]+:/.freeze
-
- UNICODE_EMOJI_REGEX = %r{(
- [\u{1F300}-\u{1F5FF}] |
- [\u{1F1E6}-\u{1F1FF}] |
- [\u{2700}-\u{27BF}] |
- [\u{1F900}-\u{1F9FF}] |
- [\u{1F600}-\u{1F64F}] |
- [\u{1F680}-\u{1F6FF}] |
- [\u{2600}-\u{26FF}]
- )}x.freeze
-
- def initialize
- names = JSON.parse(File.read(DIGESTS)).keys +
- JSON.parse(File.read(ALIASES)).keys
-
- @emoji = names.map { |name| ":#{name}:" }
- end
-
- def includes_text_emoji?(text)
- return false unless text.match?(LIKELY_EMOJI)
-
- @emoji.any? { |emoji| text.include?(emoji) }
- end
-
- def includes_unicode_emoji?(text)
- text.match?(UNICODE_EMOJI_REGEX)
- end
- end
- end
-end
diff --git a/lib/gitlab/danger/helper.rb b/lib/gitlab/danger/helper.rb
deleted file mode 100644
index 09e013e24b8..00000000000
--- a/lib/gitlab/danger/helper.rb
+++ /dev/null
@@ -1,273 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'teammate'
-require_relative 'title_linting'
-
-module Gitlab
- module Danger
- module Helper
- RELEASE_TOOLS_BOT = 'gitlab-release-tools-bot'
-
- # Returns a list of all files that have been added, modified or renamed.
- # `git.modified_files` might contain paths that already have been renamed,
- # so we need to remove them from the list.
- #
- # Considering these changes:
- #
- # - A new_file.rb
- # - D deleted_file.rb
- # - M modified_file.rb
- # - R renamed_file_before.rb -> renamed_file_after.rb
- #
- # it will return
- # ```
- # [ 'new_file.rb', 'modified_file.rb', 'renamed_file_after.rb' ]
- # ```
- #
- # @return [Array<String>]
- def all_changed_files
- Set.new
- .merge(git.added_files.to_a)
- .merge(git.modified_files.to_a)
- .merge(git.renamed_files.map { |x| x[:after] })
- .subtract(git.renamed_files.map { |x| x[:before] })
- .to_a
- .sort
- end
-
- # Returns a string containing changed lines as git diff
- #
- # Considering changing a line in lib/gitlab/usage_data.rb it will return:
- #
- # [ "--- a/lib/gitlab/usage_data.rb",
- # "+++ b/lib/gitlab/usage_data.rb",
- # "+ # Test change",
- # "- # Old change" ]
- def changed_lines(changed_file)
- diff = git.diff_for_file(changed_file)
- return [] unless diff
-
- diff.patch.split("\n").select { |line| %r{^[+-]}.match?(line) }
- end
-
- def all_ee_changes
- all_changed_files.grep(%r{\Aee/})
- end
-
- def ee?
- # Support former project name for `dev` and support local Danger run
- %w[gitlab gitlab-ee].include?(ENV['CI_PROJECT_NAME']) || Dir.exist?(File.expand_path('../../../ee', __dir__))
- end
-
- def gitlab_helper
- # Unfortunately the following does not work:
- # - respond_to?(:gitlab)
- # - respond_to?(:gitlab, true)
- gitlab
- rescue NameError
- nil
- end
-
- def release_automation?
- gitlab_helper&.mr_author == RELEASE_TOOLS_BOT
- end
-
- def project_name
- ee? ? 'gitlab' : 'gitlab-foss'
- end
-
- def markdown_list(items)
- list = items.map { |item| "* `#{item}`" }.join("\n")
-
- if items.size > 10
- "\n<details>\n\n#{list}\n\n</details>\n"
- else
- list
- end
- end
-
- # @return [Hash<String,Array<String>>]
- def changes_by_category
- all_changed_files.each_with_object(Hash.new { |h, k| h[k] = [] }) do |file, hash|
- categories_for_file(file).each { |category| hash[category] << file }
- end
- end
-
- # Determines the categories a file is in, e.g., `[:frontend]`, `[:backend]`, or `%i[frontend engineering_productivity]`
- # using filename regex and specific change regex if given.
- #
- # @return Array<Symbol>
- def categories_for_file(file)
- _, categories = CATEGORIES.find do |key, _|
- filename_regex, changes_regex = Array(key)
-
- found = filename_regex.match?(file)
- found &&= changed_lines(file).any? { |changed_line| changes_regex.match?(changed_line) } if changes_regex
-
- found
- end
-
- Array(categories || :unknown)
- end
-
- # Returns the GFM for a category label, making its best guess if it's not
- # a category we know about.
- #
- # @return[String]
- def label_for_category(category)
- CATEGORY_LABELS.fetch(category, "~#{category}")
- end
-
- CATEGORY_LABELS = {
- docs: "~documentation", # Docs are reviewed along DevOps stages, so don't need roulette for now.
- none: "",
- qa: "~QA",
- test: "~test ~Quality for `spec/features/*`",
- engineering_productivity: '~"Engineering Productivity" for CI, Danger',
- ci_template: '~"ci::templates"'
- }.freeze
- # First-match win, so be sure to put more specific regex at the top...
- CATEGORIES = {
- [%r{usage_data\.rb}, %r{^(\+|-).*\s+(count|distinct_count|estimate_batch_distinct_count)\(.*\)(.*)$}] => [:database, :backend],
-
- %r{\Adoc/.*(\.(md|png|gif|jpg))\z} => :docs,
- %r{\A(CONTRIBUTING|LICENSE|MAINTENANCE|PHILOSOPHY|PROCESS|README)(\.md)?\z} => :docs,
-
- %r{\A(ee/)?app/(assets|views)/} => :frontend,
- %r{\A(ee/)?public/} => :frontend,
- %r{\A(ee/)?spec/(javascripts|frontend)/} => :frontend,
- %r{\A(ee/)?vendor/assets/} => :frontend,
- %r{\A(ee/)?scripts/frontend/} => :frontend,
- %r{(\A|/)(
- \.babelrc |
- \.eslintignore |
- \.eslintrc(\.yml)? |
- \.nvmrc |
- \.prettierignore |
- \.prettierrc |
- \.scss-lint.yml |
- \.stylelintrc |
- \.haml-lint.yml |
- \.haml-lint_todo.yml |
- babel\.config\.js |
- jest\.config\.js |
- package\.json |
- yarn\.lock |
- config/.+\.js
- )\z}x => :frontend,
-
- %r{(\A|/)(
- \.gitlab/ci/frontend\.gitlab-ci\.yml
- )\z}x => %i[frontend engineering_productivity],
-
- %r{\A(ee/)?db/(?!fixtures)[^/]+} => :database,
- %r{\A(ee/)?lib/gitlab/(database|background_migration|sql|github_import)(/|\.rb)} => :database,
- %r{\A(app/models/project_authorization|app/services/users/refresh_authorized_projects_service)(/|\.rb)} => :database,
- %r{\A(ee/)?app/finders/} => :database,
- %r{\Arubocop/cop/migration(/|\.rb)} => :database,
-
- %r{\A(\.gitlab-ci\.yml\z|\.gitlab\/ci)} => :engineering_productivity,
- %r{\A\.codeclimate\.yml\z} => :engineering_productivity,
- %r{\Alefthook.yml\z} => :engineering_productivity,
- %r{\A\.editorconfig\z} => :engineering_productivity,
- %r{Dangerfile\z} => :engineering_productivity,
- %r{\A(ee/)?(danger/|lib/gitlab/danger/)} => :engineering_productivity,
- %r{\A(ee/)?scripts/} => :engineering_productivity,
- %r{\Atooling/} => :engineering_productivity,
- %r{(CODEOWNERS)} => :engineering_productivity,
- %r{(tests.yml)} => :engineering_productivity,
-
- %r{\Alib/gitlab/ci/templates} => :ci_template,
-
- %r{\A(ee/)?spec/features/} => :test,
- %r{\A(ee/)?spec/support/shared_examples/features/} => :test,
- %r{\A(ee/)?spec/support/shared_contexts/features/} => :test,
- %r{\A(ee/)?spec/support/helpers/features/} => :test,
-
- %r{\A(ee/)?app/(?!assets|views)[^/]+} => :backend,
- %r{\A(ee/)?(bin|config|generator_templates|lib|rubocop)/} => :backend,
- %r{\A(ee/)?spec/} => :backend,
- %r{\A(ee/)?vendor/} => :backend,
- %r{\A(Gemfile|Gemfile.lock|Rakefile)\z} => :backend,
- %r{\A[A-Z_]+_VERSION\z} => :backend,
- %r{\A\.rubocop((_manual)?_todo)?\.yml\z} => :backend,
- %r{\Afile_hooks/} => :backend,
-
- %r{\A(ee/)?qa/} => :qa,
-
- # Files that don't fit into any category are marked with :none
- %r{\A(ee/)?changelogs/} => :none,
- %r{\Alocale/gitlab\.pot\z} => :none,
- %r{\Adata/whats_new/} => :none,
-
- # GraphQL auto generated doc files and schema
- %r{\Adoc/api/graphql/reference/} => :backend,
-
- # Fallbacks in case the above patterns miss anything
- %r{\.rb\z} => :backend,
- %r{(
- \.(md|txt)\z |
- \.markdownlint\.json
- )}x => :none, # To reinstate roulette for documentation, set to `:docs`.
- %r{\.js\z} => :frontend
- }.freeze
-
- def new_teammates(usernames)
- usernames.map { |u| Gitlab::Danger::Teammate.new('username' => u) }
- end
-
- def draft_mr?
- return false unless gitlab_helper
-
- TitleLinting.has_draft_flag?(gitlab_helper.mr_json['title'])
- end
-
- def security_mr?
- return false unless gitlab_helper
-
- gitlab_helper.mr_json['web_url'].include?('/gitlab-org/security/')
- end
-
- def cherry_pick_mr?
- return false unless gitlab_helper
-
- /cherry[\s-]*pick/i.match?(gitlab_helper.mr_json['title'])
- end
-
- def stable_branch?
- return false unless gitlab_helper
-
- /\A\d+-\d+-stable-ee/i.match?(gitlab_helper.mr_json['target_branch'])
- end
-
- def mr_has_labels?(*labels)
- return false unless gitlab_helper
-
- labels = labels.flatten.uniq
- (labels & gitlab_helper.mr_labels) == labels
- end
-
- def labels_list(labels, sep: ', ')
- labels.map { |label| %Q{~"#{label}"} }.join(sep)
- end
-
- def prepare_labels_for_mr(labels)
- return '' unless labels.any?
-
- "/label #{labels_list(labels, sep: ' ')}"
- end
-
- def changed_files(regex)
- all_changed_files.grep(regex)
- end
-
- def has_database_scoped_labels?(current_mr_labels)
- current_mr_labels.any? { |label| label.start_with?('database::') }
- end
-
- def has_ci_changes?
- changed_files(%r{\A(\.gitlab-ci\.yml|\.gitlab/ci/)}).any?
- end
- end
- end
-end
diff --git a/lib/gitlab/danger/merge_request_linter.rb b/lib/gitlab/danger/merge_request_linter.rb
deleted file mode 100644
index ed354bfc68d..00000000000
--- a/lib/gitlab/danger/merge_request_linter.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-base_linter_path = File.expand_path('base_linter', __dir__)
-
-if defined?(Rails)
- require_dependency(base_linter_path)
-else
- require_relative(base_linter_path)
-end
-
-module Gitlab
- module Danger
- class MergeRequestLinter < BaseLinter
- alias_method :lint, :lint_subject
-
- def self.subject_description
- 'merge request title'
- end
-
- def self.mr_run_options_regex
- [
- 'RUN AS-IF-FOSS',
- 'UPDATE CACHE',
- 'RUN ALL RSPEC',
- 'SKIP RSPEC FAIL-FAST'
- ].join('|')
- end
-
- private
-
- def subject
- super.gsub(/\[?(#{self.class.mr_run_options_regex})\]?/, '').strip
- end
- end
- end
-end
diff --git a/lib/gitlab/danger/request_helper.rb b/lib/gitlab/danger/request_helper.rb
deleted file mode 100644
index 06da4ed9ad3..00000000000
--- a/lib/gitlab/danger/request_helper.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-require 'net/http'
-require 'json'
-
-module Gitlab
- module Danger
- module RequestHelper
- HTTPError = Class.new(RuntimeError)
-
- # @param [String] url
- def self.http_get_json(url)
- rsp = Net::HTTP.get_response(URI.parse(url))
-
- unless rsp.is_a?(Net::HTTPOK)
- raise HTTPError, "Failed to read #{url}: #{rsp.code} #{rsp.message}"
- end
-
- JSON.parse(rsp.body)
- end
- end
- end
-end
diff --git a/lib/gitlab/danger/roulette.rb b/lib/gitlab/danger/roulette.rb
deleted file mode 100644
index 21feda2cf20..00000000000
--- a/lib/gitlab/danger/roulette.rb
+++ /dev/null
@@ -1,169 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'teammate'
-require_relative 'request_helper' unless defined?(Gitlab::Danger::RequestHelper)
-require_relative 'weightage/reviewers'
-require_relative 'weightage/maintainers'
-
-module Gitlab
- module Danger
- module Roulette
- ROULETTE_DATA_URL = 'https://gitlab-org.gitlab.io/gitlab-roulette/roulette.json'
- HOURS_WHEN_PERSON_CAN_BE_PICKED = (6..14).freeze
-
- INCLUDE_TIMEZONE_FOR_CATEGORY = {
- database: false
- }.freeze
-
- Spin = Struct.new(:category, :reviewer, :maintainer, :optional_role, :timezone_experiment)
-
- def team_mr_author
- team.find { |person| person.username == mr_author_username }
- end
-
- # Assigns GitLab team members to be reviewer and maintainer
- # for each change category that a Merge Request contains.
- #
- # @return [Array<Spin>]
- def spin(project, categories, timezone_experiment: false)
- spins = categories.sort.map do |category|
- including_timezone = INCLUDE_TIMEZONE_FOR_CATEGORY.fetch(category, timezone_experiment)
-
- spin_for_category(project, category, timezone_experiment: including_timezone)
- end
-
- backend_spin = spins.find { |spin| spin.category == :backend }
-
- spins.each do |spin|
- including_timezone = INCLUDE_TIMEZONE_FOR_CATEGORY.fetch(spin.category, timezone_experiment)
- case spin.category
- when :qa
- # MR includes QA changes, but also other changes, and author isn't an SET
- if categories.size > 1 && !team_mr_author&.reviewer?(project, spin.category, [])
- spin.optional_role = :maintainer
- end
- when :test
- spin.optional_role = :maintainer
-
- if spin.reviewer.nil?
- # Fetch an already picked backend reviewer, or pick one otherwise
- spin.reviewer = backend_spin&.reviewer || spin_for_category(project, :backend, timezone_experiment: including_timezone).reviewer
- end
- when :engineering_productivity
- if spin.maintainer.nil?
- # Fetch an already picked backend maintainer, or pick one otherwise
- spin.maintainer = backend_spin&.maintainer || spin_for_category(project, :backend, timezone_experiment: including_timezone).maintainer
- end
- when :ci_template
- if spin.maintainer.nil?
- # Fetch an already picked backend maintainer, or pick one otherwise
- spin.maintainer = backend_spin&.maintainer || spin_for_category(project, :backend, timezone_experiment: including_timezone).maintainer
- end
- end
- end
-
- spins
- end
-
- # Looks up the current list of GitLab team members and parses it into a
- # useful form
- #
- # @return [Array<Teammate>]
- def team
- @team ||=
- begin
- data = Gitlab::Danger::RequestHelper.http_get_json(ROULETTE_DATA_URL)
- data.map { |hash| ::Gitlab::Danger::Teammate.new(hash) }
- rescue JSON::ParserError
- raise "Failed to parse JSON response from #{ROULETTE_DATA_URL}"
- end
- end
-
- # Like +team+, but only returns teammates in the current project, based on
- # project_name.
- #
- # @return [Array<Teammate>]
- def project_team(project_name)
- team.select { |member| member.in_project?(project_name) }
- rescue => err
- warn("Reviewer roulette failed to load team data: #{err.message}")
- []
- end
-
- # Known issue: If someone is rejected due to OOO, and then becomes not OOO, the
- # selection will change on next spin
- # @param [Array<Teammate>] people
- def spin_for_person(people, random:, timezone_experiment: false)
- shuffled_people = people.shuffle(random: random)
-
- if timezone_experiment
- shuffled_people.find(&method(:valid_person_with_timezone?))
- else
- shuffled_people.find(&method(:valid_person?))
- end
- end
-
- private
-
- # @param [Teammate] person
- # @return [Boolean]
- def valid_person?(person)
- !mr_author?(person) && person.available
- end
-
- # @param [Teammate] person
- # @return [Boolean]
- def valid_person_with_timezone?(person)
- valid_person?(person) && HOURS_WHEN_PERSON_CAN_BE_PICKED.cover?(person.local_hour)
- end
-
- # @param [Teammate] person
- # @return [Boolean]
- def mr_author?(person)
- person.username == mr_author_username
- end
-
- def mr_author_username
- helper.gitlab_helper&.mr_author || `whoami`
- end
-
- def mr_source_branch
- return `git rev-parse --abbrev-ref HEAD` unless helper.gitlab_helper&.mr_json
-
- helper.gitlab_helper.mr_json['source_branch']
- end
-
- def mr_labels
- helper.gitlab_helper&.mr_labels || []
- end
-
- def new_random(seed)
- Random.new(Digest::MD5.hexdigest(seed).to_i(16))
- end
-
- def spin_role_for_category(team, role, project, category)
- team.select do |member|
- member.public_send("#{role}?", project, category, mr_labels) # rubocop:disable GitlabSecurity/PublicSend
- end
- end
-
- def spin_for_category(project, category, timezone_experiment: false)
- team = project_team(project)
- reviewers, traintainers, maintainers =
- %i[reviewer traintainer maintainer].map do |role|
- spin_role_for_category(team, role, project, category)
- end
-
- random = new_random(mr_source_branch)
-
- weighted_reviewers = Weightage::Reviewers.new(reviewers, traintainers).execute
- weighted_maintainers = Weightage::Maintainers.new(maintainers).execute
-
- reviewer = spin_for_person(weighted_reviewers, random: random, timezone_experiment: timezone_experiment)
- maintainer = spin_for_person(weighted_maintainers, random: random, timezone_experiment: timezone_experiment)
-
- Spin.new(category, reviewer, maintainer, false, timezone_experiment)
- end
- end
- end
-end
diff --git a/lib/gitlab/danger/sidekiq_queues.rb b/lib/gitlab/danger/sidekiq_queues.rb
deleted file mode 100644
index 726b6134abf..00000000000
--- a/lib/gitlab/danger/sidekiq_queues.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Danger
- module SidekiqQueues
- def changed_queue_files
- @changed_queue_files ||= git.modified_files.grep(%r{\A(ee/)?app/workers/all_queues\.yml})
- end
-
- def added_queue_names
- @added_queue_names ||= new_queues.keys - old_queues.keys
- end
-
- def changed_queue_names
- @changed_queue_names ||=
- (new_queues.values_at(*old_queues.keys) - old_queues.values)
- .compact.map { |queue| queue[:name] }
- end
-
- private
-
- def old_queues
- @old_queues ||= queues_for(gitlab.base_commit)
- end
-
- def new_queues
- @new_queues ||= queues_for(gitlab.head_commit)
- end
-
- def queues_for(branch)
- changed_queue_files
- .flat_map { |file| YAML.safe_load(`git show #{branch}:#{file}`, permitted_classes: [Symbol]) }
- .to_h { |queue| [queue[:name], queue] }
- end
- end
- end
-end
diff --git a/lib/gitlab/danger/teammate.rb b/lib/gitlab/danger/teammate.rb
deleted file mode 100644
index 911b84d93ec..00000000000
--- a/lib/gitlab/danger/teammate.rb
+++ /dev/null
@@ -1,117 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Danger
- class Teammate
- attr_reader :options, :username, :name, :role, :projects, :available, :hungry, :reduced_capacity, :tz_offset_hours
-
- # The options data are produced by https://gitlab.com/gitlab-org/gitlab-roulette/-/blob/master/lib/team_member.rb
- def initialize(options = {})
- @options = options
- @username = options['username']
- @name = options['name']
- @markdown_name = options['markdown_name']
- @role = options['role']
- @projects = options['projects']
- @available = options['available']
- @hungry = options['hungry']
- @reduced_capacity = options['reduced_capacity']
- @tz_offset_hours = options['tz_offset_hours']
- end
-
- def to_h
- options
- end
-
- def ==(other)
- return false unless other.respond_to?(:username)
-
- other.username == username
- end
-
- def in_project?(name)
- projects&.has_key?(name)
- end
-
- def reviewer?(project, category, labels)
- has_capability?(project, category, :reviewer, labels)
- end
-
- def traintainer?(project, category, labels)
- has_capability?(project, category, :trainee_maintainer, labels)
- end
-
- def maintainer?(project, category, labels)
- has_capability?(project, category, :maintainer, labels)
- end
-
- def markdown_name(author: nil)
- "#{@markdown_name} (#{utc_offset_text(author)})"
- end
-
- def local_hour
- (Time.now.utc + tz_offset_hours * 3600).hour
- end
-
- protected
-
- def floored_offset_hours
- floored_offset = tz_offset_hours.floor(0)
-
- floored_offset == tz_offset_hours ? floored_offset : tz_offset_hours
- end
-
- private
-
- def utc_offset_text(author = nil)
- offset_text =
- if floored_offset_hours >= 0
- "UTC+#{floored_offset_hours}"
- else
- "UTC#{floored_offset_hours}"
- end
-
- return offset_text unless author
-
- "#{offset_text}, #{offset_diff_compared_to_author(author)}"
- end
-
- def offset_diff_compared_to_author(author)
- diff = floored_offset_hours - author.floored_offset_hours
- return "same timezone as `@#{author.username}`" if diff == 0
-
- ahead_or_behind = diff < 0 ? 'behind' : 'ahead of'
- pluralized_hours = pluralize(diff.abs, 'hour', 'hours')
-
- "#{pluralized_hours} #{ahead_or_behind} `@#{author.username}`"
- end
-
- def has_capability?(project, category, kind, labels)
- case category
- when :test
- area = role[/Software Engineer in Test(?:.*?, (\w+))/, 1]
-
- area && labels.any?("devops::#{area.downcase}") if kind == :reviewer
- when :engineering_productivity
- return false unless role[/Engineering Productivity/]
- return true if kind == :reviewer
- return true if capabilities(project).include?("#{kind} engineering_productivity")
-
- capabilities(project).include?("#{kind} backend")
- else
- capabilities(project).include?("#{kind} #{category}")
- end
- end
-
- def capabilities(project)
- Array(projects.fetch(project, []))
- end
-
- def pluralize(count, singular, plural)
- word = count == 1 || count.to_s =~ /^1(\.0+)?$/ ? singular : plural
-
- "#{count || 0} #{word}"
- end
- end
- end
-end
diff --git a/lib/gitlab/danger/title_linting.rb b/lib/gitlab/danger/title_linting.rb
deleted file mode 100644
index db1ccaaf9a9..00000000000
--- a/lib/gitlab/danger/title_linting.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Danger
- module TitleLinting
- DRAFT_REGEX = /\A*#{Regexp.union(/(?i)(\[WIP\]\s*|WIP:\s*|WIP$)/, /(?i)(\[draft\]|\(draft\)|draft:|draft\s\-\s|draft$)/)}+\s*/i.freeze
-
- module_function
-
- def sanitize_mr_title(title)
- remove_draft_flag(title).gsub(/`/, '\\\`')
- end
-
- def remove_draft_flag(title)
- title.gsub(DRAFT_REGEX, '')
- end
-
- def has_draft_flag?(title)
- DRAFT_REGEX.match?(title)
- end
- end
- end
-end
diff --git a/lib/gitlab/danger/weightage.rb b/lib/gitlab/danger/weightage.rb
deleted file mode 100644
index 67fade27573..00000000000
--- a/lib/gitlab/danger/weightage.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Danger
- module Weightage
- CAPACITY_MULTIPLIER = 2 # change this number to change what it means to be a reduced capacity reviewer 1/this number
- BASE_REVIEWER_WEIGHT = 1
- end
- end
-end
diff --git a/lib/gitlab/danger/weightage/maintainers.rb b/lib/gitlab/danger/weightage/maintainers.rb
deleted file mode 100644
index cc0eb370e7a..00000000000
--- a/lib/gitlab/danger/weightage/maintainers.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# frozen_string_literal: true
-
-require_relative '../weightage'
-
-module Gitlab
- module Danger
- module Weightage
- class Maintainers
- def initialize(maintainers)
- @maintainers = maintainers
- end
-
- def execute
- maintainers.each_with_object([]) do |maintainer, weighted_maintainers|
- add_weighted_reviewer(weighted_maintainers, maintainer, BASE_REVIEWER_WEIGHT)
- end
- end
-
- private
-
- attr_reader :maintainers
-
- def add_weighted_reviewer(reviewers, reviewer, weight)
- if reviewer.reduced_capacity
- reviewers.fill(reviewer, reviewers.size, weight)
- else
- reviewers.fill(reviewer, reviewers.size, weight * CAPACITY_MULTIPLIER)
- end
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/danger/weightage/reviewers.rb b/lib/gitlab/danger/weightage/reviewers.rb
deleted file mode 100644
index c8019be716e..00000000000
--- a/lib/gitlab/danger/weightage/reviewers.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-# frozen_string_literal: true
-
-require_relative '../weightage'
-
-module Gitlab
- module Danger
- module Weightage
- # Weights after (current multiplier of 2)
- #
- # +------------------------------+--------------------------------+
- # | reviewer type | weight(times in reviewer pool) |
- # +------------------------------+--------------------------------+
- # | reduced capacity reviewer | 1 |
- # | reviewer | 2 |
- # | hungry reviewer | 4 |
- # | reduced capacity traintainer | 3 |
- # | traintainer | 6 |
- # | hungry traintainer | 8 |
- # +------------------------------+--------------------------------+
- #
- class Reviewers
- DEFAULT_REVIEWER_WEIGHT = CAPACITY_MULTIPLIER * BASE_REVIEWER_WEIGHT
- TRAINTAINER_WEIGHT = 3
-
- def initialize(reviewers, traintainers)
- @reviewers = reviewers
- @traintainers = traintainers
- end
-
- def execute
- # TODO: take CODEOWNERS into account?
- # https://gitlab.com/gitlab-org/gitlab/issues/26723
-
- weighted_reviewers + weighted_traintainers
- end
-
- private
-
- attr_reader :reviewers, :traintainers
-
- def weighted_reviewers
- reviewers.each_with_object([]) do |reviewer, total_reviewers|
- add_weighted_reviewer(total_reviewers, reviewer, BASE_REVIEWER_WEIGHT)
- end
- end
-
- def weighted_traintainers
- traintainers.each_with_object([]) do |reviewer, total_traintainers|
- add_weighted_reviewer(total_traintainers, reviewer, TRAINTAINER_WEIGHT)
- end
- end
-
- def add_weighted_reviewer(reviewers, reviewer, weight)
- if reviewer.reduced_capacity
- reviewers.fill(reviewer, reviewers.size, weight)
- elsif reviewer.hungry
- reviewers.fill(reviewer, reviewers.size, weight * CAPACITY_MULTIPLIER + DEFAULT_REVIEWER_WEIGHT)
- else
- reviewers.fill(reviewer, reviewers.size, weight * CAPACITY_MULTIPLIER)
- end
- end
- end
- end
- end
-end