gitlab-dangerfiles 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6298bb4b8fed9c66cd1bc9b811e9ddb93d6e8474dd8797da837780ec5bcbe1a8
4
- data.tar.gz: 2be36370906090104ebba958d4910d5f105cf3b8d0619849c78dbb5b625dcd9a
3
+ metadata.gz: 679d12adff8fd532552d644bba3f1ae62395077310ffcc7aa62181b477f5453d
4
+ data.tar.gz: f6460b3c349cf27d1a65246f41744cae15c95c0c949811aa1b972abe654b3e90
5
5
  SHA512:
6
- metadata.gz: 9c09bdec93ab9d0b1a73565e62a46e400204907f758cb924c0d27d27eda7aae0871e45a02d7ac1f6846d2aa0f6a8c41d341f57147d5e04d94ac4351a2b6b64d1
7
- data.tar.gz: 67710acb881d9f5229944ccb3dc40bb7d9c4ae0d66743190cfb3fac7e560f22f3ca8a7ca7b7b60119e6f595b4a4e00d3235df0728a79370d60723985cb4d0b93
6
+ metadata.gz: 8ebcb0cd08d49021346a0b74adb50b5ff10a8fe64227e86f960a72e6cc88260f0249927088bae3e949ddbf9a295c65d2d4c005d8d4b0ee157a907f700b72ce46
7
+ data.tar.gz: e747d6b68ca54ee597e008332a5bfc7d4fa01a437e44af42fc68d5ab28aaf674b2e76995ab792559016fa381c0cd1e08b58071bb5c3d4b66f37e8c7891bbaa77
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "net/http"
4
4
  require "json"
5
+ require "danger"
5
6
  require_relative "../gitlab/dangerfiles/teammate"
6
7
 
7
8
  module Danger
@@ -15,77 +16,6 @@ module Danger
15
16
  test: "~test ~Quality for `spec/features/*`",
16
17
  engineering_productivity: '~"Engineering Productivity" for CI, Danger',
17
18
  }.freeze
18
- # First-match win, so be sure to put more specific regex at the top...
19
- CATEGORIES = {
20
- %r{\Adoc/} => :docs,
21
- %r{\A(CONTRIBUTING|LICENSE|MAINTENANCE|PHILOSOPHY|PROCESS|README)(\.md)?\z} => :docs,
22
-
23
- %r{\A(ee/)?app/(assets|views)/} => :frontend,
24
- %r{\A(ee/)?public/} => :frontend,
25
- %r{\A(ee/)?spec/(javascripts|frontend)/} => :frontend,
26
- %r{\A(ee/)?vendor/assets/} => :frontend,
27
- %r{\A(ee/)?scripts/frontend/} => :frontend,
28
- %r{(\A|/)(
29
- \.babelrc |
30
- \.eslintignore |
31
- \.eslintrc(\.yml)? |
32
- \.nvmrc |
33
- \.prettierignore |
34
- \.prettierrc |
35
- \.scss-lint.yml |
36
- \.stylelintrc |
37
- \.haml-lint.yml |
38
- \.haml-lint_todo.yml |
39
- babel\.config\.js |
40
- jest\.config\.js |
41
- package\.json |
42
- yarn\.lock |
43
- config/.+\.js
44
- )\z}x => :frontend,
45
-
46
- %r{(\A|/)(
47
- \.gitlab/ci/frontend\.gitlab-ci\.yml
48
- )\z}x => %i[frontend engineering_productivity],
49
-
50
- %r{\A(ee/)?db/(?!fixtures)[^/]+} => :database,
51
- %r{\A(ee/)?lib/gitlab/(database|background_migration|sql|github_import)(/|\.rb)} => :database,
52
- %r{\A(app/models/project_authorization|app/services/users/refresh_authorized_projects_service)(/|\.rb)} => :database,
53
- %r{\A(ee/)?app/finders/} => :database,
54
- %r{\Arubocop/cop/migration(/|\.rb)} => :database,
55
-
56
- %r{\A(\.gitlab-ci\.yml\z|\.gitlab\/ci)} => :engineering_productivity,
57
- %r{\A\.codeclimate\.yml\z} => :engineering_productivity,
58
- %r{\A\.overcommit\.yml\.example\z} => :engineering_productivity,
59
- %r{\A\.editorconfig\z} => :engineering_productivity,
60
- %r{Dangerfile\z} => :engineering_productivity,
61
- %r{\A(ee/)?(danger/|lib/gitlab/danger/)} => :engineering_productivity,
62
- %r{\A(ee/)?scripts/} => :engineering_productivity,
63
- %r{\Atooling/} => :engineering_productivity,
64
-
65
- %r{\A(ee/)?app/(?!assets|views)[^/]+} => :backend,
66
- %r{\A(ee/)?(bin|config|generator_templates|lib|rubocop)/} => :backend,
67
- %r{\A(ee/)?spec/features/} => :test,
68
- %r{\A(ee/)?spec/} => :backend,
69
- %r{\A(ee/)?vendor/} => :backend,
70
- %r{\A(Gemfile|Gemfile.lock|Rakefile)\z} => :backend,
71
- %r{\A[A-Z_]+_VERSION\z} => :backend,
72
- %r{\A\.rubocop(_todo)?\.yml\z} => :backend,
73
- %r{\Afile_hooks/} => :backend,
74
-
75
- %r{\A(ee/)?qa/} => :qa,
76
-
77
- # Files that don't fit into any category are marked with :none
78
- %r{\A(ee/)?changelogs/} => :none,
79
- %r{\Alocale/gitlab\.pot\z} => :none,
80
-
81
- # Fallbacks in case the above patterns miss anything
82
- %r{\.rb\z} => :backend,
83
- %r{(
84
- \.(md|txt)\z |
85
- \.markdownlint\.json
86
- )}x => :none, # To reinstate roulette for documentation, set to `:docs`.
87
- %r{\.js\z} => :frontend,
88
- }.freeze
89
19
 
