gitlab-dangerfiles 0.8.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -1,9 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
4
|
-
require_relative "
|
5
|
-
require_relative "
|
6
|
-
require_relative "../gitlab/dangerfiles/weightage/reviewers"
|
3
|
+
require_relative "../../gitlab/dangerfiles/teammate"
|
4
|
+
require_relative "../../gitlab/dangerfiles/weightage/maintainers"
|
5
|
+
require_relative "../../gitlab/dangerfiles/weightage/reviewers"
|
7
6
|
|
8
7
|
module Danger
|
9
8
|
# Common helper functions for our danger scripts. See Danger::Helper
|
@@ -17,16 +16,24 @@ module Danger
|
|
17
16
|
}.freeze
|
18
17
|
|
19
18
|
Spin = Struct.new(:category, :reviewer, :maintainer, :optional_role, :timezone_experiment)
|
19
|
+
HTTPError = Class.new(StandardError)
|
20
20
|
|
21
|
+
# Finds the +Gitlab::Dangerfiles::Teammate+ object whose username matches the MR author username.
|
22
|
+
#
|
23
|
+
# @return [Gitlab::Dangerfiles::Teammate]
|
21
24
|
def team_mr_author
|
22
|
-
|
25
|
+
company_members.find { |person| person.username == helper.mr_author }
|
23
26
|
end
|
24
27
|
|
25
28
|
# Assigns GitLab team members to be reviewer and maintainer
|
26
|
-
# for
|
29
|
+
# for the given +categories+.
|
30
|
+
#
|
31
|
+
# @param project [String] A project path.
|
32
|
+
# @param categories [Array<Symbol>] An array of categories symbols.
|
33
|
+
# @param timezone_experiment [Boolean] Whether to select reviewers based in timezone or not.
|
27
34
|
#
|
28
35
|
# @return [Array<Spin>]
|
29
|
-
def spin(project, categories, timezone_experiment: false)
|
36
|
+
def spin(project, categories = [nil], timezone_experiment: false)
|
30
37
|
spins = categories.sort.map do |category|
|
31
38
|
including_timezone = INCLUDE_TIMEZONE_FOR_CATEGORY.fetch(category, timezone_experiment)
|
32
39
|
|
@@ -34,6 +41,7 @@ module Danger
|
|
34
41
|
end
|
35
42
|
|
36
43
|
backend_spin = spins.find { |spin| spin.category == :backend }
|
44
|
+
frontend_spin = spins.find { |spin| spin.category == :frontend }
|
37
45
|
|
38
46
|
spins.each do |spin|
|
39
47
|
including_timezone = INCLUDE_TIMEZONE_FOR_CATEGORY.fetch(spin.category, timezone_experiment)
|
@@ -60,81 +68,37 @@ module Danger
|
|
60
68
|
# Fetch an already picked backend maintainer, or pick one otherwise
|
61
69
|
spin.maintainer = backend_spin&.maintainer || spin_for_category(project, :backend, timezone_experiment: including_timezone).maintainer
|
62
70
|
end
|
71
|
+
when :product_intelligence
|
72
|
+
spin.optional_role = :maintainer
|
73
|
+
|
74
|
+
if spin.maintainer.nil?
|
75
|
+
# Fetch an already picked maintainer, or pick one otherwise
|
76
|
+
spin.maintainer = backend_spin&.maintainer || frontend_spin&.maintainer || spin_for_category(project, :backend, timezone_experiment: including_timezone).maintainer
|
77
|
+
end
|
63
78
|
end
|
64
79
|
end
|
65
80
|
|
66
81
|
spins
|
67
82
|
end
|
68
83
|
|
69
|
-
# Looks up the current list of GitLab team members and parses it into a
|
70
|
-
# useful form
|
71
|
-
#
|
72
|
-
# @return [Array<Teammate>]
|
73
|
-
def team
|
74
|
-
@team ||= begin
|
75
|
-
data = helper.http_get_json(ROULETTE_DATA_URL)
|
76
|
-
data.map { |hash| Gitlab::Dangerfiles::Teammate.new(hash) }
|
77
|
-
rescue JSON::ParserError
|
78
|
-
raise "Failed to parse JSON response from #{ROULETTE_DATA_URL}"
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
# Like +team+, but only returns teammates in the current project, based on
|
83
|
-
# project_name.
|
84
|
-
#
|
85
|
-
# @return [Array<Teammate>]
|
86
|
-
def project_team(project_name)
|
87
|
-
team.select { |member| member.in_project?(project_name) }
|
88
|
-
rescue => err
|
89
|
-
warn("Reviewer roulette failed to load team data: #{err.message}")
|
90
|
-
[]
|
91
|
-
end
|
92
|
-
|
93
|
-
# Known issue: If someone is rejected due to OOO, and then becomes not OOO, the
|
94
|
-
# selection will change on next spin
|
95
|
-
# @param [Array<Teammate>] people
|
96
|
-
def spin_for_person(people, random:, timezone_experiment: false)
|
97
|
-
shuffled_people = people.shuffle(random: random)
|
98
|
-
|
99
|
-
if timezone_experiment
|
100
|
-
shuffled_people.find(&method(:valid_person_with_timezone?))
|
101
|
-
else
|
102
|
-
shuffled_people.find(&method(:valid_person?))
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
84
|
private
|
107
85
|
|
108
|
-
# @param [Teammate] person
|
86
|
+
# @param [Gitlab::Dangerfiles::Teammate] person
|
109
87
|
# @return [Boolean]
|
110
88
|
def valid_person?(person)
|
111
89
|
!mr_author?(person) && person.available
|
112
90
|
end
|
113
91
|
|
114
|
-
# @param [Teammate] person
|
92
|
+
# @param [Gitlab::Dangerfiles::Teammate] person
|
115
93
|
# @return [Boolean]
|
116
94
|
def valid_person_with_timezone?(person)
|
117
95
|
valid_person?(person) && HOURS_WHEN_PERSON_CAN_BE_PICKED.cover?(person.local_hour)
|
118
96
|
end
|
119
97
|
|
120
|
-
# @param [Teammate] person
|
98
|
+
# @param [Gitlab::Dangerfiles::Teammate] person
|
121
99
|
# @return [Boolean]
|
122
100
|
def mr_author?(person)
|
123
|
-
person.username ==
|
124
|
-
end
|
125
|
-
|
126
|
-
def mr_author_username
|
127
|
-
helper.gitlab_helper&.mr_author || `whoami`
|
128
|
-
end
|
129
|
-
|
130
|
-
def mr_source_branch
|
131
|
-
return `git rev-parse --abbrev-ref HEAD` unless helper.gitlab_helper&.mr_json
|
132
|
-
|
133
|
-
helper.gitlab_helper.mr_json["source_branch"]
|
134
|
-
end
|
135
|
-
|
136
|
-
def mr_labels
|
137
|
-
helper.gitlab_helper&.mr_labels || []
|
101
|
+
person.username == helper.mr_author
|
138
102
|
end
|
139
103
|
|
140
104
|
def new_random(seed)
|
@@ -143,7 +107,23 @@ module Danger
|
|
143
107
|
|
144
108
|
def spin_role_for_category(team, role, project, category)
|
145
109
|
team.select do |member|
|
146
|
-
member.public_send("#{role}?", project, category, mr_labels) # rubocop:disable GitlabSecurity/PublicSend
|
110
|
+
member.public_send("#{role}?", project, category, helper.mr_labels) # rubocop:disable GitlabSecurity/PublicSend
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Known issue: If someone is rejected due to OOO, and then becomes not OOO, the
|
115
|
+
# selection will change on next spin.
|
116
|
+
#
|
117
|
+
# @param [Array<Gitlab::Dangerfiles::Teammate>] people
|
118
|
+
#
|
119
|
+
# @return [Gitlab::Dangerfiles::Teammate]
|
120
|
+
def spin_for_person(people, random:, timezone_experiment: false)
|
121
|
+
shuffled_people = people.shuffle(random: random)
|
122
|
+
|
123
|
+
if timezone_experiment
|
124
|
+
shuffled_people.find(&method(:valid_person_with_timezone?))
|
125
|
+
else
|
126
|
+
shuffled_people.find(&method(:valid_person?))
|
147
127
|
end
|
148
128
|
end
|
149
129
|
|
@@ -154,7 +134,7 @@ module Danger
|
|
154
134
|
spin_role_for_category(team, role, project, category)
|
155
135
|
end
|
156
136
|
|
157
|
-
random = new_random(mr_source_branch)
|
137
|
+
random = new_random(helper.mr_source_branch)
|
158
138
|
|
159
139
|
weighted_reviewers = Gitlab::Dangerfiles::Weightage::Reviewers.new(reviewers, traintainers).execute
|
160
140
|
weighted_maintainers = Gitlab::Dangerfiles::Weightage::Maintainers.new(maintainers).execute
|
@@ -164,5 +144,44 @@ module Danger
|
|
164
144
|
|
165
145
|
Spin.new(category, reviewer, maintainer, false, timezone_experiment)
|
166
146
|
end
|
147
|
+
|
148
|
+
# Fetches the given +url+ and parse its response as JSON.
|
149
|
+
#
|
150
|
+
# @param [String] url
|
151
|
+
#
|
152
|
+
# @return [Hash, Array]
|
153
|
+
def http_get_json(url)
|
154
|
+
rsp = Net::HTTP.get_response(URI.parse(url))
|
155
|
+
|
156
|
+
unless rsp.is_a?(Net::HTTPOK)
|
157
|
+
raise HTTPError, "Failed to read #{url}: #{rsp.code} #{rsp.message}"
|
158
|
+
end
|
159
|
+
|
160
|
+
JSON.parse(rsp.body)
|
161
|
+
end
|
162
|
+
|
163
|
+
# Looks up the current list of GitLab team members and parses it into a
|
164
|
+
# useful form.
|
165
|
+
#
|
166
|
+
# @return [Array<Gitlab::Dangerfiles::Teammate>]
|
167
|
+
def company_members
|
168
|
+
@company_members ||= begin
|
169
|
+
data = http_get_json(ROULETTE_DATA_URL)
|
170
|
+
data.map { |hash| Gitlab::Dangerfiles::Teammate.new(hash) }
|
171
|
+
rescue JSON::ParserError
|
172
|
+
raise "Failed to parse JSON response from #{ROULETTE_DATA_URL}"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Like +team+, but only returns teammates in the current project, based on
|
177
|
+
# project_name.
|
178
|
+
#
|
179
|
+
# @return [Array<Gitlab::Dangerfiles::Teammate>]
|
180
|
+
def project_team(project_name)
|
181
|
+
company_members.select { |member| member.in_project?(project_name) }
|
182
|
+
rescue => err
|
183
|
+
warn("Reviewer roulette failed to load team data: #{err.message}")
|
184
|
+
[]
|
185
|
+
end
|
167
186
|
end
|
168
187
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
thresholds = helper.config.code_size_thresholds
|
4
|
+
lines_changed = git.lines_of_code
|
5
|
+
|
6
|
+
if lines_changed > thresholds[:high]
|
7
|
+
warn "This merge request is definitely too big (#{lines_changed} lines changed), please split it into multiple merge requests."
|
8
|
+
elsif lines_changed > thresholds[:medium]
|
9
|
+
warn "This merge request is quite big (#{lines_changed} lines changed), please consider splitting it into multiple merge requests."
|
10
|
+
end
|
data/lib/gitlab/dangerfiles.rb
CHANGED
@@ -2,8 +2,87 @@ require "gitlab/dangerfiles/version"
|
|
2
2
|
|
3
3
|
module Gitlab
|
4
4
|
module Dangerfiles
|
5
|
-
|
6
|
-
|
5
|
+
RULES_DIR = File.expand_path("../danger/rules", __dir__)
|
6
|
+
EXISTING_RULES = Dir.glob(File.join(RULES_DIR, "*")).each_with_object([]) do |path, memo|
|
7
|
+
if File.directory?(path)
|
8
|
+
memo << File.basename(path)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
LOCAL_RULES = %w[
|
12
|
+
changes_size
|
13
|
+
].freeze
|
14
|
+
CI_ONLY_RULES = %w[
|
15
|
+
].freeze
|
16
|
+
|
17
|
+
# This class provides utility methods to import plugins and dangerfiles easily.
|
18
|
+
class Engine
|
19
|
+
# @param dangerfile [Danger::Dangerfile] A +Danger::Dangerfile+ object.
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# # In your main Dangerfile:
|
23
|
+
# dangerfiles = Gitlab::Dangerfiles::Engine.new(self)
|
24
|
+
#
|
25
|
+
# @return [Gitlab::Dangerfiles::Engine]
|
26
|
+
def initialize(dangerfile)
|
27
|
+
@dangerfile = dangerfile
|
28
|
+
end
|
29
|
+
|
30
|
+
# Import all available plugins.
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# # In your main Dangerfile:
|
34
|
+
# dangerfiles = Gitlab::Dangerfiles::Engine.new(self)
|
35
|
+
#
|
36
|
+
# # Import all plugins
|
37
|
+
# dangerfiles.import_plugins
|
38
|
+
def import_plugins
|
39
|
+
danger_plugin.import_plugin(File.expand_path("../danger/plugins/*.rb", __dir__))
|
40
|
+
end
|
41
|
+
|
42
|
+
# Import available Dangerfiles.
|
43
|
+
#
|
44
|
+
# @param rules [Symbol, Array<String>] Can be either +:all+ (default) to import all rules,
|
45
|
+
# or an array of rules.
|
46
|
+
# Available rules are: +changes_size+.
|
47
|
+
#
|
48
|
+
# @example
|
49
|
+
# # In your main Dangerfile:
|
50
|
+
# dangerfiles = Gitlab::Dangerfiles::Engine.new(self)
|
51
|
+
#
|
52
|
+
# # Import all rules
|
53
|
+
# dangerfiles.import_dangerfiles
|
54
|
+
#
|
55
|
+
# # Or import only a subset of rules
|
56
|
+
# dangerfiles.import_dangerfiles(rules: %w[changes_size])
|
57
|
+
def import_dangerfiles(rules: :all)
|
58
|
+
filtered_rules(rules).each do |rule|
|
59
|
+
danger_plugin.import_dangerfile(path: File.join(RULES_DIR, rule))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
attr_reader :dangerfile
|
66
|
+
|
67
|
+
def allowed_rules
|
68
|
+
return LOCAL_RULES unless helper_plugin.respond_to?(:ci?)
|
69
|
+
|
70
|
+
helper_plugin.ci? ? LOCAL_RULES | CI_ONLY_RULES : LOCAL_RULES
|
71
|
+
end
|
72
|
+
|
73
|
+
def filtered_rules(rules)
|
74
|
+
rules = EXISTING_RULES if rules == :all
|
75
|
+
|
76
|
+
Array(rules).map(&:to_s) & EXISTING_RULES & allowed_rules
|
77
|
+
end
|
78
|
+
|
79
|
+
def danger_plugin
|
80
|
+
@danger_plugin ||= dangerfile.plugins[Danger::DangerfileDangerPlugin]
|
81
|
+
end
|
82
|
+
|
83
|
+
def helper_plugin
|
84
|
+
@helper_plugin ||= dangerfile.plugins[Danger::Helper]
|
85
|
+
end
|
7
86
|
end
|
8
87
|
end
|
9
88
|
end
|
@@ -4,41 +4,63 @@ require_relative "title_linting"
|
|
4
4
|
|
5
5
|
module Gitlab
|
6
6
|
module Dangerfiles
|
7
|
+
# @!attribute file
|
8
|
+
# @return [String] the file name that's changed.
|
9
|
+
# @!attribute change_type
|
10
|
+
# @return [Symbol] the type of change (+:added+, +:modified+, +:deleted+, +:renamed_before+, +:renamed_after+).
|
11
|
+
# @!attribute category
|
12
|
+
# @return [Symbol] the category of the change.
|
13
|
+
# This is defined by consumers of the gem through +helper.changes_by_category+ or +helper.changes+.
|
7
14
|
Change = Struct.new(:file, :change_type, :category)
|
8
15
|
|
9
16
|
class Changes < ::SimpleDelegator
|
17
|
+
# Return an +Gitlab::Dangerfiles::Changes+ object with only the changes for the added files.
|
18
|
+
#
|
19
|
+
# @return [Gitlab::Dangerfiles::Changes]
|
10
20
|
def added
|
11
21
|
select_by_change_type(:added)
|
12
22
|
end
|
13
23
|
|
24
|
+
# @return [Gitlab::Dangerfiles::Changes] the changes for the modified files.
|
14
25
|
def modified
|
15
26
|
select_by_change_type(:modified)
|
16
27
|
end
|
17
28
|
|
29
|
+
# @return [Gitlab::Dangerfiles::Changes] the changes for the deleted files.
|
18
30
|
def deleted
|
19
31
|
select_by_change_type(:deleted)
|
20
32
|
end
|
21
33
|
|
34
|
+
# @return [Gitlab::Dangerfiles::Changes] the changes for the renamed files (before the rename).
|
22
35
|
def renamed_before
|
23
36
|
select_by_change_type(:renamed_before)
|
24
37
|
end
|
25
38
|
|
39
|
+
# @return [Gitlab::Dangerfiles::Changes] the changes for the renamed files (after the rename).
|
26
40
|
def renamed_after
|
27
41
|
select_by_change_type(:renamed_after)
|
28
42
|
end
|
29
43
|
|
44
|
+
# @param category [Symbol] A category of change.
|
45
|
+
#
|
46
|
+
# @return [Boolean] whether there are any change for the given +category+.
|
30
47
|
def has_category?(category)
|
31
48
|
any? { |change| change.category == category }
|
32
49
|
end
|
33
50
|
|
51
|
+
# @param category [Symbol] a category of change.
|
52
|
+
#
|
53
|
+
# @return [Gitlab::Dangerfiles::Changes] changes for the given +category+.
|
34
54
|
def by_category(category)
|
35
55
|
Changes.new(select { |change| change.category == category })
|
36
56
|
end
|
37
57
|
|
58
|
+
# @return [Array<Symbol>] an array of the unique categories of changes.
|
38
59
|
def categories
|
39
60
|
map(&:category).uniq
|
40
61
|
end
|
41
62
|
|
63
|
+
# @return [Array<String>] an array of the changed files.
|
42
64
|
def files
|
43
65
|
map(&:file)
|
44
66
|
end
|
@@ -15,7 +15,7 @@ module Gitlab
|
|
15
15
|
{
|
16
16
|
separator_missing: "The commit subject and body must be separated by a blank line",
|
17
17
|
details_too_many_changes: "Commits that change #{MAX_CHANGED_LINES_IN_COMMIT} or more lines across " \
|
18
|
-
"at least #{MAX_CHANGED_FILES_IN_COMMIT} files
|
18
|
+
"at least #{MAX_CHANGED_FILES_IN_COMMIT} files should describe these changes in the commit body",
|
19
19
|
details_line_too_long: "The commit body should not contain more than #{MAX_LINE_LENGTH} characters per line",
|
20
20
|
message_contains_text_emoji: "Avoid the use of Markdown Emoji such as `:+1:`. These add limited value " \
|
21
21
|
"to the commit message, and are displayed as plain text outside of GitLab",
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gitlab
|
4
|
+
module Dangerfiles
|
5
|
+
class Config
|
6
|
+
# @!attribute code_size_thresholds
|
7
|
+
# @return [{ high: Integer, medium: Integer }] a hash of the form +{ high: 42, medium: 12 }+ where +:high+ is the lines changed threshold which triggers an error, and +:medium+ is the lines changed threshold which triggers a warning. Also, see +DEFAULT_CHANGES_SIZE_THRESHOLDS+ for the format of the hash.
|
8
|
+
attr_accessor :code_size_thresholds
|
9
|
+
|
10
|
+
DEFAULT_CHANGES_SIZE_THRESHOLDS = { high: 2_000, medium: 500 }.freeze
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@code_size_thresholds = DEFAULT_CHANGES_SIZE_THRESHOLDS
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -45,17 +45,63 @@ end
|
|
45
45
|
|
46
46
|
RSpec.shared_context "with dangerfile" do
|
47
47
|
let(:dangerfile) { DangerSpecHelper.testing_dangerfile }
|
48
|
-
let(:added_files) { %w[
|
49
|
-
let(:modified_files) { %w[
|
50
|
-
let(:deleted_files) { %w[
|
51
|
-
let(:renamed_before_file) { "renamed_before" }
|
52
|
-
let(:renamed_after_file) { "renamed_after" }
|
48
|
+
let(:added_files) { %w[added-from-git] }
|
49
|
+
let(:modified_files) { %w[modified-from-git] }
|
50
|
+
let(:deleted_files) { %w[deleted-from-git] }
|
51
|
+
let(:renamed_before_file) { "renamed_before-from-git" }
|
52
|
+
let(:renamed_after_file) { "renamed_after-from-git" }
|
53
53
|
let(:renamed_files) { [{ before: renamed_before_file, after: renamed_after_file }] }
|
54
54
|
let(:change_class) { Gitlab::Dangerfiles::Change }
|
55
55
|
let(:changes_class) { Gitlab::Dangerfiles::Changes }
|
56
56
|
let(:changes) { changes_class.new([]) }
|
57
57
|
let(:mr_title) { "Fake Title" }
|
58
58
|
let(:mr_labels) { [] }
|
59
|
+
let(:mr_changes_from_api) do
|
60
|
+
{
|
61
|
+
"changes" => [
|
62
|
+
{
|
63
|
+
"old_path" => "added-from-api",
|
64
|
+
"new_path" => "added-from-api",
|
65
|
+
"a_mode" => "100644",
|
66
|
+
"b_mode" => "100644",
|
67
|
+
"new_file" => true,
|
68
|
+
"renamed_file" => false,
|
69
|
+
"deleted_file" => false,
|
70
|
+
"diff" => "@@ -49,6 +49,14 @@\n- vendor/ruby/\n policy: pull\n \n+.danger-review-cache:\n",
|
71
|
+
},
|
72
|
+
{
|
73
|
+
"old_path" => "modified-from-api",
|
74
|
+
"new_path" => "modified-from-api",
|
75
|
+
"a_mode" => "100644",
|
76
|
+
"b_mode" => "100644",
|
77
|
+
"new_file" => false,
|
78
|
+
"renamed_file" => false,
|
79
|
+
"deleted_file" => false,
|
80
|
+
"diff" => "@@ -49,6 +49,14 @@\n- vendor/ruby/\n policy: pull\n \n+.danger-review-cache:\n",
|
81
|
+
},
|
82
|
+
{
|
83
|
+
"old_path" => "renamed_before-from-api",
|
84
|
+
"new_path" => "renamed_after-from-api",
|
85
|
+
"a_mode" => "100644",
|
86
|
+
"b_mode" => "100644",
|
87
|
+
"new_file" => false,
|
88
|
+
"renamed_file" => true,
|
89
|
+
"deleted_file" => false,
|
90
|
+
"diff" => "@@ -49,6 +49,14 @@\n- vendor/ruby/\n policy: pull\n \n+.danger-review-cache:\n",
|
91
|
+
},
|
92
|
+
{
|
93
|
+
"old_path" => "deleted-from-api",
|
94
|
+
"new_path" => "deleted-from-api",
|
95
|
+
"a_mode" => "100644",
|
96
|
+
"b_mode" => "100644",
|
97
|
+
"new_file" => false,
|
98
|
+
"renamed_file" => false,
|
99
|
+
"deleted_file" => true,
|
100
|
+
"diff" => "@@ -49,6 +49,14 @@\n- vendor/ruby/\n policy: pull\n \n+.danger-review-cache:\n",
|
101
|
+
},
|
102
|
+
],
|
103
|
+
}
|
104
|
+
end
|
59
105
|
|
60
106
|
let(:fake_git) { double("fake-git", added_files: added_files, modified_files: modified_files, deleted_files: deleted_files, renamed_files: renamed_files) }
|
61
107
|
let(:fake_helper) { double("fake-helper", changes: changes, mr_iid: 1234, mr_title: mr_title, mr_labels: mr_labels) }
|