gitlab-dangerfiles 4.6.0 → 4.7.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/.gitignore +0 -1
- data/.gitlab-ci.yml +9 -16
- data/.rubocop.yml +3 -0
- data/Gemfile.lock +231 -0
- data/lib/danger/plugins/internal/helper.rb +2 -1
- data/lib/danger/plugins/roulette.rb +34 -267
- data/lib/danger/rules/commit_messages/Dangerfile +1 -1
- data/lib/gitlab/dangerfiles/approval.rb +22 -0
- data/lib/gitlab/dangerfiles/capability.rb +84 -0
- data/lib/gitlab/dangerfiles/spec_helper.rb +230 -0
- data/lib/gitlab/dangerfiles/spin.rb +15 -0
- data/lib/gitlab/dangerfiles/spinner.rb +190 -0
- data/lib/gitlab/dangerfiles/teammate.rb +88 -2
- data/lib/gitlab/dangerfiles/version.rb +1 -1
- metadata +7 -3
- data/lib/gitlab/dangerfiles/category.rb +0 -111
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3462c1433faf49899f1472e4411ce488ada1b6f59da331ede45114703fe2dfa
|
4
|
+
data.tar.gz: 37b7134a0cec36afed2dc1a4d78321cf9c9a80a405c5551fe52b2ce4834fd312
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9a7d44496574a8336bd42665f3c096e4b5c4bb7eeb322533b0b0a84d901d541104878fa13ab214a80fecea15c5c875ad4cb6f98d751b290b8f00108d2d837816
|
7
|
+
data.tar.gz: c032a92c6fb18de185e1552faabc9b648573ff95ee1d0b5e88fe3f57c7668e502d251cb51f2bd9e4b36d555c147cb9f538f6904223546b3e4fbbb5591d9826b5
|
data/.gitignore
CHANGED
data/.gitlab-ci.yml
CHANGED
@@ -2,9 +2,6 @@ stages:
|
|
2
2
|
- test
|
3
3
|
- deploy
|
4
4
|
|
5
|
-
variables:
|
6
|
-
DEFAULT_CI_IMAGE: "ruby:${RUBY_VERSION}"
|
7
|
-
|
8
5
|
workflow:
|
9
6
|
rules:
|
10
7
|
# For merge requests, create a pipeline.
|
@@ -15,12 +12,8 @@ workflow:
|
|
15
12
|
- if: '$CI_COMMIT_TAG'
|
16
13
|
|
17
14
|
default:
|
18
|
-
image: "${DEFAULT_CI_IMAGE}"
|
19
15
|
tags:
|
20
16
|
- gitlab-org
|
21
|
-
before_script:
|
22
|
-
- gem install bundler
|
23
|
-
- bundle install -j $(nproc) --path vendor
|
24
17
|
cache:
|
25
18
|
key:
|
26
19
|
files:
|
@@ -28,12 +21,14 @@ default:
|
|
28
21
|
- gitlab-dangerfiles.gemspec
|
29
22
|
paths:
|
30
23
|
- vendor/ruby
|
31
|
-
- Gemfile.lock
|
32
|
-
policy: pull
|
33
24
|
|
34
25
|
.default-test-job:
|
26
|
+
image: "ruby:${RUBY_VERSION}"
|
35
27
|
stage: test
|
36
28
|
needs: []
|
29
|
+
before_script:
|
30
|
+
- gem install bundler
|
31
|
+
- bundle install -j $(nproc) --path vendor
|
37
32
|
parallel:
|
38
33
|
matrix:
|
39
34
|
- RUBY_VERSION: ['3.0', '3.1', '3.2']
|
@@ -49,15 +44,13 @@ test:rubocop:
|
|
49
44
|
- bundle exec rubocop -P -E .
|
50
45
|
|
51
46
|
include:
|
52
|
-
-
|
53
|
-
-
|
54
|
-
-
|
55
|
-
- project: 'gitlab-org/quality/pipeline-common'
|
56
|
-
file:
|
57
|
-
- '/ci/danger-review.yml'
|
58
|
-
- component: "gitlab.com/gitlab-org/quality/pipeline-common/gem-release@7.6.1"
|
47
|
+
- component: gitlab.com/components/sast/sast@~latest
|
48
|
+
- component: gitlab.com/components/secret-detection/secret-detection@~latest
|
49
|
+
- component: gitlab.com/gitlab-org/components/gem-release/gem-release@~latest
|
59
50
|
inputs:
|
60
51
|
smoke_test_script: "ruby -r 'gitlab-dangerfiles' -e \"puts Gitlab::Dangerfiles::VERSION\""
|
52
|
+
- component: gitlab.com/gitlab-org/components/danger-review/danger-review@~latest
|
53
|
+
- template: Security/Dependency-Scanning.gitlab-ci.yml
|
61
54
|
|
62
55
|
# run security jobs on MRs
|
63
56
|
# see: https://gitlab.com/gitlab-org/gitlab/-/issues/218444#note_478761991
|
data/.rubocop.yml
CHANGED
@@ -28,6 +28,9 @@ RSpec/FilePath:
|
|
28
28
|
RSpec/SpecFilePathFormat:
|
29
29
|
Enabled: false
|
30
30
|
|
31
|
+
Style/ArrayIntersect:
|
32
|
+
Enabled: false # We're still supporting Ruby 3.1 and below
|
33
|
+
|
31
34
|
Style/HashSyntax:
|
32
35
|
EnforcedStyle: ruby19_no_mixed_keys
|
33
36
|
# Introduced in Ruby 3.1. Disable for now.
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,231 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
gitlab-dangerfiles (4.7.0)
|
5
|
+
danger (>= 9.3.0)
|
6
|
+
danger-gitlab (>= 8.0.0)
|
7
|
+
rake (~> 13.0)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
activesupport (7.0.4.2)
|
13
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
14
|
+
i18n (>= 1.6, < 2)
|
15
|
+
minitest (>= 5.1)
|
16
|
+
tzinfo (~> 2.0)
|
17
|
+
addressable (2.8.1)
|
18
|
+
public_suffix (>= 2.0.2, < 6.0)
|
19
|
+
ast (2.4.2)
|
20
|
+
binding_of_caller (1.0.0)
|
21
|
+
debug_inspector (>= 0.0.1)
|
22
|
+
claide (1.1.0)
|
23
|
+
claide-plugins (0.9.2)
|
24
|
+
cork
|
25
|
+
nap
|
26
|
+
open4 (~> 1.3)
|
27
|
+
climate_control (1.2.0)
|
28
|
+
coderay (1.1.3)
|
29
|
+
colored2 (3.1.2)
|
30
|
+
concurrent-ruby (1.2.0)
|
31
|
+
cork (0.3.0)
|
32
|
+
colored2 (~> 3.1)
|
33
|
+
crack (0.4.5)
|
34
|
+
rexml
|
35
|
+
danger (9.3.0)
|
36
|
+
claide (~> 1.0)
|
37
|
+
claide-plugins (>= 0.9.2)
|
38
|
+
colored2 (~> 3.1)
|
39
|
+
cork (~> 0.1)
|
40
|
+
faraday (>= 0.9.0, < 3.0)
|
41
|
+
faraday-http-cache (~> 2.0)
|
42
|
+
git (~> 1.13.0)
|
43
|
+
kramdown (~> 2.3)
|
44
|
+
kramdown-parser-gfm (~> 1.0)
|
45
|
+
no_proxy_fix
|
46
|
+
octokit (~> 5.0)
|
47
|
+
terminal-table (>= 1, < 4)
|
48
|
+
danger-gitlab (8.0.0)
|
49
|
+
danger
|
50
|
+
gitlab (~> 4.2, >= 4.2.0)
|
51
|
+
debug_inspector (1.1.0)
|
52
|
+
diff-lcs (1.5.0)
|
53
|
+
faraday (2.7.10)
|
54
|
+
faraday-net_http (>= 2.0, < 3.1)
|
55
|
+
ruby2_keywords (>= 0.0.4)
|
56
|
+
faraday-http-cache (2.5.0)
|
57
|
+
faraday (>= 0.8)
|
58
|
+
faraday-net_http (3.0.2)
|
59
|
+
ffi (1.15.5)
|
60
|
+
formatador (1.1.0)
|
61
|
+
git (1.13.2)
|
62
|
+
addressable (~> 2.8)
|
63
|
+
rchardet (~> 1.8)
|
64
|
+
gitlab (4.19.0)
|
65
|
+
httparty (~> 0.20)
|
66
|
+
terminal-table (>= 1.5.1)
|
67
|
+
gitlab-styles (10.0.0)
|
68
|
+
rubocop (~> 1.43.0)
|
69
|
+
rubocop-graphql (~> 0.18)
|
70
|
+
rubocop-performance (~> 1.15)
|
71
|
+
rubocop-rails (~> 2.17)
|
72
|
+
rubocop-rspec (~> 2.18)
|
73
|
+
guard (2.18.0)
|
74
|
+
formatador (>= 0.2.4)
|
75
|
+
listen (>= 2.7, < 4.0)
|
76
|
+
lumberjack (>= 1.0.12, < 2.0)
|
77
|
+
nenv (~> 0.1)
|
78
|
+
notiffany (~> 0.0)
|
79
|
+
pry (>= 0.13.0)
|
80
|
+
shellany (~> 0.0)
|
81
|
+
thor (>= 0.18.1)
|
82
|
+
guard-compat (1.2.1)
|
83
|
+
guard-rspec (4.7.3)
|
84
|
+
guard (~> 2.1)
|
85
|
+
guard-compat (~> 1.1)
|
86
|
+
rspec (>= 2.99.0, < 4.0)
|
87
|
+
hashdiff (1.0.1)
|
88
|
+
httparty (0.21.0)
|
89
|
+
mini_mime (>= 1.0.0)
|
90
|
+
multi_xml (>= 0.5.2)
|
91
|
+
i18n (1.12.0)
|
92
|
+
concurrent-ruby (~> 1.0)
|
93
|
+
json (2.6.3)
|
94
|
+
kramdown (2.4.0)
|
95
|
+
rexml
|
96
|
+
kramdown-parser-gfm (1.1.0)
|
97
|
+
kramdown (~> 2.0)
|
98
|
+
lefthook (1.5.2)
|
99
|
+
listen (3.8.0)
|
100
|
+
rb-fsevent (~> 0.10, >= 0.10.3)
|
101
|
+
rb-inotify (~> 0.9, >= 0.9.10)
|
102
|
+
lumberjack (1.2.8)
|
103
|
+
method_source (1.0.0)
|
104
|
+
mini_mime (1.1.2)
|
105
|
+
minitest (5.17.0)
|
106
|
+
multi_xml (0.6.0)
|
107
|
+
nap (1.1.0)
|
108
|
+
nenv (0.3.0)
|
109
|
+
no_proxy_fix (0.1.2)
|
110
|
+
notiffany (0.1.3)
|
111
|
+
nenv (~> 0.1)
|
112
|
+
shellany (~> 0.0)
|
113
|
+
octokit (5.6.1)
|
114
|
+
faraday (>= 1, < 3)
|
115
|
+
sawyer (~> 0.9)
|
116
|
+
open4 (1.3.4)
|
117
|
+
parallel (1.22.1)
|
118
|
+
parser (3.2.1.0)
|
119
|
+
ast (~> 2.4.1)
|
120
|
+
proc_to_ast (0.1.0)
|
121
|
+
coderay
|
122
|
+
parser
|
123
|
+
unparser
|
124
|
+
pry (0.14.2)
|
125
|
+
coderay (~> 1.1)
|
126
|
+
method_source (~> 1.0)
|
127
|
+
public_suffix (5.0.1)
|
128
|
+
rack (3.0.4.1)
|
129
|
+
rainbow (3.1.1)
|
130
|
+
rake (13.0.6)
|
131
|
+
rb-fsevent (0.11.2)
|
132
|
+
rb-inotify (0.10.1)
|
133
|
+
ffi (~> 1.0)
|
134
|
+
rchardet (1.8.0)
|
135
|
+
regexp_parser (2.7.0)
|
136
|
+
rexml (3.2.5)
|
137
|
+
rspec (3.12.0)
|
138
|
+
rspec-core (~> 3.12.0)
|
139
|
+
rspec-expectations (~> 3.12.0)
|
140
|
+
rspec-mocks (~> 3.12.0)
|
141
|
+
rspec-core (3.12.1)
|
142
|
+
rspec-support (~> 3.12.0)
|
143
|
+
rspec-expectations (3.12.2)
|
144
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
145
|
+
rspec-support (~> 3.12.0)
|
146
|
+
rspec-mocks (3.12.3)
|
147
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
148
|
+
rspec-support (~> 3.12.0)
|
149
|
+
rspec-parameterized (1.0.0)
|
150
|
+
rspec-parameterized-core (< 2)
|
151
|
+
rspec-parameterized-table_syntax (< 2)
|
152
|
+
rspec-parameterized-core (1.0.0)
|
153
|
+
parser
|
154
|
+
proc_to_ast
|
155
|
+
rspec (>= 2.13, < 4)
|
156
|
+
unparser
|
157
|
+
rspec-parameterized-table_syntax (1.0.0)
|
158
|
+
binding_of_caller
|
159
|
+
rspec-parameterized-core (< 2)
|
160
|
+
rspec-support (3.12.0)
|
161
|
+
rubocop (1.43.0)
|
162
|
+
json (~> 2.3)
|
163
|
+
parallel (~> 1.10)
|
164
|
+
parser (>= 3.2.0.0)
|
165
|
+
rainbow (>= 2.2.2, < 4.0)
|
166
|
+
regexp_parser (>= 1.8, < 3.0)
|
167
|
+
rexml (>= 3.2.5, < 4.0)
|
168
|
+
rubocop-ast (>= 1.24.1, < 2.0)
|
169
|
+
ruby-progressbar (~> 1.7)
|
170
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
171
|
+
rubocop-ast (1.26.0)
|
172
|
+
parser (>= 3.2.1.0)
|
173
|
+
rubocop-capybara (2.19.0)
|
174
|
+
rubocop (~> 1.41)
|
175
|
+
rubocop-factory_bot (2.24.0)
|
176
|
+
rubocop (~> 1.33)
|
177
|
+
rubocop-graphql (0.19.0)
|
178
|
+
rubocop (>= 0.87, < 2)
|
179
|
+
rubocop-performance (1.19.1)
|
180
|
+
rubocop (>= 1.7.0, < 2.0)
|
181
|
+
rubocop-ast (>= 0.4.0)
|
182
|
+
rubocop-rails (2.17.4)
|
183
|
+
activesupport (>= 4.2.0)
|
184
|
+
rack (>= 1.1)
|
185
|
+
rubocop (>= 1.33.0, < 2.0)
|
186
|
+
rubocop-rspec (2.24.1)
|
187
|
+
rubocop (~> 1.33)
|
188
|
+
rubocop-capybara (~> 2.17)
|
189
|
+
rubocop-factory_bot (~> 2.22)
|
190
|
+
ruby-progressbar (1.11.0)
|
191
|
+
ruby2_keywords (0.0.5)
|
192
|
+
sawyer (0.9.2)
|
193
|
+
addressable (>= 2.3.5)
|
194
|
+
faraday (>= 0.17.3, < 3)
|
195
|
+
shellany (0.0.1)
|
196
|
+
terminal-table (3.0.2)
|
197
|
+
unicode-display_width (>= 1.1.1, < 3)
|
198
|
+
thor (1.2.1)
|
199
|
+
timecop (0.9.6)
|
200
|
+
tzinfo (2.0.6)
|
201
|
+
concurrent-ruby (~> 1.0)
|
202
|
+
unicode-display_width (2.4.2)
|
203
|
+
unparser (0.6.7)
|
204
|
+
diff-lcs (~> 1.3)
|
205
|
+
parser (>= 3.2.0)
|
206
|
+
webmock (3.18.1)
|
207
|
+
addressable (>= 2.8.0)
|
208
|
+
crack (>= 0.3.2)
|
209
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
210
|
+
webrick (1.7.0)
|
211
|
+
yard (0.9.28)
|
212
|
+
webrick (~> 1.7.0)
|
213
|
+
|
214
|
+
PLATFORMS
|
215
|
+
ruby
|
216
|
+
|
217
|
+
DEPENDENCIES
|
218
|
+
climate_control
|
219
|
+
gitlab-dangerfiles!
|
220
|
+
gitlab-styles (~> 10.0)
|
221
|
+
guard-rspec (~> 4.7.3)
|
222
|
+
lefthook (~> 1.3)
|
223
|
+
rspec (~> 3.8)
|
224
|
+
rspec-parameterized
|
225
|
+
rubocop-rails (< 2.21.2)
|
226
|
+
timecop
|
227
|
+
webmock
|
228
|
+
yard
|
229
|
+
|
230
|
+
BUNDLED WITH
|
231
|
+
2.5.4
|
@@ -27,7 +27,8 @@ module Danger
|
|
27
27
|
analytics_instrumentation: '~"analytics instrumentation"',
|
28
28
|
import_integrate_be: '~"group::import and integrate" (backend)',
|
29
29
|
import_integrate_fe: '~"group::import and integrate" (frontend)',
|
30
|
-
|
30
|
+
Authentication: '~"group::authentication"',
|
31
|
+
Authorization: '~"group::authorization"',
|
31
32
|
Compliance: '~"group::compliance"',
|
32
33
|
}.freeze
|
33
34
|
# rubocop:enable Style/HashSyntax
|
@@ -1,34 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "../../gitlab/dangerfiles/approval"
|
4
|
+
require_relative "../../gitlab/dangerfiles/spinner"
|
3
5
|
require_relative "../../gitlab/dangerfiles/teammate"
|
4
|
-
require_relative "../../gitlab/dangerfiles/weightage/maintainers"
|
5
|
-
require_relative "../../gitlab/dangerfiles/weightage/reviewers"
|
6
6
|
|
7
7
|
module Danger
|
8
8
|
# Common helper functions for our danger scripts. See Danger::Helper
|
9
9
|
# for more details
|
10
10
|
class Roulette < Danger::Plugin
|
11
|
-
ROULETTE_DATA_URL = "https://gitlab-org.gitlab.io/gitlab-roulette/roulette.json"
|
12
11
|
HOURS_WHEN_PERSON_CAN_BE_PICKED = (6..14).freeze
|
13
|
-
|
14
|
-
Spin = Struct.new(:category, :reviewer, :maintainer, :optional_role)
|
15
12
|
HTTPError = Class.new(StandardError)
|
16
13
|
|
17
|
-
Approval = Struct.new(:category, :spin) do
|
18
|
-
def self.from_approval_rule(rule, maintainer)
|
19
|
-
category =
|
20
|
-
if rule["section"] == "codeowners"
|
21
|
-
"`#{rule["name"]}`"
|
22
|
-
else
|
23
|
-
rule["section"]
|
24
|
-
end.to_sym
|
25
|
-
|
26
|
-
spin = Spin.new(category, nil, maintainer, :reviewer)
|
27
|
-
|
28
|
-
new(category, spin)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
14
|
def prepare_categories(changes_keys)
|
33
15
|
categories = Set.new(changes_keys)
|
34
16
|
|
@@ -53,7 +35,7 @@ module Danger
|
|
53
35
|
#
|
54
36
|
# @return [Gitlab::Dangerfiles::Teammate]
|
55
37
|
def team_mr_author
|
56
|
-
@team_mr_author ||= find_member(helper.mr_author)
|
38
|
+
@team_mr_author ||= Gitlab::Dangerfiles::Teammate.find_member(helper.mr_author)
|
57
39
|
end
|
58
40
|
|
59
41
|
# Assigns GitLab team members to be reviewer and maintainer
|
@@ -63,70 +45,22 @@ module Danger
|
|
63
45
|
# @param categories [Array<Symbol>] An array of categories symbols.
|
64
46
|
#
|
65
47
|
# @return [Array<Spin>]
|
66
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
67
48
|
def spin(project = nil, categories = [:none], ux_fallback_wider_community_reviewer: teammate_pedroms)
|
49
|
+
# TODO: Deprecate the project argument. It prevents us from
|
50
|
+
# memorizing Spinner and can cause unexpected results if it's
|
51
|
+
# passing a different project than the merge request project.
|
68
52
|
project = (project || config_project_name).downcase
|
69
53
|
categories = categories.map { |category| category&.downcase || :none }
|
70
|
-
categories.reject! { |category| import_and_integrate_reject_category?(category, project) }
|
71
|
-
|
72
|
-
spins = categories.sort_by(&:to_s).map do |category|
|
73
|
-
spin_for_category(project, category)
|
74
|
-
end
|
75
|
-
|
76
|
-
backend_spin = spins.find { |spin| spin.category == :backend }
|
77
|
-
frontend_spin = spins.find { |spin| spin.category == :frontend }
|
78
|
-
|
79
|
-
spins.each do |spin|
|
80
|
-
case spin.category
|
81
|
-
when :qa
|
82
|
-
# MR includes QA changes, but also other changes, and author isn't an SET
|
83
|
-
if categories.size > 1 &&
|
84
|
-
!(team_mr_author && team_mr_author.capabilities(project).any? { |capability| capability.end_with?("qa") })
|
85
|
-
spin.optional_role = :maintainer
|
86
|
-
end
|
87
|
-
when :test
|
88
|
-
spin.optional_role = :maintainer
|
89
|
-
|
90
|
-
if spin.reviewer.nil?
|
91
|
-
# Fetch an already picked backend reviewer, or pick one otherwise
|
92
|
-
spin.reviewer = backend_spin&.reviewer || spin_for_category(project, :backend).reviewer
|
93
|
-
end
|
94
|
-
when :tooling
|
95
|
-
if spin.maintainer.nil?
|
96
|
-
# Fetch an already picked backend maintainer, or pick one otherwise
|
97
|
-
spin.maintainer = backend_spin&.maintainer || spin_for_category(project, :backend).maintainer
|
98
|
-
end
|
99
|
-
when :ci_template
|
100
|
-
if spin.maintainer.nil?
|
101
|
-
# Fetch an already picked backend maintainer, or pick one otherwise
|
102
|
-
spin.maintainer = backend_spin&.maintainer || spin_for_category(project, :backend).maintainer
|
103
|
-
end
|
104
|
-
when :analytics_instrumentation
|
105
|
-
spin.optional_role = :maintainer
|
106
|
-
|
107
|
-
if spin.maintainer.nil?
|
108
|
-
# Fetch an already picked maintainer, or pick one otherwise
|
109
|
-
spin.maintainer = backend_spin&.maintainer || frontend_spin&.maintainer || spin_for_category(project, :backend).maintainer
|
110
|
-
end
|
111
|
-
when :import_integrate_be, :import_integrate_fe
|
112
|
-
spin.optional_role = :maintainer
|
113
|
-
when :ux
|
114
|
-
spin.optional_role = :maintainer
|
115
54
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
end
|
124
|
-
|
125
|
-
spins
|
55
|
+
Gitlab::Dangerfiles::Spinner.new(
|
56
|
+
project: project,
|
57
|
+
author: helper.mr_author, team_author: team_mr_author,
|
58
|
+
labels: labels, categories: categories, random: random,
|
59
|
+
ux_fallback_wider_community_reviewer:
|
60
|
+
ux_fallback_wider_community_reviewer)
|
61
|
+
.spin
|
126
62
|
end
|
127
63
|
|
128
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
129
|
-
|
130
64
|
def codeowners_approvals
|
131
65
|
approval_rules = helper.mr_approval_state["rules"]
|
132
66
|
|
@@ -134,23 +68,31 @@ module Danger
|
|
134
68
|
|
135
69
|
required_approval_rules = unique_approval_rules(approval_rules)
|
136
70
|
required_approval_rules.filter_map do |rule|
|
137
|
-
spin_for_approval_rule?(rule)
|
138
|
-
|
71
|
+
if spin_for_approval_rule?(rule)
|
72
|
+
approver = Gitlab::Dangerfiles::Spinner.new(
|
73
|
+
project: config_project_name.downcase,
|
74
|
+
author: helper.mr_author, team_author: team_mr_author,
|
75
|
+
random: random
|
76
|
+
).spin_for_approver(rule)
|
77
|
+
|
78
|
+
Gitlab::Dangerfiles::Approval.from_approval_rule(rule, approver)
|
79
|
+
end
|
139
80
|
end
|
140
81
|
end
|
141
82
|
|
83
|
+
alias_method :required_approvals, :codeowners_approvals
|
84
|
+
|
85
|
+
# For backward compatibility
|
142
86
|
def warnings
|
143
|
-
|
87
|
+
Gitlab::Dangerfiles::Teammate.warnings
|
144
88
|
end
|
145
89
|
|
90
|
+
private
|
91
|
+
|
146
92
|
def teammate_pedroms
|
147
|
-
@teammate_pedroms ||= find_member("pedroms")
|
93
|
+
@teammate_pedroms ||= Gitlab::Dangerfiles::Teammate.find_member("pedroms")
|
148
94
|
end
|
149
95
|
|
150
|
-
alias_method :required_approvals, :codeowners_approvals
|
151
|
-
|
152
|
-
private
|
153
|
-
|
154
96
|
def spin_for_approval_rule?(rule)
|
155
97
|
rule["rule_type"] == "code_owner" &&
|
156
98
|
should_include_codeowners_rule?(rule) &&
|
@@ -188,179 +130,17 @@ module Danger
|
|
188
130
|
end
|
189
131
|
end
|
190
132
|
|
191
|
-
# @param [Gitlab::Dangerfiles::Teammate] person
|
192
|
-
# @return [Boolean]
|
193
|
-
def valid_person?(person)
|
194
|
-
!mr_author?(person) && person.available
|
195
|
-
end
|
196
|
-
|
197
|
-
# @param [Gitlab::Dangerfiles::Teammate] person
|
198
|
-
# @return [Boolean]
|
199
|
-
def mr_author?(person)
|
200
|
-
person.username == helper.mr_author
|
201
|
-
end
|
202
|
-
|
203
|
-
# @param [String] category name
|
204
|
-
# @return [Boolean]
|
205
|
-
def import_and_integrate_reject_category?(category, project)
|
206
|
-
# Reject Import and Integrate categories if the MR author has reviewing abilities for the category.
|
207
|
-
team_mr_author&.import_integrate_be?(project, category, labels) ||
|
208
|
-
team_mr_author&.import_integrate_fe?(project, category, labels)
|
209
|
-
end
|
210
|
-
|
211
133
|
def random
|
212
134
|
@random ||= Random.new(Digest::MD5.hexdigest(helper.mr_source_branch).to_i(16))
|
213
135
|
end
|
214
136
|
|
215
|
-
def spin_role_for_category(team, role, project, category)
|
216
|
-
team.select do |member|
|
217
|
-
member.public_send("#{role}?", project, category, labels)
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
# Known issue: If someone is rejected due to OOO, and then becomes not OOO, the
|
222
|
-
# selection will change on next spin.
|
223
|
-
#
|
224
|
-
# @param [Array<Gitlab::Dangerfiles::Teammate>] people
|
225
|
-
#
|
226
|
-
# @return [Gitlab::Dangerfiles::Teammate]
|
227
|
-
def spin_for_person(people)
|
228
|
-
shuffled_people = people.shuffle(random: random)
|
229
|
-
|
230
|
-
shuffled_people.find(&method(:valid_person?))
|
231
|
-
end
|
232
|
-
|
233
|
-
# Spin a reviewer for a particular approval rule
|
234
|
-
#
|
235
|
-
# @param [Hash] rule of approval
|
236
|
-
#
|
237
|
-
# @return [Gitlab::Dangerfiles::Teammate]
|
238
|
-
def spin_for_approver(rule)
|
239
|
-
approvers = rule["eligible_approvers"].filter_map do |approver|
|
240
|
-
find_member(approver["username"], project: config_project_name.downcase)
|
241
|
-
end
|
242
|
-
|
243
|
-
spin_for_person(approvers) || spin_for_approver_fallback(rule)
|
244
|
-
end
|
245
|
-
|
246
|
-
# It can be possible that we don't have a valid reviewer for approval.
|
247
|
-
# In this case, we sample again without considering:
|
248
|
-
#
|
249
|
-
# * If they're available
|
250
|
-
# * If they're an actual reviewer from roulette data
|
251
|
-
#
|
252
|
-
# We do this because we strictly require an approval from the approvers.
|
253
|
-
#
|
254
|
-
# @param [Hash] rule of approval
|
255
|
-
#
|
256
|
-
# @return [Gitlab::Dangerfiles::Teammate]
|
257
|
-
def spin_for_approver_fallback(rule)
|
258
|
-
fallback_approvers = rule["eligible_approvers"].map do |approver|
|
259
|
-
find_member(approver["username"]) ||
|
260
|
-
Gitlab::Dangerfiles::Teammate.new(approver)
|
261
|
-
end
|
262
|
-
|
263
|
-
# Intentionally not using `spin_for_person` to skip `valid_person?`.
|
264
|
-
# This should strictly return someone so we don't filter anything,
|
265
|
-
# and it's a fallback mechanism which should not happen often that
|
266
|
-
# deserves a complex algorithm.
|
267
|
-
fallback_approvers.sample(random: random)
|
268
|
-
end
|
269
|
-
|
270
|
-
def spin_for_category(project, category)
|
271
|
-
team = project_team(project)
|
272
|
-
reviewers, traintainers, maintainers =
|
273
|
-
%i[reviewer traintainer maintainer].map do |role|
|
274
|
-
spin_role_for_category(team, role, project, category)
|
275
|
-
end
|
276
|
-
|
277
|
-
weighted_reviewers = Gitlab::Dangerfiles::Weightage::Reviewers.new(reviewers, traintainers).execute
|
278
|
-
weighted_maintainers = Gitlab::Dangerfiles::Weightage::Maintainers.new(maintainers).execute
|
279
|
-
|
280
|
-
reviewer = spin_for_person(weighted_reviewers)
|
281
|
-
maintainer = spin_for_person(weighted_maintainers)
|
282
|
-
|
283
|
-
# allow projects with small number of reviewers to take from maintainers if possible
|
284
|
-
if reviewer.nil? && weighted_maintainers.uniq.size > 1
|
285
|
-
weighted_maintainers.delete(maintainer)
|
286
|
-
reviewer = spin_for_person(weighted_maintainers)
|
287
|
-
end
|
288
|
-
|
289
|
-
Spin.new(category, reviewer, maintainer, false)
|
290
|
-
end
|
291
|
-
|
292
137
|
def prepare_ux_category!(categories)
|
293
|
-
if labels.include?("Community contribution")
|
138
|
+
if labels.include?("Community contribution") ||
|
139
|
+
# We only want to spin a reviewer for merge requests which has a
|
140
|
+
# designer for the team.
|
141
|
+
Gitlab::Dangerfiles::Teammate.has_member_for_the_group?(
|
142
|
+
:ux, project: config_project_name.downcase, labels: labels)
|
294
143
|
categories << :ux
|
295
|
-
else
|
296
|
-
begin
|
297
|
-
# We only want to spin a reviewer for merge requests which has a
|
298
|
-
# designer for the team. There's no easy way to tell this, so we
|
299
|
-
# pretend this is a community contribution, in which case we only
|
300
|
-
# pick the team designer. If there's no one got picked, it means
|
301
|
-
# there's no designer for this team.
|
302
|
-
labels << "Community contribution"
|
303
|
-
|
304
|
-
# Don't use a fallback reviewer so when a group doesn't have
|
305
|
-
# available reviewers, it'll not give us any reviewers.
|
306
|
-
ux_spin = spin(nil, [:ux], ux_fallback_wider_community_reviewer: nil).first
|
307
|
-
|
308
|
-
categories << :ux if ux_spin.reviewer || ux_spin.maintainer
|
309
|
-
ensure
|
310
|
-
# Make sure we delete the label afterward
|
311
|
-
labels.delete("Community contribution")
|
312
|
-
end
|
313
|
-
end
|
314
|
-
end
|
315
|
-
|
316
|
-
# Fetches the given +url+ and parse its response as JSON.
|
317
|
-
#
|
318
|
-
# @param [String] url
|
319
|
-
#
|
320
|
-
# @return [Hash, Array, NilClass]
|
321
|
-
def http_get_json(url)
|
322
|
-
rsp = Net::HTTP.get_response(URI.parse(url))
|
323
|
-
|
324
|
-
if rsp.is_a?(Net::HTTPRedirection)
|
325
|
-
uri = URI.parse(rsp.header["location"])
|
326
|
-
|
327
|
-
uri.query = nil if uri
|
328
|
-
|
329
|
-
warnings << "Redirection detected: #{uri}."
|
330
|
-
return nil
|
331
|
-
end
|
332
|
-
|
333
|
-
unless rsp.is_a?(Net::HTTPOK)
|
334
|
-
message = rsp.message[0, 30]
|
335
|
-
warnings << "HTTPError: Failed to read #{url}: #{rsp.code} #{message}."
|
336
|
-
return nil
|
337
|
-
end
|
338
|
-
|
339
|
-
JSON.parse(rsp.body)
|
340
|
-
end
|
341
|
-
|
342
|
-
# Looks up the current list of GitLab team members and parses it into a
|
343
|
-
# useful form.
|
344
|
-
#
|
345
|
-
# @return [Array<Gitlab::Dangerfiles::Teammate>]
|
346
|
-
def company_members
|
347
|
-
@company_members ||= begin
|
348
|
-
data = http_get_json(ROULETTE_DATA_URL) || []
|
349
|
-
data.map { |hash| Gitlab::Dangerfiles::Teammate.new(hash) }
|
350
|
-
rescue JSON::ParserError
|
351
|
-
warnings << "Failed to parse JSON response from #{ROULETTE_DATA_URL}"
|
352
|
-
[]
|
353
|
-
end
|
354
|
-
end
|
355
|
-
|
356
|
-
def find_member(username, project: nil)
|
357
|
-
company_members.find do |member|
|
358
|
-
member.username == username &&
|
359
|
-
if project
|
360
|
-
member.in_project?(project)
|
361
|
-
else
|
362
|
-
true
|
363
|
-
end
|
364
144
|
end
|
365
145
|
end
|
366
146
|
|
@@ -377,18 +157,5 @@ module Danger
|
|
377
157
|
def labels
|
378
158
|
@labels ||= helper.mr_labels
|
379
159
|
end
|
380
|
-
|
381
|
-
# Like +team+, but only returns teammates in the current project, based on
|
382
|
-
# project_name.
|
383
|
-
#
|
384
|
-
# @return [Array<Gitlab::Dangerfiles::Teammate>]
|
385
|
-
def project_team(project_name)
|
386
|
-
company_members.select do |member|
|
387
|
-
member.in_project?(project_name)
|
388
|
-
end
|
389
|
-
rescue => err
|
390
|
-
warn("Reviewer roulette failed to load team data: #{err.message}")
|
391
|
-
[]
|
392
|
-
end
|
393
160
|
end
|
394
161
|
end
|