90
20
  HTTPError = Class.new(StandardError)
91
21
 
@@ -98,10 +28,6 @@ module Danger
98
28
  nil
99
29
  end
100
30
 
101
- def rule_names
102
- ci? ? Gitlab::Dangerfiles::LOCAL_RULES | Gitlab::Dangerfiles::CI_ONLY_RULES : Gitlab::Dangerfiles::LOCAL_RULES
103
- end
104
-
105
31
  def html_link(str)
106
32
  ci? ? gitlab_helper.html_link(str) : str
107
33
  end
@@ -148,23 +74,25 @@ module Danger
148
74
  .sort
149
75
  end
150
76
 
151
- def all_ee_changes
152
- all_changed_files.grep(%r{\Aee/})
153
- end
77
+ # Returns a string containing changed lines as git diff
78
+ #
79
+ # Considering changing a line in lib/gitlab/usage_data.rb it will return:
80
+ #
81
+ # [ "--- a/lib/gitlab/usage_data.rb",
82
+ # "+++ b/lib/gitlab/usage_data.rb",
83
+ # "+ # Test change",
84
+ # "- # Old change" ]
85
+ def changed_lines(changed_file)
86
+ diff = git.diff_for_file(changed_file)
87
+ return [] unless diff
154
88
 
155
- def ee?
156
- # Support former project name for `dev` and support local Danger run
157
- %w[gitlab gitlab-ee].include?(ENV["CI_PROJECT_NAME"]) || Dir.exist?("../../ee")
89
+ diff.patch.split("\n").select { |line| %r{^[+-]}.match?(line) }
158
90
  end
159
91
 
160
92
  def release_automation?
161
93
  gitlab_helper&.mr_author == RELEASE_TOOLS_BOT
162
94
  end
163
95
 
164
- def project_name
165
- ee? ? "gitlab" : "gitlab-foss"
166
- end
167
-
168
96
  def markdown_list(items)
169
97
  list = items.map { |item| "* `#{item}`" }.join("\n")
170
98
 
@@ -176,16 +104,23 @@ module Danger
176
104
  end
177
105
 
178
106
  # @return [Hash<String,Array<String>>]
179
- def changes_by_category
107
+ def changes_by_category(categories)
180
108
  all_changed_files.each_with_object(Hash.new { |h, k| h[k] = [] }) do |file, hash|
181
- categories_for_file(file).each { |category| hash[category] << file }
109
+ categories_for_file(file, categories).each { |category| hash[category] << file }
182
110
  end
183
111
  end
184
112
 
185
113
  # Determines the categories a file is in, e.g., `[:frontend]`, `[:backend]`, or `%i[frontend engineering_productivity]`.
