gitlab-dangerfiles 3.3.0 → 3.4.2
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/Gemfile +4 -0
- data/README.md +7 -0
- data/lefthook.yml +11 -0
- data/lib/danger/plugins/internal/helper.rb +10 -0
- data/lib/danger/plugins/roulette.rb +75 -8
- data/lib/gitlab/dangerfiles/teammate.rb +18 -5
- data/lib/gitlab/dangerfiles/version.rb +1 -1
- data/lib/gitlab/dangerfiles.rb +24 -25
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bcb56a4513a5f26239f800a2692bd8feedc8fed15bd2d4d2d93d9d2203e0df70
|
4
|
+
data.tar.gz: 0fd4fd6f9cacee27bf394c2b5ef17a7fee6b34b45b215e08524d5641fc0b092d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8bcfa9f04ce155b7e77393c9d8e2eef9ab6534111f2ce5eaa33d484ed5e811827172ba90f40589b81f1987fd35277eab91afcd5365282e4deb94de92752d5054
|
7
|
+
data.tar.gz: 526c301f0e148a4ed6faf4dcffb3afa4d142ab828eb36eb8e745783ba80964a769a4e3456683ad1898d0d4328bc3f25f5847557042fad7ae9f53b20b7d2e22e1
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -215,12 +215,19 @@ Latest documentation can be found at <https://www.rubydoc.info/gems/gitlab-dange
|
|
215
215
|
|
216
216
|
## Development
|
217
217
|
|
218
|
+
### Initial setup
|
219
|
+
|
218
220
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
219
221
|
|
220
222
|
To install this gem onto your local machine, run `bundle exec rake install`.
|
221
223
|
|
222
224
|
To release a new version, update the version number in `version.rb`, and get the MR merged by a maintainer. This will be then be packaged into a gem and pushed to [rubygems.org](https://rubygems.org) by the CI/CD.
|
223
225
|
|
226
|
+
### Activate lefthook locally
|
227
|
+
|
228
|
+
```shell
|
229
|
+
lefthook install
|
230
|
+
```
|
224
231
|
## Contributing
|
225
232
|
|
226
233
|
Bug reports and merge requests are welcome at https://gitlab.com/gitlab-org/gitlab-dangerfiles. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://gitlab.com/gitlab-org/gitlab-dangerfiles/blob/master/CODE_OF_CONDUCT.md).
|
data/lefthook.yml
ADDED
@@ -25,6 +25,7 @@ module Danger
|
|
25
25
|
product_intelligence: '~"product intelligence"',
|
26
26
|
integrations_be: '~"group::integrations" (backend)',
|
27
27
|
integrations_fe: '~"group::integrations" (frontend)',
|
28
|
+
"Authentication and Authorization": '~"group::authentication and authorization"',
|
28
29
|
}.freeze
|
29
30
|
|
30
31
|
# Allows to set specific rule's configuration by passing a block.
|
@@ -299,6 +300,15 @@ module Danger
|
|
299
300
|
gitlab_helper.mr_json["target_branch"]
|
300
301
|
end
|
301
302
|
|
303
|
+
# @return [Hash] +{}+ when not in the CI context, and the merge request approval state otherwise.
|
304
|
+
def mr_approval_state
|
305
|
+
return {} unless ci?
|
306
|
+
|
307
|
+
gitlab_helper.api.merge_request_approval_state(
|
308
|
+
mr_target_project_id, mr_iid
|
309
|
+
)
|
310
|
+
end
|
311
|
+
|
302
312
|
# @return [Boolean] +true+ when not in the CI context, and whether the MR is set to be squashed otherwise.
|
303
313
|
def squash_mr?
|
304
314
|
return true unless ci?
|
@@ -18,11 +18,20 @@ module Danger
|
|
18
18
|
Spin = Struct.new(:category, :reviewer, :maintainer, :optional_role, :timezone_experiment)
|
19
19
|
HTTPError = Class.new(StandardError)
|
20
20
|
|
21
|
+
Approval = Struct.new(:category, :spin) do
|
22
|
+
def self.from_approval_rule(rule, maintainer)
|
23
|
+
category = rule["section"].to_sym
|
24
|
+
spin = Spin.new(category, nil, maintainer, :reviewer)
|
25
|
+
|
26
|
+
new(category, spin)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
21
30
|
# Finds the +Gitlab::Dangerfiles::Teammate+ object whose username matches the MR author username.
|
22
31
|
#
|
23
32
|
# @return [Gitlab::Dangerfiles::Teammate]
|
24
33
|
def team_mr_author
|
25
|
-
@team_mr_author ||=
|
34
|
+
@team_mr_author ||= find_member(helper.mr_author)
|
26
35
|
end
|
27
36
|
|
28
37
|
# Assigns GitLab team members to be reviewer and maintainer
|
@@ -87,6 +96,18 @@ module Danger
|
|
87
96
|
spins
|
88
97
|
end
|
89
98
|
|
99
|
+
def required_approvals
|
100
|
+
approval_rules = helper.mr_approval_state["rules"]
|
101
|
+
|
102
|
+
return [] unless approval_rules
|
103
|
+
|
104
|
+
approval_rules.filter_map do |rule|
|
105
|
+
rule["rule_type"] == "code_owner" &&
|
106
|
+
rule["approvals_required"] > 0 &&
|
107
|
+
Approval.from_approval_rule(rule, spin_for_approver(rule))
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
90
111
|
private
|
91
112
|
|
92
113
|
# @param [Gitlab::Dangerfiles::Teammate] person
|
@@ -115,8 +136,8 @@ module Danger
|
|
115
136
|
team_mr_author&.integrations_fe?(project, category, helper.mr_labels)
|
116
137
|
end
|
117
138
|
|
118
|
-
def
|
119
|
-
Random.new(Digest::MD5.hexdigest(
|
139
|
+
def random
|
140
|
+
@random ||= Random.new(Digest::MD5.hexdigest(helper.mr_source_branch).to_i(16))
|
120
141
|
end
|
121
142
|
|
122
143
|
def spin_role_for_category(team, role, project, category)
|
@@ -131,7 +152,7 @@ module Danger
|
|
131
152
|
# @param [Array<Gitlab::Dangerfiles::Teammate>] people
|
132
153
|
#
|
133
154
|
# @return [Gitlab::Dangerfiles::Teammate]
|
134
|
-
def spin_for_person(people,
|
155
|
+
def spin_for_person(people, timezone_experiment: false)
|
135
156
|
shuffled_people = people.shuffle(random: random)
|
136
157
|
|
137
158
|
if timezone_experiment
|
@@ -141,6 +162,46 @@ module Danger
|
|
141
162
|
end
|
142
163
|
end
|
143
164
|
|
165
|
+
# Spin a reviewer for a particular approval rule
|
166
|
+
#
|
167
|
+
# @param [Hash] rule of approval
|
168
|
+
#
|
169
|
+
# @return [Gitlab::Dangerfiles::Teammate]
|
170
|
+
def spin_for_approver(rule)
|
171
|
+
# This will filter out approvers who are not even reviewers who
|
172
|
+
# don't show up in roulette data we're relying on.
|
173
|
+
# That's why `filter_map` is used.
|
174
|
+
approvers = rule["eligible_approvers"].filter_map do |approver|
|
175
|
+
find_member(approver["username"])
|
176
|
+
end
|
177
|
+
|
178
|
+
spin_for_person(approvers) || spin_for_approver_fallback(rule)
|
179
|
+
end
|
180
|
+
|
181
|
+
# It can be possible that we don't have a valid reviewer for approval.
|
182
|
+
# In this case, we sample again without considering:
|
183
|
+
#
|
184
|
+
# * If they're available
|
185
|
+
# * If they're an actual reviewer from roulette data
|
186
|
+
#
|
187
|
+
# We do this because we strictly require an approval from the approvers.
|
188
|
+
#
|
189
|
+
# @param [Hash] rule of approval
|
190
|
+
#
|
191
|
+
# @return [Gitlab::Dangerfiles::Teammate]
|
192
|
+
def spin_for_approver_fallback(rule)
|
193
|
+
fallback_approvers = rule["eligible_approvers"].map do |approver|
|
194
|
+
find_member(approver["username"]) ||
|
195
|
+
Gitlab::Dangerfiles::Teammate.new(approver)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Intentionally not using `spin_for_person` to skip `valid_person?`.
|
199
|
+
# This should strictly return someone so we don't filter anything,
|
200
|
+
# and it's a fallback mechanism which should not happen often that
|
201
|
+
# deserves a complex algorithm.
|
202
|
+
fallback_approvers.sample(random: random)
|
203
|
+
end
|
204
|
+
|
144
205
|
def spin_for_category(project, category, timezone_experiment: false)
|
145
206
|
team = project_team(project)
|
146
207
|
reviewers, traintainers, maintainers =
|
@@ -148,13 +209,11 @@ module Danger
|
|
148
209
|
spin_role_for_category(team, role, project, category)
|
149
210
|
end
|
150
211
|
|
151
|
-
random = new_random(helper.mr_source_branch)
|
152
|
-
|
153
212
|
weighted_reviewers = Gitlab::Dangerfiles::Weightage::Reviewers.new(reviewers, traintainers).execute
|
154
213
|
weighted_maintainers = Gitlab::Dangerfiles::Weightage::Maintainers.new(maintainers).execute
|
155
214
|
|
156
|
-
reviewer = spin_for_person(weighted_reviewers,
|
157
|
-
maintainer = spin_for_person(weighted_maintainers,
|
215
|
+
reviewer = spin_for_person(weighted_reviewers, timezone_experiment: timezone_experiment)
|
216
|
+
maintainer = spin_for_person(weighted_maintainers, timezone_experiment: timezone_experiment)
|
158
217
|
|
159
218
|
Spin.new(category, reviewer, maintainer, false, timezone_experiment)
|
160
219
|
end
|
@@ -167,6 +226,10 @@ module Danger
|
|
167
226
|
def http_get_json(url)
|
168
227
|
rsp = Net::HTTP.get_response(URI.parse(url))
|
169
228
|
|
229
|
+
if rsp.is_a?(Net::HTTPRedirection)
|
230
|
+
raise "Redirection detected: #{rsp.header["location"]}"
|
231
|
+
end
|
232
|
+
|
170
233
|
unless rsp.is_a?(Net::HTTPOK)
|
171
234
|
raise HTTPError, "Failed to read #{url}: #{rsp.code} #{rsp.message}"
|
172
235
|
end
|
@@ -187,6 +250,10 @@ module Danger
|
|
187
250
|
end
|
188
251
|
end
|
189
252
|
|
253
|
+
def find_member(username)
|
254
|
+
company_members.find { |person| person.username == username }
|
255
|
+
end
|
256
|
+
|
190
257
|
# Like +team+, but only returns teammates in the current project, based on
|
191
258
|
# project_name.
|
192
259
|
#
|
@@ -10,7 +10,8 @@ module Gitlab
|
|
10
10
|
@options = options
|
11
11
|
@username = options["username"]
|
12
12
|
@name = options["name"]
|
13
|
-
@markdown_name = options["markdown_name"]
|
13
|
+
@markdown_name = options["markdown_name"] ||
|
14
|
+
default_markdown_name(*options.values_at("username", "name"))
|
14
15
|
@role = options["role"]
|
15
16
|
@projects = process_projects(options["projects"])
|
16
17
|
@available = options["available"]
|
@@ -23,6 +24,10 @@ module Gitlab
|
|
23
24
|
options
|
24
25
|
end
|
25
26
|
|
27
|
+
def inspect
|
28
|
+
"#<#{self.class} @username=#{username.inspect}>"
|
29
|
+
end
|
30
|
+
|
26
31
|
def ==(other)
|
27
32
|
return false unless other.respond_to?(:username)
|
28
33
|
|
@@ -62,7 +67,7 @@ module Gitlab
|
|
62
67
|
end
|
63
68
|
|
64
69
|
def markdown_name(author: nil)
|
65
|
-
"#{@markdown_name}
|
70
|
+
"#{@markdown_name}#{utc_offset_text(author)}"
|
66
71
|
end
|
67
72
|
|
68
73
|
def local_hour
|
@@ -79,6 +84,10 @@ module Gitlab
|
|
79
84
|
|
80
85
|
private
|
81
86
|
|
87
|
+
def default_markdown_name(username, name)
|
88
|
+
"[#{name}](https://gitlab.com/#{username}) (`@#{username}`)"
|
89
|
+
end
|
90
|
+
|
82
91
|
def process_projects(projects)
|
83
92
|
return nil unless projects
|
84
93
|
|
@@ -88,15 +97,19 @@ module Gitlab
|
|
88
97
|
end
|
89
98
|
|
90
99
|
def utc_offset_text(author = nil)
|
100
|
+
return unless tz_offset_hours
|
101
|
+
|
91
102
|
offset_text = if floored_offset_hours >= 0
|
92
103
|
"UTC+#{floored_offset_hours}"
|
93
104
|
else
|
94
105
|
"UTC#{floored_offset_hours}"
|
95
106
|
end
|
96
107
|
|
97
|
-
|
98
|
-
|
99
|
-
|
108
|
+
if author
|
109
|
+
" (#{offset_text}, #{offset_diff_compared_to_author(author)})"
|
110
|
+
else
|
111
|
+
" (#{offset_text})"
|
112
|
+
end
|
100
113
|
end
|
101
114
|
|
102
115
|
def offset_diff_compared_to_author(author)
|
data/lib/gitlab/dangerfiles.rb
CHANGED
@@ -58,7 +58,7 @@ module Gitlab
|
|
58
58
|
danger_plugin.import_plugin(File.expand_path("../danger/plugins/*.rb", __dir__))
|
59
59
|
|
60
60
|
Dir.glob(File.expand_path("danger/plugins/*.rb", config.project_root)).sort.each do |path|
|
61
|
-
puts "Importing plugin at #{path}" if
|
61
|
+
puts "Importing plugin at #{path}" if helper_plugin.ci?
|
62
62
|
danger_plugin.import_plugin(path)
|
63
63
|
end
|
64
64
|
end
|
@@ -87,10 +87,9 @@ module Gitlab
|
|
87
87
|
return if helper_plugin.release_automation?
|
88
88
|
|
89
89
|
rules = filtered_rules(only, except)
|
90
|
-
puts "Running rules: #{rules}\n" if dangerfile.verbose
|
91
90
|
|
92
91
|
rules.each do |rule, path|
|
93
|
-
puts "Importing rule #{rule} at #{path}" if
|
92
|
+
puts "Importing rule #{rule} at #{path}" if helper_plugin.ci?
|
94
93
|
danger_plugin.import_dangerfile(path: path)
|
95
94
|
end
|
96
95
|
end
|
@@ -116,45 +115,45 @@ module Gitlab
|
|
116
115
|
|
117
116
|
attr_reader :dangerfile
|
118
117
|
|
118
|
+
def filtered_rules(only_rules, except_rules)
|
119
|
+
only_rules = Array(only_rules).compact.map(&:to_s)
|
120
|
+
|
121
|
+
rules = allowed_rules_based_on_context.reject { |rule, _v| except_rules.include?(rule) }
|
122
|
+
|
123
|
+
if only_rules.any?
|
124
|
+
rules.select! { |rule, _v| only_rules.include?(rule) }
|
125
|
+
end
|
126
|
+
|
127
|
+
rules.sort.to_h
|
128
|
+
end
|
129
|
+
|
130
|
+
def allowed_rules_based_on_context
|
131
|
+
helper_plugin.ci? ? all_rules : local_rules
|
132
|
+
end
|
133
|
+
|
134
|
+
def all_rules
|
135
|
+
all_gem_rules.merge(custom_rules)
|
136
|
+
end
|
137
|
+
|
119
138
|
def all_gem_rules
|
120
|
-
@all_gem_rules ||= Dir.glob(File.join(RULES_DIR, "*")).
|
139
|
+
@all_gem_rules ||= Dir.glob(File.join(RULES_DIR, "*")).each_with_object({}) do |path, memo|
|
121
140
|
rule_name = File.basename(path)
|
122
141
|
memo[rule_name] = path if File.directory?(path) && File.exist?(File.join(path, "Dangerfile"))
|
123
142
|
end
|
124
143
|
end
|
125
144
|
|
126
145
|
def custom_rules
|
127
|
-
@custom_rules ||= Dir.glob(File.expand_path("danger/*", config.project_root)).
|
146
|
+
@custom_rules ||= Dir.glob(File.expand_path("danger/*", config.project_root)).each_with_object({}) do |path, memo|
|
128
147
|
rule_name = File.basename(path)
|
129
148
|
memo[rule_name] = path if File.directory?(path) && File.exist?(File.join(path, "Dangerfile"))
|
130
149
|
end
|
131
150
|
end
|
132
151
|
|
133
|
-
def all_rules
|
134
|
-
all_gem_rules.merge(custom_rules)
|
135
|
-
end
|
136
|
-
|
137
152
|
def local_rules
|
138
153
|
ci_only_rules = CI_ONLY_RULES | config.ci_only_rules
|
139
154
|
all_rules.reject { |rule, _v| ci_only_rules.include?(rule) }
|
140
155
|
end
|
141
156
|
|
142
|
-
def allowed_rules_based_on_context
|
143
|
-
helper_plugin.ci? ? all_rules : local_rules
|
144
|
-
end
|
145
|
-
|
146
|
-
def filtered_rules(only_rules, except_rules)
|
147
|
-
only_rules = Array(only_rules).compact.map(&:to_s)
|
148
|
-
|
149
|
-
rules = allowed_rules_based_on_context.reject { |rule, _v| except_rules.include?(rule) }
|
150
|
-
|
151
|
-
if only_rules.any?
|
152
|
-
rules.select! { |rule, _v| only_rules.include?(rule) }
|
153
|
-
end
|
154
|
-
|
155
|
-
rules
|
156
|
-
end
|
157
|
-
|
158
157
|
def danger_plugin
|
159
158
|
@danger_plugin ||= dangerfile.plugins[Danger::DangerfileDangerPlugin]
|
160
159
|
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: 3.
|
4
|
+
version: 3.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitLab
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-06-
|
11
|
+
date: 2022-06-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -163,6 +163,7 @@ files:
|
|
163
163
|
- fixtures/emojis/aliases.json
|
164
164
|
- fixtures/emojis/digests.json
|
165
165
|
- gitlab-dangerfiles.gemspec
|
166
|
+
- lefthook.yml
|
166
167
|
- lib/danger/plugins/changelog.rb
|
167
168
|
- lib/danger/plugins/internal/helper.rb
|
168
169
|
- lib/danger/plugins/roulette.rb
|