gitlab-dangerfiles 0.8.1 → 2.0.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/.yardopts +5 -0
- data/Gemfile +1 -0
- data/LICENSE.txt +1 -1
- data/README.md +46 -6
- data/gitlab-dangerfiles.gemspec +4 -4
- data/lib/danger/plugins/helper.rb +417 -0
- data/lib/danger/{roulette.rb → plugins/roulette.rb} +83 -64
- data/lib/danger/rules/changes_size/Dangerfile +10 -0
- data/lib/gitlab/dangerfiles.rb +81 -2
- data/lib/gitlab/dangerfiles/changes.rb +22 -0
- data/lib/gitlab/dangerfiles/commit_linter.rb +1 -1
- data/lib/gitlab/dangerfiles/config.rb +17 -0
- data/lib/gitlab/dangerfiles/emoji_checker.rb +1 -0
- data/lib/gitlab/dangerfiles/spec_helper.rb +51 -5
- data/lib/gitlab/dangerfiles/teammate.rb +2 -0
- data/lib/gitlab/dangerfiles/version.rb +1 -1
- data/lib/gitlab/dangerfiles/weightage.rb +1 -0
- data/lib/gitlab/dangerfiles/weightage/maintainers.rb +1 -0
- data/lib/gitlab/dangerfiles/weightage/reviewers.rb +1 -0
- metadata +12 -9
- data/lib/danger/helper.rb +0 -257
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0d3407bad03ff3220a55329e625c1fd5a962b4ffe29b4ab1da68a7e998761024
|
4
|
+
data.tar.gz: a9f31b18452500fa6e437a847b1d726cbbe2c47a1912c5e40fd18196ca3716fa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3390ab204b7572654e748198fb1d03b70056ab26b32a8ea29cc2c624e307e59c5cfab5306afb8900c71df62671b984ccdbb21e7c1e9adb16b2ab43a9299774c0
|
7
|
+
data.tar.gz: '06879db4e54bff63e540793f8c94f3c9412d6481641144bfa80934bb36a82872c76e9693c86aa9b381d78528da06a27795f620ff9127829e4904938807958ede'
|
data/.yardopts
ADDED
data/Gemfile
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# Gitlab::Dangerfiles
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
3
|
+
The goal of this gem is to centralize Danger plugins and rules that to be used by multiple GitLab projects.
|
6
4
|
|
7
5
|
## Installation
|
8
6
|
|
@@ -14,15 +12,57 @@ gem 'gitlab-dangerfiles'
|
|
14
12
|
|
15
13
|
And then execute:
|
16
14
|
|
17
|
-
|
15
|
+
```sh
|
16
|
+
$ bundle install
|
17
|
+
```
|
18
18
|
|
19
19
|
Or install it yourself as:
|
20
20
|
|
21
|
-
|
21
|
+
```sh
|
22
|
+
$ gem install gitlab-dangerfiles
|
23
|
+
```
|
22
24
|
|
23
25
|
## Usage
|
24
26
|
|
25
|
-
|
27
|
+
### Importing plugins and rules
|
28
|
+
|
29
|
+
In your project's `Dangerfile`, add the following two line to import the plugins and rules from this gem:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
# Get an instance of Gitlab::Dangerfiles
|
33
|
+
gitlab_dangerfiles = Gitlab::Dangerfiles::Engine.new(self)
|
34
|
+
|
35
|
+
# Import all plugins from the gem
|
36
|
+
gitlab_dangerfiles.import_plugins
|
37
|
+
|
38
|
+
# Import all rules from the gem
|
39
|
+
gitlab_dangerfiles.import_dangerfiles
|
40
|
+
|
41
|
+
# Or import a subset of rules from the gem
|
42
|
+
gitlab_dangerfiles.import_dangerfiles(rules: [:changes_size])
|
43
|
+
```
|
44
|
+
|
45
|
+
### Plugins
|
46
|
+
|
47
|
+
Danger plugins are located under `lib/danger/plugins`.
|
48
|
+
|
49
|
+
- `Danger::Helper` available in `Dangerfile`s as `helper`
|
50
|
+
- `Danger::Roulette` available in `Dangerfile`s as `roulette`
|
51
|
+
|
52
|
+
For the full documentation about the plugins, please see https://www.rubydoc.info/gems/gitlab-dangerfiles.
|
53
|
+
|
54
|
+
### Rules
|
55
|
+
|
56
|
+
|
57
|
+
Danger rules are located under `lib/danger/rules`.
|
58
|
+
|
59
|
+
#### `changes_size`
|
60
|
+
|
61
|
+
##### Available configurations
|
62
|
+
|
63
|
+
- `code_size_thresholds`: A hash of the form `{ high: 42, medium: 12 }` where
|
64
|
+
`:high` is the lines changed threshold which triggers an error, and
|
65
|
+
`:medium` is the lines changed threshold which triggers a warning.
|
26
66
|
|
27
67
|
## Development
|
28
68
|
|
data/gitlab-dangerfiles.gemspec
CHANGED
@@ -3,8 +3,8 @@ require_relative "lib/gitlab/dangerfiles/version"
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
4
|
spec.name = "gitlab-dangerfiles"
|
5
5
|
spec.version = Gitlab::Dangerfiles::VERSION
|
6
|
-
spec.authors = ["
|
7
|
-
spec.email = ["
|
6
|
+
spec.authors = ["GitLab"]
|
7
|
+
spec.email = ["gitlab_rubygems@gitlab.com"]
|
8
8
|
|
9
9
|
spec.summary = %q{This gem provides common Dangerfile and plugins for GitLab projects.}
|
10
10
|
spec.description = %q{This gem provides common Dangerfile and plugins for GitLab projects.}
|
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
|
|
16
16
|
|
17
17
|
spec.metadata["homepage_uri"] = spec.homepage
|
18
18
|
spec.metadata["source_code_uri"] = "https://gitlab.com/gitlab-org/gitlab-dangerfiles"
|
19
|
-
spec.metadata["changelog_uri"] = "https://gitlab.com/gitlab-org/gitlab-dangerfiles"
|
19
|
+
spec.metadata["changelog_uri"] = "https://gitlab.com/gitlab-org/gitlab-dangerfiles/-/releases"
|
20
20
|
|
21
21
|
# Specify which files should be added to the gem when it is released.
|
22
22
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
@@ -27,7 +27,7 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
28
|
spec.require_paths = ["lib"]
|
29
29
|
|
30
|
-
spec.add_dependency "danger"
|
30
|
+
spec.add_dependency "danger-gitlab"
|
31
31
|
|
32
32
|
spec.add_development_dependency "rspec", "~> 3.0"
|
33
33
|
spec.add_development_dependency "rspec-parameterized"
|
@@ -0,0 +1,417 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "net/http"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
require_relative "../../gitlab/dangerfiles/changes"
|
7
|
+
require_relative "../../gitlab/dangerfiles/config"
|
8
|
+
require_relative "../../gitlab/dangerfiles/teammate"
|
9
|
+
require_relative "../../gitlab/dangerfiles/title_linting"
|
10
|
+
|
11
|
+
module Danger
|
12
|
+
# Common helper functions for our danger scripts.
|
13
|
+
class Helper < Danger::Plugin
|
14
|
+
RELEASE_TOOLS_BOT = "gitlab-release-tools-bot"
|
15
|
+
CATEGORY_LABELS = {
|
16
|
+
docs: "~documentation", # Docs are reviewed along DevOps stages, so don't need roulette for now.
|
17
|
+
none: "",
|
18
|
+
qa: "~QA",
|
19
|
+
test: "~test ~Quality for `spec/features/*`",
|
20
|
+
engineering_productivity: '~"Engineering Productivity" for CI, Danger',
|
21
|
+
ci_template: '~"ci::templates"',
|
22
|
+
product_intelligence: '~"product intelligence"',
|
23
|
+
}.freeze
|
24
|
+
|
25
|
+
# Allows to set specific rule's configuration by passing a block.
|
26
|
+
#
|
27
|
+
# @yield [c] Yield a Gitlab::Dangerfiles::Config object
|
28
|
+
#
|
29
|
+
# @yieldparam [Gitlab::Dangerfiles::Config] The Gitlab::Dangerfiles::Config object
|
30
|
+
# @yieldreturn [Gitlab::Dangerfiles::Config] The Gitlab::Dangerfiles::Config object
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# helper.config do |config|
|
34
|
+
# config.code_size_thresholds = { high: 42, medium: 12 }
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# @return [Gitlab::Dangerfiles::Config]
|
38
|
+
def config
|
39
|
+
(@config ||= Gitlab::Dangerfiles::Config.new).tap do |c|
|
40
|
+
yield c if block_given?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# @example
|
45
|
+
# <a href='https://gitlab.com/artsy/eigen/blob/561827e46167077b5e53515b4b7349b8ae04610b/file.txt'>file.txt</a>
|
46
|
+
#
|
47
|
+
# @param [String, Array<String>] paths
|
48
|
+
# A list of strings to convert to gitlab anchors
|
49
|
+
# @param [Boolean] full_path
|
50
|
+
# Shows the full path as the link's text, defaults to +true+.
|
51
|
+
#
|
52
|
+
# @see https://danger.systems/reference.html Danger reference where #html_link is described
|
53
|
+
# @see https://github.com/danger/danger/blob/eca19719d3e585fe1cc46bc5377f9aa955ebf609/lib/danger/danger_core/plugins/dangerfile_gitlab_plugin.rb#L216 Danger reference where #html_link is implemented
|
54
|
+
#
|
55
|
+
# @return [String] a list of HTML anchors for a file, or multiple files
|
56
|
+
def html_link(paths, full_path: true)
|
57
|
+
ci? ? gitlab_helper.html_link(paths, full_path: full_path) : paths
|
58
|
+
end
|
59
|
+
|
60
|
+
# @return [Boolean] whether we're in the CI context or not.
|
61
|
+
def ci?
|
62
|
+
!gitlab_helper.nil?
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [Array<String>] a list of filenames added in this MR.
|
66
|
+
def added_files
|
67
|
+
@added_files ||= if changes_from_api
|
68
|
+
changes_from_api.select { |file| file["new_file"] }.map { |file| file["new_path"] }
|
69
|
+
else
|
70
|
+
git.added_files.to_a
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# @return [Array<String>] a list of filenames modifier in this MR.
|
75
|
+
def modified_files
|
76
|
+
@modified_files ||= if changes_from_api
|
77
|
+
changes_from_api.select { |file| !file["new_file"] && !file["deleted_file"] && !file["renamed_file"] }.map { |file| file["new_path"] }
|
78
|
+
else
|
79
|
+
git.modified_files.to_a
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [Array<String>] a list of filenames renamed in this MR.
|
84
|
+
def renamed_files
|
85
|
+
@renamed_files ||= if changes_from_api
|
86
|
+
changes_from_api.select { |file| file["renamed_file"] }.each_with_object([]) do |file, memo|
|
87
|
+
memo << { before: file["old_path"], after: file["new_path"] }
|
88
|
+
end
|
89
|
+
else
|
90
|
+
git.renamed_files.to_a
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# @return [Array<String>] a list of filenames deleted in this MR.
|
95
|
+
def deleted_files
|
96
|
+
@deleted_files ||= if changes_from_api
|
97
|
+
changes_from_api.select { |file| file["deleted_file"] }.map { |file| file["new_path"] }
|
98
|
+
else
|
99
|
+
git.deleted_files.to_a
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# @example
|
104
|
+
# # Considering these changes:
|
105
|
+
# # - A new_file.rb
|
106
|
+
# # - D deleted_file.rb
|
107
|
+
# # - M modified_file.rb
|
108
|
+
# # - R renamed_file_before.rb -> renamed_file_after.rb
|
109
|
+
# # it will return:
|
110
|
+
#
|
111
|
+
# #=> ['new_file.rb', 'modified_file.rb', 'renamed_file_after.rb']
|
112
|
+
#
|
113
|
+
#
|
114
|
+
# @return [Array<String>] a list of all files that have been added, modified or renamed.
|
115
|
+
# +modified_files+ might contain paths that already have been renamed,
|
116
|
+
# so we need to remove them from the list.
|
117
|
+
def all_changed_files
|
118
|
+
Set.new
|
119
|
+
.merge(added_files)
|
120
|
+
.merge(modified_files)
|
121
|
+
.merge(renamed_files.map { |x| x[:after] })
|
122
|
+
.subtract(renamed_files.map { |x| x[:before] })
|
123
|
+
.to_a
|
124
|
+
.sort
|
125
|
+
end
|
126
|
+
|
127
|
+
# @param filename [String] A file name for which we want the diff.
|
128
|
+
#
|
129
|
+
# @example
|
130
|
+
# # Considering changing a line in lib/gitlab/usage_data.rb, it will return:
|
131
|
+
#
|
132
|
+
# ["--- a/lib/gitlab/usage_data.rb",
|
133
|
+
# "+++ b/lib/gitlab/usage_data.rb",
|
134
|
+
# "+ # Test change",
|
135
|
+
# "- # Old change"]
|
136
|
+
#
|
137
|
+
# @return [Array<String>] an array of changed lines in Git diff format.
|
138
|
+
def changed_lines(filename)
|
139
|
+
diff = diff_for_file(filename)
|
140
|
+
return [] unless diff
|
141
|
+
|
142
|
+
diff.split("\n").select { |line| %r{^[+-]}.match?(line) }
|
143
|
+
end
|
144
|
+
|
145
|
+
def release_automation?
|
146
|
+
gitlab_helper&.mr_author == RELEASE_TOOLS_BOT
|
147
|
+
end
|
148
|
+
|
149
|
+
# @param items [Array<String>] An array of items to transform into a bullet list.
|
150
|
+
#
|
151
|
+
# @example
|
152
|
+
# markdown_list(%w[foo bar])
|
153
|
+
# # => * foo
|
154
|
+
# * bar
|
155
|
+
#
|
156
|
+
# @return [String] a bullet list for the given +items+. If there are more than 10 items, wrap the list in a +<details></details>+ block.
|
157
|
+
def markdown_list(items)
|
158
|
+
list = items.map { |item| "* `#{item}`" }.join("\n")
|
159
|
+
|
160
|
+
if items.size > 10
|
161
|
+
"\n<details>\n\n#{list}\n\n</details>\n"
|
162
|
+
else
|
163
|
+
list
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# @param categories [{Regexp => Array<Symbol>}, {Array<Regexp> => Array<Symbol>}] A hash of the form +{ filename_regex => categories, [filename_regex, changes_regex] => categories }+.
|
168
|
+
# +filename_regex+ is the regex pattern to match file names. +changes_regex+ is the regex pattern to
|
169
|
+
# match changed lines in files that match +filename_regex+
|
170
|
+
#
|
171
|
+
# @return [{Symbol => Array<String>}] a hash of the type +{ category1: ["file1", "file2"], category2: ["file3", "file4"] }+
|
172
|
+
# using filename regex (+filename_regex+) and specific change regex (+changes_regex+) from the given +categories+ hash.
|
173
|
+
def changes_by_category(categories)
|
174
|
+
all_changed_files.each_with_object(Hash.new { |h, k| h[k] = [] }) do |file, hash|
|
175
|
+
categories_for_file(file, categories).each { |category| hash[category] << file }
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# @param categories [{Regexp => Array<Symbol>}, {Array<Regexp> => Array<Symbol>}] A hash of the form +{ filename_regex => categories, [filename_regex, changes_regex] => categories }+.
|
180
|
+
# +filename_regex+ is the regex pattern to match file names. +changes_regex+ is the regex pattern to
|
181
|
+
# match changed lines in files that match +filename_regex+
|
182
|
+
#
|
183
|
+
# @return [Gitlab::Dangerfiles::Changes] a +Gitlab::Dangerfiles::Changes+ object that represents the changes of an MR
|
184
|
+
# using filename regex (+filename_regex+) and specific change regex (+changes_regex+) from the given +categories+ hash.
|
185
|
+
def changes(categories)
|
186
|
+
Gitlab::Dangerfiles::Changes.new([]).tap do |changes|
|
187
|
+
added_files.each do |file|
|
188
|
+
categories_for_file(file, categories).each { |category| changes << Gitlab::Dangerfiles::Change.new(file, :added, category) }
|
189
|
+
end
|
190
|
+
|
191
|
+
modified_files.each do |file|
|
192
|
+
categories_for_file(file, categories).each { |category| changes << Gitlab::Dangerfiles::Change.new(file, :modified, category) }
|
193
|
+
end
|
194
|
+
|
195
|
+
deleted_files.each do |file|
|
196
|
+
categories_for_file(file, categories).each { |category| changes << Gitlab::Dangerfiles::Change.new(file, :deleted, category) }
|
197
|
+
end
|
198
|
+
|
199
|
+
renamed_files.map { |x| x[:before] }.each do |file|
|
200
|
+
categories_for_file(file, categories).each { |category| changes << Gitlab::Dangerfiles::Change.new(file, :renamed_before, category) }
|
201
|
+
end
|
202
|
+
|
203
|
+
renamed_files.map { |x| x[:after] }.each do |file|
|
204
|
+
categories_for_file(file, categories).each { |category| changes << Gitlab::Dangerfiles::Change.new(file, :renamed_after, category) }
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# @param filename [String] A file name.
|
210
|
+
# @param categories [{Regexp => Array<Symbol>}, {Array<Regexp> => Array<Symbol>}] A hash of the form +{ filename_regex => categories, [filename_regex, changes_regex] => categories }+.
|
211
|
+
# +filename_regex+ is the regex pattern to match file names. +changes_regex+ is the regex pattern to
|
212
|
+
# match changed lines in files that match +filename_regex+
|
213
|
+
#
|
214
|
+
# @return [Array<Symbol>] the categories a file is in, e.g., +[:frontend]+, +[:backend]+, or +%i[frontend engineering_productivity]+
|
215
|
+
# using filename regex (+filename_regex+) and specific change regex (+changes_regex+) from the given +categories+ hash.
|
216
|
+
def categories_for_file(filename, categories)
|
217
|
+
_, categories = categories.find do |key, _|
|
218
|
+
filename_regex, changes_regex = Array(key)
|
219
|
+
|
220
|
+
found = filename_regex.match?(filename)
|
221
|
+
found &&= changed_lines(filename).any? { |changed_line| changes_regex.match?(changed_line) } if changes_regex
|
222
|
+
|
223
|
+
found
|
224
|
+
end
|
225
|
+
|
226
|
+
Array(categories || :unknown)
|
227
|
+
end
|
228
|
+
|
229
|
+
# @param category [Symbol] A category.
|
230
|
+
#
|
231
|
+
# @return [String] the GFM for a category label, making its best guess if it's not
|
232
|
+
# a category we know about.
|
233
|
+
def label_for_category(category)
|
234
|
+
CATEGORY_LABELS.fetch(category, "~#{category}")
|
235
|
+
end
|
236
|
+
|
237
|
+
# @return [String] +""+ when not in the CI context, and the MR IID as a string otherwise.
|
238
|
+
def mr_iid
|
239
|
+
return "" unless ci?
|
240
|
+
|
241
|
+
gitlab_helper.mr_json["iid"].to_s
|
242
|
+
end
|
243
|
+
|
244
|
+
# @return [String] +`whoami`+ when not in the CI context, and the MR author username otherwise.
|
245
|
+
def mr_author
|
246
|
+
return `whoami`.strip unless ci?
|
247
|
+
|
248
|
+
gitlab_helper.mr_author
|
249
|
+
end
|
250
|
+
|
251
|
+
# @return [String] +""+ when not in the CI context, and the MR title otherwise.
|
252
|
+
def mr_title
|
253
|
+
return "" unless ci?
|
254
|
+
|
255
|
+
gitlab_helper.mr_json["title"]
|
256
|
+
end
|
257
|
+
|
258
|
+
# @return [String] +""+ when not in the CI context, and the MR URL otherwise.
|
259
|
+
def mr_web_url
|
260
|
+
return "" unless ci?
|
261
|
+
|
262
|
+
gitlab_helper.mr_json["web_url"]
|
263
|
+
end
|
264
|
+
|
265
|
+
# @return [Array<String>] +[]+ when not in the CI context, and the MR labels otherwise.
|
266
|
+
def mr_labels
|
267
|
+
return [] unless ci?
|
268
|
+
|
269
|
+
gitlab_helper.mr_labels
|
270
|
+
end
|
271
|
+
|
272
|
+
# @return [String] +`git rev-parse --abbrev-ref HEAD`+ when not in the CI context, and the MR source branch otherwise.
|
273
|
+
def mr_source_branch
|
274
|
+
return `git rev-parse --abbrev-ref HEAD`.strip unless ci?
|
275
|
+
|
276
|
+
gitlab_helper.mr_json["source_branch"]
|
277
|
+
end
|
278
|
+
|
279
|
+
# @return [String] +""+ when not in the CI context, and the MR target branch otherwise.
|
280
|
+
def mr_target_branch
|
281
|
+
return "" unless ci?
|
282
|
+
|
283
|
+
gitlab_helper.mr_json["target_branch"]
|
284
|
+
end
|
285
|
+
|
286
|
+
# @return [Boolean] whether a MR is a Draft or not.
|
287
|
+
def draft_mr?
|
288
|
+
Gitlab::Dangerfiles::TitleLinting.has_draft_flag?(mr_title)
|
289
|
+
end
|
290
|
+
|
291
|
+
# @return [Boolean] whether a MR is opened in the security mirror or not.
|
292
|
+
def security_mr?
|
293
|
+
mr_web_url.include?("/gitlab-org/security/")
|
294
|
+
end
|
295
|
+
|
296
|
+
# @return [Boolean] whether a MR title includes "cherry-pick" or not.
|
297
|
+
def cherry_pick_mr?
|
298
|
+
Gitlab::Dangerfiles::TitleLinting.has_cherry_pick_flag?(mr_title)
|
299
|
+
end
|
300
|
+
|
301
|
+
# @return [Boolean] whether a MR title includes "RUN ALL RSPEC" or not.
|
302
|
+
def run_all_rspec_mr?
|
303
|
+
Gitlab::Dangerfiles::TitleLinting.has_run_all_rspec_flag?(mr_title)
|
304
|
+
end
|
305
|
+
|
306
|
+
# @return [Boolean] whether a MR title includes "RUN AS-IF-FOSS" or not.
|
307
|
+
def run_as_if_foss_mr?
|
308
|
+
Gitlab::Dangerfiles::TitleLinting.has_run_as_if_foss_flag?(mr_title)
|
309
|
+
end
|
310
|
+
|
311
|
+
# @return [Boolean] whether a MR targets a stable branch or not.
|
312
|
+
def stable_branch?
|
313
|
+
/\A\d+-\d+-stable-ee/i.match?(mr_target_branch)
|
314
|
+
end
|
315
|
+
|
316
|
+
# Whether a MR has any database-scoped labels (i.e. +"database::*"+) set or not.
|
317
|
+
#
|
318
|
+
# @return [Boolean]
|
319
|
+
def has_database_scoped_labels?
|
320
|
+
mr_labels.any? { |label| label.start_with?("database::") }
|
321
|
+
end
|
322
|
+
|
323
|
+
# @return [Boolean] whether a MR has any CI-related changes (i.e. +".gitlab-ci.yml"+ or +".gitlab/ci/*"+) or not.
|
324
|
+
def has_ci_changes?
|
325
|
+
changed_files(%r{\A(\.gitlab-ci\.yml|\.gitlab/ci/)}).any?
|
326
|
+
end
|
327
|
+
|
328
|
+
# @param labels [Array<String>] An array of labels.
|
329
|
+
#
|
330
|
+
# @return [Boolean] whether a MR has the given +labels+ set or not.
|
331
|
+
def mr_has_labels?(*labels)
|
332
|
+
labels = labels.flatten.uniq
|
333
|
+
|
334
|
+
(labels & mr_labels) == labels
|
335
|
+
end
|
336
|
+
|
337
|
+
# @param labels [Array<String>] An array of labels.
|
338
|
+
# @param sep [String] A separator.
|
339
|
+
#
|
340
|
+
# @example
|
341
|
+
# labels_list(["foo", "bar baz"], sep: "; ")
|
342
|
+
# # => '~"foo"; ~"bar baz"'
|
343
|
+
#
|
344
|
+
# @return [String] the list of +labels+ ready for being used in a Markdown comment, separated by +sep+.
|
345
|
+
def labels_list(labels, sep: ", ")
|
346
|
+
labels.map { |label| %Q{~"#{label}"} }.join(sep)
|
347
|
+
end
|
348
|
+
|
349
|
+
# @deprecated Use {#quick_action_label} instead.
|
350
|
+
def prepare_labels_for_mr(labels)
|
351
|
+
quick_action_label(labels)
|
352
|
+
end
|
353
|
+
|
354
|
+
# @param labels [Array<String>] An array of labels.
|
355
|
+
#
|
356
|
+
# @example
|
357
|
+
# quick_action_label(["foo", "bar baz"])
|
358
|
+
# # => '/label ~"foo" ~"bar baz"'
|
359
|
+
#
|
360
|
+
# @return [String] a quick action to set the +given+ labels. Returns +""+ if +labels+ is empty.
|
361
|
+
def quick_action_label(labels)
|
362
|
+
return "" unless labels.any?
|
363
|
+
|
364
|
+
"/label #{labels_list(labels, sep: " ")}"
|
365
|
+
end
|
366
|
+
|
367
|
+
# @param regex [Regexp] A Regexp to match against.
|
368
|
+
#
|
369
|
+
# @return [Array<String>] changed files matching the given +regex+.
|
370
|
+
def changed_files(regex)
|
371
|
+
all_changed_files.grep(regex)
|
372
|
+
end
|
373
|
+
|
374
|
+
# @return [Array<String>] the group labels (i.e. +"group::*"+) set on the MR.
|
375
|
+
def group_label
|
376
|
+
mr_labels.find { |label| label.start_with?("group::") }
|
377
|
+
end
|
378
|
+
|
379
|
+
private
|
380
|
+
|
381
|
+
# @return [Danger::RequestSources::GitLab, nil] the +gitlab+ helper, or +nil+ when it's not available.
|
382
|
+
def gitlab_helper
|
383
|
+
# Unfortunately the following does not work:
|
384
|
+
# - respond_to?(:gitlab)
|
385
|
+
# - respond_to?(:gitlab, true)
|
386
|
+
gitlab
|
387
|
+
rescue NoMethodError
|
388
|
+
nil
|
389
|
+
end
|
390
|
+
|
391
|
+
# @param filename [String] A filename for which we want the diff.
|
392
|
+
#
|
393
|
+
# @return [String] the raw diff as a string for the given +filename+.
|
394
|
+
def diff_for_file(filename)
|
395
|
+
if changes_from_api
|
396
|
+
changes_hash = changes_from_api.find { |file| file["new_path"] == filename }
|
397
|
+
changes_hash["diff"] if changes_hash
|
398
|
+
else
|
399
|
+
git.diff_for_file(filename)&.patch
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
# Fetches MR changes from the API instead of Git (default).
|
404
|
+
#
|
405
|
+
# @return [Array<Hash>, nil]
|
406
|
+
def changes_from_api
|
407
|
+
return nil unless ci?
|
408
|
+
return nil if defined?(@force_changes_from_git)
|
409
|
+
|
410
|
+
@changes_from_api ||= gitlab_helper.mr_changes.to_h["changes"]
|
411
|
+
rescue
|
412
|
+
# Fallback to the Git strategy in any case
|
413
|
+
@force_changes_from_git = true
|
414
|
+
nil
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end
|