186
114
  # @return Array<Symbol>
187
- def categories_for_file(file)
188
- _, categories = CATEGORIES.find { |regexp, _| regexp.match?(file) }
115
+ def categories_for_file(file, categories)
116
+ _, categories = categories.find do |key, _|
117
+ filename_regex, changes_regex = Array(key)
118
+
119
+ found = filename_regex.match?(file)
120
+ found &&= changed_lines(file).any? { |changed_line| changes_regex.match?(changed_line) } if changes_regex
121
+
122
+ found
123
+ end
189
124
 
190
125
  Array(categories || :unknown)
191
126
  end
@@ -251,6 +186,10 @@ module Danger
251
186
  "/label #{labels_list(labels, sep: " ")}"
252
187
  end
253
188
 
189
+ def changed_files(regex)
190
+ all_changed_files.grep(regex)
191
+ end
192
+
254
193
  private
255
194
 
256
195
  def has_database_scoped_labels?(current_mr_labels)
@@ -9,42 +9,53 @@ module Danger
9
9
  ROULETTE_DATA_URL = "https://gitlab-org.gitlab.io/gitlab-roulette/roulette.json"
10
10
  HOURS_WHEN_PERSON_CAN_BE_PICKED = (6..14).freeze
11
11
 
12
- Spin = Struct.new(:category, :reviewer, :maintainer, :optional_role)
12
+ INCLUDE_TIMEZONE_FOR_CATEGORY = {
13
+ database: false,
14
+ }.freeze
15
+
16
+ Spin = Struct.new(:category, :reviewer, :maintainer, :optional_role, :timezone_experiment)
17
+
18
+ def team_mr_author
19
+ team.find { |person| person.username == mr_author_username }
20
+ end
13
21
 
14
22
  # Assigns GitLab team members to be reviewer and maintainer
15
23
  # for each change category that a Merge Request contains.
16
24
  #
17
25
  # @return [Array<Spin>]
18
- def spin(project, categories, branch_name, timezone_experiment: false)
19
- team = begin
20
- project_team(project)
21
- rescue => err
22
- warn("Reviewer roulette failed to load team data: #{err.message}")
23
- []
24
- end
26
+ def spin(project, categories, timezone_experiment: false)
27
+ spins = categories.map do |category|
28
+ including_timezone = INCLUDE_TIMEZONE_FOR_CATEGORY.fetch(category, timezone_experiment)
25
29
 
26
- canonical_branch_name = canonical_branch_name(branch_name)
27
-
28
- spin_per_category = categories.each_with_object({}) do |category, memo|
29
- memo[category] = spin_for_category(team, project, category, canonical_branch_name, timezone_experiment: timezone_experiment)
30
+ spin_for_category(project, category, timezone_experiment: including_timezone)
30
31
  end
31
32
 
32
- spin_per_category.map do |category, spin|
33
- case category
33
+ backend_spin = spins.find { |spin| spin.category == :backend }
34
+
35
+ spins.each do |spin|
36
+ including_timezone = INCLUDE_TIMEZONE_FOR_CATEGORY.fetch(spin.category, timezone_experiment)
37
+ case spin.category
38
+ when :qa
39
+ # MR includes QA changes, but also other changes, and author isn't an SET
40
+ if categories.size > 1 && !team_mr_author&.reviewer?(project, spin.category, [])
41
+ spin.optional_role = :maintainer
42
+ end
34
43
  when :test
44
+ spin.optional_role = :maintainer
45
+
35
46
  if spin.reviewer.nil?
36
47
  # Fetch an already picked backend reviewer, or pick one otherwise
37
- spin.reviewer = spin_per_category[:backend]&.reviewer || spin_for_category(team, project, :backend, canonical_branch_name).reviewer
48
+ spin.reviewer = backend_spin&.reviewer || spin_for_category(project, :backend, timezone_experiment: including_timezone).reviewer
38
49
  end
39
50
  when :engineering_productivity
40
51
  if spin.maintainer.nil?
41
52
  # Fetch an already picked backend maintainer, or pick one otherwise
42
- spin.maintainer = spin_per_category[:backend]&.maintainer || spin_for_category(team, project, :backend, canonical_branch_name).maintainer
53
+ spin.maintainer = backend_spin&.maintainer || spin_for_category(project, :backend, timezone_experiment: including_timezone).maintainer
43
54
  end
