gitlab-dangerfiles 1.1.1 → 2.1.3
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 +1 -0
- data/.gitlab-ci.yml +36 -1
- data/.yardopts +5 -0
- data/Gemfile +1 -0
- data/LICENSE.txt +1 -1
- data/README.md +57 -6
- data/gitlab-dangerfiles.gemspec +3 -3
- data/lib/danger/plugins/helper.rb +190 -88
- data/lib/danger/plugins/roulette.rb +75 -43
- data/lib/danger/rules/changes_size/Dangerfile +10 -0
- data/lib/danger/rules/commit_messages/Dangerfile +138 -0
- data/lib/gitlab/dangerfiles.rb +82 -2
- data/lib/gitlab/dangerfiles/changes.rb +22 -0
- data/lib/gitlab/dangerfiles/commit_linter.rb +7 -3
- data/lib/gitlab/dangerfiles/config.rb +23 -0
- data/lib/gitlab/dangerfiles/emoji_checker.rb +1 -0
- data/lib/gitlab/dangerfiles/teammate.rb +0 -5
- data/lib/gitlab/dangerfiles/title_linting.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 +10 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d49d453f0fec847768490aa8c5f7d5790620f588f2c1afb7c2d9d707ae2155b9
|
4
|
+
data.tar.gz: 4af3ce84ee6a755477b0f9dc2f97ba43349f3fcbbdf6494a754ccb940345b6e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e1b2d019c5d1f83c1f01f1dfb4001c02fe8634feade3016cd7b7b479abb27aa4d5c409b45b37928fd379822d9a138c9efb3504fe0c8f3c5e23f2f211771d82c3
|
7
|
+
data.tar.gz: acd1dcd443b2ee129f0db7a13b4946bb373e164ef1e52c06b1b1a91e9256124304375d09c4dcaa26f51b667886c1345e3c55f36653f4a5be146dc7d2c900e448
|
data/.gitignore
CHANGED
data/.gitlab-ci.yml
CHANGED
@@ -11,7 +11,7 @@ workflow:
|
|
11
11
|
# For tags, create a pipeline.
|
12
12
|
- if: '$CI_COMMIT_TAG'
|
13
13
|
|
14
|
-
default:
|
14
|
+
.default:
|
15
15
|
image: ruby:2.7
|
16
16
|
tags:
|
17
17
|
- gitlab-org
|
@@ -29,15 +29,50 @@ default:
|
|
29
29
|
policy: pull
|
30
30
|
|
31
31
|
test:rspec:
|
32
|
+
extends: .default
|
32
33
|
stage: test
|
33
34
|
script:
|
34
35
|
- bundle exec rspec
|
35
36
|
|
36
37
|
test:rufo:
|
38
|
+
extends: .default
|
37
39
|
stage: test
|
38
40
|
script:
|
39
41
|
- bundle exec rufo --check .
|
40
42
|
|
41
43
|
include:
|
44
|
+
- template: Security/Dependency-Scanning.gitlab-ci.yml
|
45
|
+
- template: Security/License-Scanning.gitlab-ci.yml
|
46
|
+
- template: Security/SAST.gitlab-ci.yml
|
47
|
+
- template: Security/Secret-Detection.gitlab-ci.yml
|
42
48
|
- project: 'gitlab-org/quality/pipeline-common'
|
43
49
|
file: '/ci/gem-release.yml'
|
50
|
+
|
51
|
+
# run security jobs on MRs
|
52
|
+
# see: https://gitlab.com/gitlab-org/gitlab/-/issues/218444#note_478761991
|
53
|
+
|
54
|
+
brakeman-sast:
|
55
|
+
rules:
|
56
|
+
- if: '$CI_MERGE_REQUEST_IID'
|
57
|
+
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
58
|
+
|
59
|
+
gemnasium-dependency_scanning:
|
60
|
+
rules:
|
61
|
+
- if: '$CI_MERGE_REQUEST_IID'
|
62
|
+
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
63
|
+
|
64
|
+
bundler-audit-dependency_scanning:
|
65
|
+
rules:
|
66
|
+
- if: '$CI_MERGE_REQUEST_IID'
|
67
|
+
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
68
|
+
|
69
|
+
license_scanning:
|
70
|
+
rules:
|
71
|
+
- if: '$CI_MERGE_REQUEST_IID'
|
72
|
+
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
73
|
+
|
74
|
+
secret_detection:
|
75
|
+
rules:
|
76
|
+
- if: '$CI_MERGE_REQUEST_IID'
|
77
|
+
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
78
|
+
|
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,68 @@ 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.
|
66
|
+
|
67
|
+
#### `commit_messages`
|
68
|
+
|
69
|
+
##### Available configurations
|
70
|
+
|
71
|
+
- `max_commits_count`: The maximum number of allowed non-squashed/non-fixup commits for a given MR.
|
72
|
+
A warning is triggered if the MR has more commits.
|
73
|
+
|
74
|
+
## Documentation
|
75
|
+
|
76
|
+
Latest documentation can be found at <https://www.rubydoc.info/gems/gitlab-dangerfiles>.
|
26
77
|
|
27
78
|
## Development
|
28
79
|
|
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.
|
@@ -4,6 +4,7 @@ require "net/http"
|
|
4
4
|
require "json"
|
5
5
|
|
6
6
|
require_relative "../../gitlab/dangerfiles/changes"
|
7
|
+
require_relative "../../gitlab/dangerfiles/config"
|
7
8
|
require_relative "../../gitlab/dangerfiles/teammate"
|
8
9
|
require_relative "../../gitlab/dangerfiles/title_linting"
|
9
10
|
|
@@ -11,7 +12,6 @@ module Danger
|
|
11
12
|
# Common helper functions for our danger scripts.
|
12
13
|
class Helper < Danger::Plugin
|
13
14
|
RELEASE_TOOLS_BOT = "gitlab-release-tools-bot"
|
14
|
-
DRAFT_REGEX = /\A*#{Regexp.union(/(?i)(\[WIP\]\s*|WIP:\s*|WIP$)/, /(?i)(\[draft\]|\(draft\)|draft:|draft\s\-\s|draft$)/)}+\s*/i.freeze
|
15
15
|
CATEGORY_LABELS = {
|
16
16
|
docs: "~documentation", # Docs are reviewed along DevOps stages, so don't need roulette for now.
|
17
17
|
none: "",
|
@@ -22,36 +22,47 @@ module Danger
|
|
22
22
|
product_intelligence: '~"product intelligence"',
|
23
23
|
}.freeze
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
34
42
|
end
|
35
43
|
|
36
|
-
|
37
|
-
|
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
|
38
58
|
end
|
39
59
|
|
60
|
+
# @return [Boolean] whether we're in the CI context or not.
|
40
61
|
def ci?
|
41
62
|
!gitlab_helper.nil?
|
42
63
|
end
|
43
64
|
|
44
|
-
# @
|
45
|
-
def http_get_json(url)
|
46
|
-
rsp = Net::HTTP.get_response(URI.parse(url))
|
47
|
-
|
48
|
-
unless rsp.is_a?(Net::HTTPOK)
|
49
|
-
raise HTTPError, "Failed to read #{url}: #{rsp.code} #{rsp.message}"
|
50
|
-
end
|
51
|
-
|
52
|
-
JSON.parse(rsp.body)
|
53
|
-
end
|
54
|
-
|
65
|
+
# @return [Array<String>] a list of filenames added in this MR.
|
55
66
|
def added_files
|
56
67
|
@added_files ||= if changes_from_api
|
57
68
|
changes_from_api.select { |file| file["new_file"] }.map { |file| file["new_path"] }
|
@@ -60,6 +71,7 @@ module Danger
|
|
60
71
|
end
|
61
72
|
end
|
62
73
|
|
74
|
+
# @return [Array<String>] a list of filenames modifier in this MR.
|
63
75
|
def modified_files
|
64
76
|
@modified_files ||= if changes_from_api
|
65
77
|
changes_from_api.select { |file| !file["new_file"] && !file["deleted_file"] && !file["renamed_file"] }.map { |file| file["new_path"] }
|
@@ -68,6 +80,7 @@ module Danger
|
|
68
80
|
end
|
69
81
|
end
|
70
82
|
|
83
|
+
# @return [Array<String>] a list of filenames renamed in this MR.
|
71
84
|
def renamed_files
|
72
85
|
@renamed_files ||= if changes_from_api
|
73
86
|
changes_from_api.select { |file| file["renamed_file"] }.each_with_object([]) do |file, memo|
|
@@ -78,6 +91,7 @@ module Danger
|
|
78
91
|
end
|
79
92
|
end
|
80
93
|
|
94
|
+
# @return [Array<String>] a list of filenames deleted in this MR.
|
81
95
|
def deleted_files
|
82
96
|
@deleted_files ||= if changes_from_api
|
83
97
|
changes_from_api.select { |file| file["deleted_file"] }.map { |file| file["new_path"] }
|
@@ -86,43 +100,20 @@ module Danger
|
|
86
100
|
end
|
87
101
|
end
|
88
102
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
end
|
97
|
-
|
98
|
-
def changes_from_api
|
99
|
-
return nil unless ci?
|
100
|
-
return nil if defined?(@force_changes_from_git)
|
101
|
-
|
102
|
-
@changes_from_api ||= gitlab_helper.mr_changes.to_h["changes"]
|
103
|
-
rescue
|
104
|
-
# Fallback to the Git strategy in any case
|
105
|
-
@force_changes_from_git = true
|
106
|
-
nil
|
107
|
-
end
|
108
|
-
|
109
|
-
# Returns a list of all files that have been added, modified or renamed.
|
110
|
-
# `modified_files` might contain paths that already have been renamed,
|
111
|
-
# so we need to remove them from the list.
|
112
|
-
#
|
113
|
-
# Considering these changes:
|
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:
|
114
110
|
#
|
115
|
-
#
|
116
|
-
# - D deleted_file.rb
|
117
|
-
# - M modified_file.rb
|
118
|
-
# - R renamed_file_before.rb -> renamed_file_after.rb
|
111
|
+
# #=> ['new_file.rb', 'modified_file.rb', 'renamed_file_after.rb']
|
119
112
|
#
|
120
|
-
# it will return
|
121
|
-
# ```
|
122
|
-
# [ 'new_file.rb', 'modified_file.rb', 'renamed_file_after.rb' ]
|
123
|
-
# ```
|
124
113
|
#
|
125
|
-
# @return [Array<String>]
|
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.
|
126
117
|
def all_changed_files
|
127
118
|
Set.new
|
128
119
|
.merge(added_files)
|
@@ -133,16 +124,19 @@ module Danger
|
|
133
124
|
.sort
|
134
125
|
end
|
135
126
|
|
136
|
-
#
|
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:
|
137
131
|
#
|
138
|
-
#
|
132
|
+
# ["--- a/lib/gitlab/usage_data.rb",
|
133
|
+
# "+++ b/lib/gitlab/usage_data.rb",
|
134
|
+
# "+ # Test change",
|
135
|
+
# "- # Old change"]
|
139
136
|
#
|
140
|
-
# [
|
141
|
-
|
142
|
-
|
143
|
-
# "- # Old change" ]
|
144
|
-
def changed_lines(changed_file)
|
145
|
-
diff = diff_for_file(changed_file)
|
137
|
+
# @return [Array<String>] an array of changed lines in Git diff format.
|
138
|
+
def changed_lines(filename)
|
139
|
+
diff = diff_for_file(filename)
|
146
140
|
return [] unless diff
|
147
141
|
|
148
142
|
diff.split("\n").select { |line| %r{^[+-]}.match?(line) }
|
@@ -152,6 +146,14 @@ module Danger
|
|
152
146
|
gitlab_helper&.mr_author == RELEASE_TOOLS_BOT
|
153
147
|
end
|
154
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.
|
155
157
|
def markdown_list(items)
|
156
158
|
list = items.map { |item| "* `#{item}`" }.join("\n")
|
157
159
|
|
@@ -162,14 +164,24 @@ module Danger
|
|
162
164
|
end
|
163
165
|
end
|
164
166
|
|
165
|
-
# @
|
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.
|
166
173
|
def changes_by_category(categories)
|
167
174
|
all_changed_files.each_with_object(Hash.new { |h, k| h[k] = [] }) do |file, hash|
|
168
175
|
categories_for_file(file, categories).each { |category| hash[category] << file }
|
169
176
|
end
|
170
177
|
end
|
171
178
|
|
172
|
-
# @
|
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.
|
173
185
|
def changes(categories)
|
174
186
|
Gitlab::Dangerfiles::Changes.new([]).tap do |changes|
|
175
187
|
added_files.each do |file|
|
@@ -194,16 +206,19 @@ module Danger
|
|
194
206
|
end
|
195
207
|
end
|
196
208
|
|
197
|
-
#
|
198
|
-
#
|
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+
|
199
213
|
#
|
200
|
-
# @return Array<Symbol>
|
201
|
-
|
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)
|
202
217
|
_, categories = categories.find do |key, _|
|
203
218
|
filename_regex, changes_regex = Array(key)
|
204
219
|
|
205
|
-
found = filename_regex.match?(
|
206
|
-
found &&= changed_lines(
|
220
|
+
found = filename_regex.match?(filename)
|
221
|
+
found &&= changed_lines(filename).any? { |changed_line| changes_regex.match?(changed_line) } if changes_regex
|
207
222
|
|
208
223
|
found
|
209
224
|
end
|
@@ -211,114 +226,201 @@ module Danger
|
|
211
226
|
Array(categories || :unknown)
|
212
227
|
end
|
213
228
|
|
214
|
-
#
|
215
|
-
# a category we know about.
|
229
|
+
# @param category [Symbol] A category.
|
216
230
|
#
|
217
|
-
# @return[String]
|
231
|
+
# @return [String] the GFM for a category label, making its best guess if it's not
|
232
|
+
# a category we know about.
|
218
233
|
def label_for_category(category)
|
219
234
|
CATEGORY_LABELS.fetch(category, "~#{category}")
|
220
235
|
end
|
221
236
|
|
222
|
-
|
223
|
-
usernames.map { |u| Gitlab::Dangerfiles::Teammate.new("username" => u) }
|
224
|
-
end
|
225
|
-
|
237
|
+
# @return [String] +""+ when not in the CI context, and the MR IID as a string otherwise.
|
226
238
|
def mr_iid
|
227
239
|
return "" unless ci?
|
228
240
|
|
229
|
-
gitlab_helper.mr_json["iid"]
|
241
|
+
gitlab_helper.mr_json["iid"].to_s
|
230
242
|
end
|
231
243
|
|
244
|
+
# @return [String] +`whoami`+ when not in the CI context, and the MR author username otherwise.
|
232
245
|
def mr_author
|
233
246
|
return `whoami`.strip unless ci?
|
234
247
|
|
235
248
|
gitlab_helper.mr_author
|
236
249
|
end
|
237
250
|
|
251
|
+
# @return [String] +""+ when not in the CI context, and the MR title otherwise.
|
238
252
|
def mr_title
|
239
253
|
return "" unless ci?
|
240
254
|
|
241
255
|
gitlab_helper.mr_json["title"]
|
242
256
|
end
|
243
257
|
|
258
|
+
# @return [String] +""+ when not in the CI context, and the MR URL otherwise.
|
244
259
|
def mr_web_url
|
245
260
|
return "" unless ci?
|
246
261
|
|
247
262
|
gitlab_helper.mr_json["web_url"]
|
248
263
|
end
|
249
264
|
|
265
|
+
# @return [Array<String>] +[]+ when not in the CI context, and the MR labels otherwise.
|
250
266
|
def mr_labels
|
251
267
|
return [] unless ci?
|
252
268
|
|
253
269
|
gitlab_helper.mr_labels
|
254
270
|
end
|
255
271
|
|
272
|
+
# @return [String] +`git rev-parse --abbrev-ref HEAD`+ when not in the CI context, and the MR source branch otherwise.
|
256
273
|
def mr_source_branch
|
257
274
|
return `git rev-parse --abbrev-ref HEAD`.strip unless ci?
|
258
275
|
|
259
276
|
gitlab_helper.mr_json["source_branch"]
|
260
277
|
end
|
261
278
|
|
279
|
+
# @return [String] +""+ when not in the CI context, and the MR target branch otherwise.
|
262
280
|
def mr_target_branch
|
263
281
|
return "" unless ci?
|
264
282
|
|
265
283
|
gitlab_helper.mr_json["target_branch"]
|
266
284
|
end
|
267
285
|
|
286
|
+
# @return [Boolean] +true+ when not in the CI context, and whether the MR is set to be squashed otherwise.
|
287
|
+
def squash_mr?
|
288
|
+
return true unless ci?
|
289
|
+
|
290
|
+
gitlab.mr_json["squash"]
|
291
|
+
end
|
292
|
+
|
293
|
+
# @return [Boolean] whether a MR is a Draft or not.
|
268
294
|
def draft_mr?
|
269
|
-
|
295
|
+
return false unless ci?
|
296
|
+
|
297
|
+
gitlab.mr_json["work_in_progress"]
|
270
298
|
end
|
271
299
|
|
300
|
+
# @return [Boolean] whether a MR is opened in the security mirror or not.
|
272
301
|
def security_mr?
|
273
302
|
mr_web_url.include?("/gitlab-org/security/")
|
274
303
|
end
|
275
304
|
|
305
|
+
# @return [Boolean] whether a MR title includes "cherry-pick" or not.
|
276
306
|
def cherry_pick_mr?
|
277
307
|
Gitlab::Dangerfiles::TitleLinting.has_cherry_pick_flag?(mr_title)
|
278
308
|
end
|
279
309
|
|
310
|
+
# @return [Boolean] whether a MR title includes "RUN ALL RSPEC" or not.
|
280
311
|
def run_all_rspec_mr?
|
281
312
|
Gitlab::Dangerfiles::TitleLinting.has_run_all_rspec_flag?(mr_title)
|
282
313
|
end
|
283
314
|
|
315
|
+
# @return [Boolean] whether a MR title includes "RUN AS-IF-FOSS" or not.
|
284
316
|
def run_as_if_foss_mr?
|
285
317
|
Gitlab::Dangerfiles::TitleLinting.has_run_as_if_foss_flag?(mr_title)
|
286
318
|
end
|
287
319
|
|
320
|
+
# @return [Boolean] whether a MR targets a stable branch or not.
|
288
321
|
def stable_branch?
|
289
322
|
/\A\d+-\d+-stable-ee/i.match?(mr_target_branch)
|
290
323
|
end
|
291
324
|
|
325
|
+
# Whether a MR has any database-scoped labels (i.e. +"database::*"+) set or not.
|
326
|
+
#
|
327
|
+
# @return [Boolean]
|
328
|
+
def has_database_scoped_labels?
|
329
|
+
mr_labels.any? { |label| label.start_with?("database::") }
|
330
|
+
end
|
331
|
+
|
332
|
+
# @return [Boolean] whether a MR has any CI-related changes (i.e. +".gitlab-ci.yml"+ or +".gitlab/ci/*"+) or not.
|
333
|
+
def has_ci_changes?
|
334
|
+
changed_files(%r{\A(\.gitlab-ci\.yml|\.gitlab/ci/)}).any?
|
335
|
+
end
|
336
|
+
|
337
|
+
# @param labels [Array<String>] An array of labels.
|
338
|
+
#
|
339
|
+
# @return [Boolean] whether a MR has the given +labels+ set or not.
|
292
340
|
def mr_has_labels?(*labels)
|
293
341
|
labels = labels.flatten.uniq
|
294
342
|
|
295
343
|
(labels & mr_labels) == labels
|
296
344
|
end
|
297
345
|
|
346
|
+
# @param labels [Array<String>] An array of labels.
|
347
|
+
# @param sep [String] A separator.
|
348
|
+
#
|
349
|
+
# @example
|
350
|
+
# labels_list(["foo", "bar baz"], sep: "; ")
|
351
|
+
# # => '~"foo"; ~"bar baz"'
|
352
|
+
#
|
353
|
+
# @return [String] the list of +labels+ ready for being used in a Markdown comment, separated by +sep+.
|
298
354
|
def labels_list(labels, sep: ", ")
|
299
355
|
labels.map { |label| %Q{~"#{label}"} }.join(sep)
|
300
356
|
end
|
301
357
|
|
358
|
+
# @deprecated Use {#quick_action_label} instead.
|
302
359
|
def prepare_labels_for_mr(labels)
|
360
|
+
quick_action_label(labels)
|
361
|
+
end
|
362
|
+
|
363
|
+
# @param labels [Array<String>] An array of labels.
|
364
|
+
#
|
365
|
+
# @example
|
366
|
+
# quick_action_label(["foo", "bar baz"])
|
367
|
+
# # => '/label ~"foo" ~"bar baz"'
|
368
|
+
#
|
369
|
+
# @return [String] a quick action to set the +given+ labels. Returns +""+ if +labels+ is empty.
|
370
|
+
def quick_action_label(labels)
|
303
371
|
return "" unless labels.any?
|
304
372
|
|
305
373
|
"/label #{labels_list(labels, sep: " ")}"
|
306
374
|
end
|
307
375
|
|
376
|
+
# @param regex [Regexp] A Regexp to match against.
|
377
|
+
#
|
378
|
+
# @return [Array<String>] changed files matching the given +regex+.
|
308
379
|
def changed_files(regex)
|
309
380
|
all_changed_files.grep(regex)
|
310
381
|
end
|
311
382
|
|
312
|
-
|
313
|
-
|
383
|
+
# @return [Array<String>] the group labels (i.e. +"group::*"+) set on the MR.
|
384
|
+
def group_label
|
385
|
+
mr_labels.find { |label| label.start_with?("group::") }
|
314
386
|
end
|
315
387
|
|
316
|
-
|
317
|
-
|
388
|
+
private
|
389
|
+
|
390
|
+
# @return [Danger::RequestSources::GitLab, nil] the +gitlab+ helper, or +nil+ when it's not available.
|
391
|
+
def gitlab_helper
|
392
|
+
# Unfortunately the following does not work:
|
393
|
+
# - respond_to?(:gitlab)
|
394
|
+
# - respond_to?(:gitlab, true)
|
395
|
+
gitlab
|
396
|
+
rescue NoMethodError
|
397
|
+
nil
|
398
|
+
end
|
399
|
+
|
400
|
+
# @param filename [String] A filename for which we want the diff.
|
401
|
+
#
|
402
|
+
# @return [String] the raw diff as a string for the given +filename+.
|
403
|
+
def diff_for_file(filename)
|
404
|
+
if changes_from_api
|
405
|
+
changes_hash = changes_from_api.find { |file| file["new_path"] == filename }
|
406
|
+
changes_hash["diff"] if changes_hash
|
407
|
+
else
|
408
|
+
git.diff_for_file(filename)&.patch
|
409
|
+
end
|
318
410
|
end
|
319
411
|
|
320
|
-
|
321
|
-
|
412
|
+
# Fetches MR changes from the API instead of Git (default).
|
413
|
+
#
|
414
|
+
# @return [Array<Hash>, nil]
|
415
|
+
def changes_from_api
|
416
|
+
return nil unless ci?
|
417
|
+
return nil if defined?(@force_changes_from_git)
|
418
|
+
|
419
|
+
@changes_from_api ||= gitlab_helper.mr_changes.to_h["changes"]
|
420
|
+
rescue
|
421
|
+
# Fallback to the Git strategy in any case
|
422
|
+
@force_changes_from_git = true
|
423
|
+
nil
|
322
424
|
end
|
323
425
|
end
|
324
426
|
end
|