44
55
  end
45
-
46
- spin
47
56
  end
57
+
58
+ spins
48
59
  end
49
60
 
50
61
  # Looks up the current list of GitLab team members and parses it into a
@@ -66,14 +77,9 @@ module Danger
66
77
  # @return [Array<Teammate>]
67
78
  def project_team(project_name)
68
79
  team.select { |member| member.in_project?(project_name) }
69
- end
70
-
71
- def canonical_branch_name(branch_name)
72
- branch_name.gsub(/^[ce]e-|-[ce]e$/, "")
73
- end
74
-
75
- def new_random(seed)
76
- Random.new(Digest::MD5.hexdigest(seed).to_i(16))
80
+ rescue => err
81
+ warn("Reviewer roulette failed to load team data: #{err.message}")
82
+ []
77
83
  end
78
84
 
79
85
  # Known issue: If someone is rejected due to OOO, and then becomes not OOO, the
@@ -106,16 +112,35 @@ module Danger
106
112
  # @param [Teammate] person
107
113
  # @return [Boolean]
108
114
  def mr_author?(person)
109
- person.username == gitlab.mr_author
115
+ person.username == mr_author_username
116
+ end
117
+
118
+ def mr_author_username
119
+ helper.gitlab_helper&.mr_author || `whoami`
120
+ end
121
+
122
+ def mr_source_branch
123
+ return `git rev-parse --abbrev-ref HEAD` unless helper.gitlab_helper&.mr_json
124
+
125
+ helper.gitlab_helper.mr_json["source_branch"]
126
+ end
127
+
128
+ def mr_labels
129
+ helper.gitlab_helper&.mr_labels || []
130
+ end
131
+
132
+ def new_random(seed)
133
+ Random.new(Digest::MD5.hexdigest(seed).to_i(16))
110
134
  end
111
135
 
112
136
  def spin_role_for_category(team, role, project, category)
113
137
  team.select do |member|
114
- member.public_send("#{role}?", project, category, gitlab.mr_labels) # rubocop:disable GitlabSecurity/PublicSend
138
+ member.public_send("#{role}?", project, category, mr_labels) # rubocop:disable GitlabSecurity/PublicSend
115
139
  end
116
140
  end
117
141
 
118
- def spin_for_category(team, project, category, branch_name, timezone_experiment: false)
142
+ def spin_for_category(project, category, timezone_experiment: false)
143
+ team = project_team(project)
119
144
  reviewers, traintainers, maintainers =
120
145
  %i[reviewer traintainer maintainer].map do |role|
121
146
  spin_role_for_category(team, role, project, category)
@@ -125,11 +150,11 @@ module Danger
125
150
  # https://gitlab.com/gitlab-org/gitlab/issues/26723
126
151
 
127
152
  # Make traintainers have triple the chance to be picked as a reviewer
128
- random = new_random(branch_name)
153
+ random = new_random(mr_source_branch)
129
154
  reviewer = spin_for_person(reviewers + traintainers + traintainers, random: random, timezone_experiment: timezone_experiment)
130
155
  maintainer = spin_for_person(maintainers, random: random, timezone_experiment: timezone_experiment)
131
156
 
132
- Spin.new(category, reviewer, maintainer)
157
+ Spin.new(category, reviewer, maintainer, false, timezone_experiment)
133
158
  end
134
159
  end
135
160
  end
@@ -2,39 +2,5 @@ require "gitlab/dangerfiles/version"
2
2
 
3
3
  module Gitlab
4
4
  module Dangerfiles
5
- LOCAL_RULES ||= %w[
6
- changes_size
7
- commit_messages
8
- database
9
- documentation
10
- duplicate_yarn_dependencies
11
- eslint
12
- frozen_string
13
- karma
14
- prettier
15
- telemetry
16
- utility_css
17
- ].freeze
18
- CI_ONLY_RULES ||= %w[
19
- ce_ee_vue_templates
20
- changelog
21
- metadata
22
- roulette
23
- sidekiq_queues
24
- specs
25
- ].freeze
26
- MESSAGE_PREFIX = "==>".freeze
27
-
28
- def self.load_tasks
29
- require "gitlab/dangerfiles/tasks"
30
- end
31
-
32
- def self.local_warning_message
33
- "#{MESSAGE_PREFIX} Only the following Danger rules can be run locally: #{LOCAL_RULES.join(", ")}"
34
- end
35
-
36
- def self.success_message
37
- "#{MESSAGE_PREFIX} No Danger rule violations!"
38
- end
39
5
  end
40
6
  end
@@ -1,5 +1,5 @@
1
1
  module Gitlab
2
2
  module Dangerfiles
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitlab-dangerfiles
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rémy Coutable
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-08-27 00:00:00.000000000 Z
11
+ date: 2020-09-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: danger
@@ -130,36 +130,14 @@ files:
130
130
  - fixtures/emojis/aliases.json
131
131
  - fixtures/emojis/digests.json
132
132
  - gitlab-dangerfiles.gemspec
133
- - lib/danger/changelog.rb
134
133
  - lib/danger/helper.rb
135
134
  - lib/danger/roulette.rb
136
- - lib/danger/sidekiq_queues.rb
137
135
  - lib/gitlab-dangerfiles.rb
138
136
  - lib/gitlab/Dangerfile
139
137
  - lib/gitlab/dangerfiles.rb
140
- - lib/gitlab/dangerfiles/bundle_size/Dangerfile
141
- - lib/gitlab/dangerfiles/ce_ee_vue_templates/Dangerfile
142
- - lib/gitlab/dangerfiles/changelog/Dangerfile
143
- - lib/gitlab/dangerfiles/changes_size/Dangerfile
144
138
  - lib/gitlab/dangerfiles/commit_linter.rb
145
- - lib/gitlab/dangerfiles/commit_messages/Dangerfile
146
- - lib/gitlab/dangerfiles/database/Dangerfile
147
- - lib/gitlab/dangerfiles/documentation/Dangerfile
148
- - lib/gitlab/dangerfiles/duplicate_yarn_dependencies/Dangerfile
149
139
  - lib/gitlab/dangerfiles/emoji_checker.rb
150
- - lib/gitlab/dangerfiles/eslint/Dangerfile
151
- - lib/gitlab/dangerfiles/frozen_string/Dangerfile
152
- - lib/gitlab/dangerfiles/karma/Dangerfile
153
- - lib/gitlab/dangerfiles/metadata/Dangerfile
154
- - lib/gitlab/dangerfiles/popen.rb
155
- - lib/gitlab/dangerfiles/prettier/Dangerfile
156
- - lib/gitlab/dangerfiles/roulette/Dangerfile
157
- - lib/gitlab/dangerfiles/sidekiq_queues/Dangerfile
158
- - lib/gitlab/dangerfiles/specs/Dangerfile
159
- - lib/gitlab/dangerfiles/tasks.rb
160
140
  - lib/gitlab/dangerfiles/teammate.rb
161
- - lib/gitlab/dangerfiles/telemetry/Dangerfile
162
- - lib/gitlab/dangerfiles/utility_css/Dangerfile
163
141
  - lib/gitlab/dangerfiles/version.rb
164
142
  homepage: https://gitlab.com/gitlab-org/gitlab-dangerfiles
165
143
  licenses:
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Danger
4
- # Common helper functions for our danger scripts. See Danger::Helper
5
- # for more details
6
- class Changelog < Danger::Plugin
7
- NO_CHANGELOG_LABELS = [
8
- "backstage", # To be removed by https://gitlab.com/gitlab-org/gitlab/-/issues/222360.
9
- "tooling",
10
- "tooling::pipelines",
11
- "tooling::workflow",
12
- "ci-build",
13
- "meta",
14
- ].freeze
15
- NO_CHANGELOG_CATEGORIES = %i[docs none].freeze
16
-
17
- def needed?
18
- categories_need_changelog? && (gitlab.mr_labels & NO_CHANGELOG_LABELS).empty?
19
- end
20
-
21
- def found
22
- @found ||= git.added_files.find { |path| path =~ %r{\A(ee/)?(changelogs/unreleased)(-ee)?/} }
23
- end
24
-
25
- def sanitized_mr_title
26
- gitlab.mr_json["title"].gsub(/^WIP: */, "").gsub(/`/, '\\\`')
27
- end
28
-
29
- def ee_changelog?
30
- found.start_with?("ee/")
31
- end
32
-
33
- private
34
-
35
- def categories_need_changelog?
36
- (helper.changes_by_category.keys - NO_CHANGELOG_CATEGORIES).any?
37
- end
38
- end
39
- end