danger 3.1.1 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/danger.rb +14 -0
- data/lib/danger/ci_source/jenkins.rb +1 -1
- data/lib/danger/ci_source/local_git_repo.rb +4 -0
- data/lib/danger/clients/rubygems_client.rb +14 -0
- data/lib/danger/comment_generators/bitbucket_server.md.erb +20 -0
- data/lib/danger/comment_generators/github_inline.md.erb +26 -0
- data/lib/danger/danger_core/dangerfile.rb +15 -6
- data/lib/danger/danger_core/messages/markdown.rb +28 -0
- data/lib/danger/danger_core/messages/violation.rb +35 -0
- data/lib/danger/danger_core/plugins/dangerfile_bitbucket_server_plugin.rb +182 -0
- data/lib/danger/danger_core/plugins/dangerfile_danger_plugin.rb +61 -6
- data/lib/danger/danger_core/plugins/dangerfile_messaging_plugin.rb +30 -9
- data/lib/danger/danger_core/standard_error.rb +10 -1
- data/lib/danger/helpers/comments_helper.rb +94 -33
- data/lib/danger/helpers/comments_parsing_helper.rb +52 -0
- data/lib/danger/request_source/bitbucket_server.rb +78 -0
- data/lib/danger/request_source/bitbucket_server_api.rb +70 -0
- data/lib/danger/request_source/github.rb +207 -9
- data/lib/danger/scm_source/git_repo.rb +10 -1
- data/lib/danger/version.rb +1 -1
- metadata +13 -4
- data/lib/danger/danger_core/violation.rb +0 -10
@@ -100,16 +100,42 @@ module Danger
|
|
100
100
|
previous_violations = parse_comment(comment)
|
101
101
|
end
|
102
102
|
|
103
|
-
|
103
|
+
main_violations = (warnings + errors + messages + markdowns).reject(&:inline?)
|
104
|
+
if previous_violations.empty? && main_violations.empty?
|
104
105
|
# Just remove the comment, if there's nothing to say.
|
105
106
|
delete_old_comments!(danger_id: danger_id)
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
107
|
+
end
|
108
|
+
|
109
|
+
cmp = proc do |a, b|
|
110
|
+
next -1 unless a.file
|
111
|
+
next 1 unless b.file
|
112
|
+
|
113
|
+
next a.line <=> b.line if a.file == b.file
|
114
|
+
next a.file <=> b.file
|
115
|
+
end
|
116
|
+
|
117
|
+
# Sort to group inline comments by file
|
118
|
+
# We copy because we need to mutate this arrays for inlines
|
119
|
+
comment_warnings = warnings.sort(&cmp)
|
120
|
+
comment_errors = errors.sort(&cmp)
|
121
|
+
comment_messages = messages.sort(&cmp)
|
122
|
+
comment_markdowns = markdowns.sort(&cmp)
|
123
|
+
|
124
|
+
submit_inline_comments!(warnings: comment_warnings,
|
125
|
+
errors: comment_errors,
|
126
|
+
messages: comment_messages,
|
127
|
+
markdowns: comment_markdowns,
|
128
|
+
previous_violations: previous_violations,
|
129
|
+
danger_id: danger_id)
|
130
|
+
|
131
|
+
# If there are still violations to show
|
132
|
+
unless main_violations.empty?
|
133
|
+
body = generate_comment(warnings: comment_warnings,
|
134
|
+
errors: comment_errors,
|
135
|
+
messages: comment_messages,
|
136
|
+
markdowns: comment_markdowns,
|
137
|
+
previous_violations: previous_violations,
|
138
|
+
danger_id: danger_id,
|
113
139
|
template: "github")
|
114
140
|
|
115
141
|
if editable_comments.empty?
|
@@ -124,7 +150,7 @@ module Danger
|
|
124
150
|
# Note: this can terminate the entire process.
|
125
151
|
submit_pull_request_status!(warnings: warnings,
|
126
152
|
errors: errors,
|
127
|
-
details_url: comment_result[
|
153
|
+
details_url: comment_result[:html_url])
|
128
154
|
end
|
129
155
|
|
130
156
|
def submit_pull_request_status!(warnings: [], errors: [], details_url: [])
|
@@ -170,6 +196,178 @@ module Danger
|
|
170
196
|
end
|
171
197
|
end
|
172
198
|
|
199
|
+
def submit_inline_comments!(warnings: [], errors: [], messages: [], markdowns: [], previous_violations: [], danger_id: "danger")
|
200
|
+
# Avoid doing any fetchs if there's no inline comments
|
201
|
+
return if (warnings + errors + messages).select(&:inline?).empty?
|
202
|
+
|
203
|
+
diff_lines = self.pr_diff.lines
|
204
|
+
pr_comments = client.pull_request_comments(ci_source.repo_slug, ci_source.pull_request_id)
|
205
|
+
danger_comments = pr_comments.select { |comment| comment[:body].include?("generated_by_#{danger_id}") }
|
206
|
+
non_danger_comments = pr_comments - danger_comments
|
207
|
+
|
208
|
+
submit_inline_comments_for_kind!("warning", warnings, diff_lines, danger_comments, previous_violations[:warning], danger_id: danger_id)
|
209
|
+
submit_inline_comments_for_kind!("no_entry_sign", errors, diff_lines, danger_comments, previous_violations[:error], danger_id: danger_id)
|
210
|
+
submit_inline_comments_for_kind!("book", messages, diff_lines, danger_comments, previous_violations[:message], danger_id: danger_id)
|
211
|
+
submit_inline_comments_for_kind!(nil, markdowns, diff_lines, danger_comments, [], danger_id: danger_id)
|
212
|
+
|
213
|
+
# submit removes from the array all comments that are still in force
|
214
|
+
# so we strike out all remaining ones
|
215
|
+
danger_comments.each do |comment|
|
216
|
+
violation = violations_from_table(comment[:body]).first
|
217
|
+
if !violation.nil? && violation.sticky
|
218
|
+
body = generate_inline_comment_body("white_check_mark", violation, danger_id: danger_id, resolved: true, template: "github")
|
219
|
+
client.update_pull_request_comment(ci_source.repo_slug, comment[:id], body)
|
220
|
+
else
|
221
|
+
# We remove non-sticky violations that have no replies
|
222
|
+
# Since there's no direct concept of a reply in GH, we simply consider
|
223
|
+
# the existance of non-danger comments in that line as replies
|
224
|
+
replies = non_danger_comments.select do |potential|
|
225
|
+
potential[:path] == comment[:path] &&
|
226
|
+
potential[:position] == comment[:position] &&
|
227
|
+
potential[:commit_id] == comment[:commit_id]
|
228
|
+
end
|
229
|
+
|
230
|
+
client.delete_pull_request_comment(ci_source.repo_slug, comment[:id]) if replies.empty?
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def messages_are_equivalent(m1, m2)
|
236
|
+
blob_regexp = %r{blob/[0-9a-z]+/}
|
237
|
+
m1.file == m2.file && m1.line == m2.line &&
|
238
|
+
m1.message.sub(blob_regexp, "") == m2.message.sub(blob_regexp, "")
|
239
|
+
end
|
240
|
+
|
241
|
+
def submit_inline_comments_for_kind!(emoji, messages, diff_lines, danger_comments, previous_violations, danger_id: "danger")
|
242
|
+
head_ref = pr_json[:head][:sha]
|
243
|
+
previous_violations ||= []
|
244
|
+
is_markdown_content = emoji.nil?
|
245
|
+
|
246
|
+
submit_inline = proc do |m|
|
247
|
+
next false unless m.file && m.line
|
248
|
+
|
249
|
+
position = find_position_in_diff diff_lines, m
|
250
|
+
|
251
|
+
# Keep the change if it's line is not in the diff
|
252
|
+
next false if position.nil?
|
253
|
+
|
254
|
+
# Once we know we're gonna submit it, we format it
|
255
|
+
if is_markdown_content
|
256
|
+
body = generate_inline_markdown_body(m, danger_id: danger_id, template: "github")
|
257
|
+
else
|
258
|
+
# Hide the inline link behind a span
|
259
|
+
m = process_markdown(m, true)
|
260
|
+
body = generate_inline_comment_body(emoji, m, danger_id: danger_id, template: "github")
|
261
|
+
# A comment might be in previous_violations because only now it's part of the unified diff
|
262
|
+
# We remove from the array since it won't have a place in the table anymore
|
263
|
+
previous_violations.reject! { |v| messages_are_equivalent(v, m) }
|
264
|
+
end
|
265
|
+
|
266
|
+
matching_comments = danger_comments.select do |comment_data|
|
267
|
+
if comment_data[:path] == m.file && comment_data[:commit_id] == head_ref && comment_data[:position] == position
|
268
|
+
# Parse it to avoid problems with strikethrough
|
269
|
+
violation = violations_from_table(comment_data[:body]).first
|
270
|
+
if violation
|
271
|
+
messages_are_equivalent(violation, m)
|
272
|
+
else
|
273
|
+
comment_data[:body] == body
|
274
|
+
end
|
275
|
+
else
|
276
|
+
false
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
if matching_comments.empty?
|
281
|
+
client.create_pull_request_comment(ci_source.repo_slug, ci_source.pull_request_id,
|
282
|
+
body, head_ref, m.file, position)
|
283
|
+
else
|
284
|
+
# Remove the surviving comment so we don't strike it out
|
285
|
+
danger_comments.reject! { |c| matching_comments.include? c }
|
286
|
+
|
287
|
+
# Update the comment to remove the strikethrough if present
|
288
|
+
comment = matching_comments.first
|
289
|
+
client.update_pull_request_comment(ci_source.repo_slug, comment[:id], body)
|
290
|
+
end
|
291
|
+
|
292
|
+
# Remove this element from the array
|
293
|
+
next true
|
294
|
+
end
|
295
|
+
|
296
|
+
messages.reject!(&submit_inline)
|
297
|
+
end
|
298
|
+
|
299
|
+
def find_position_in_diff(diff_lines, message)
|
300
|
+
range_header_regexp = /@@ -([0-9]+),([0-9]+) \+(?<start>[0-9]+)(,(?<end>[0-9]+))? @@.*/
|
301
|
+
file_header_regexp = %r{ a/.*}
|
302
|
+
|
303
|
+
pattern = "+++ b/" + message.file + "\n"
|
304
|
+
file_start = diff_lines.index(pattern)
|
305
|
+
|
306
|
+
return nil if file_start.nil?
|
307
|
+
|
308
|
+
position = -1
|
309
|
+
file_line = nil
|
310
|
+
|
311
|
+
diff_lines.drop(file_start).each do |line|
|
312
|
+
match = line.match range_header_regexp
|
313
|
+
|
314
|
+
# file_line is set once we find the hunk the line is in
|
315
|
+
# we need to count how many lines in new file we have
|
316
|
+
# so we do it one by one ignoring the deleted lines
|
317
|
+
if !file_line.nil? && !line.start_with?("-")
|
318
|
+
break if file_line == message.line
|
319
|
+
file_line += 1
|
320
|
+
end
|
321
|
+
|
322
|
+
# We need to count how many diff lines are between us and
|
323
|
+
# the line we're looking for
|
324
|
+
position += 1
|
325
|
+
|
326
|
+
next unless match
|
327
|
+
|
328
|
+
# If we found the start of another file diff, we went too far
|
329
|
+
break if line.match file_header_regexp
|
330
|
+
|
331
|
+
range_start = match[:start].to_i
|
332
|
+
if match[:end]
|
333
|
+
range_end = match[:end].to_i + range_start
|
334
|
+
else
|
335
|
+
range_end = range_start
|
336
|
+
end
|
337
|
+
|
338
|
+
# We are past the line position, just abort
|
339
|
+
break if message.line < range_start
|
340
|
+
next unless message.line >= range_start && message.line <= range_end
|
341
|
+
|
342
|
+
file_line = range_start
|
343
|
+
end
|
344
|
+
|
345
|
+
position unless file_line.nil?
|
346
|
+
end
|
347
|
+
|
348
|
+
# See the tests for examples of data coming in looks like
|
349
|
+
def parse_message_from_row(row)
|
350
|
+
message_regexp = %r{(<(a |span data-)href="https://github.com/#{ci_source.repo_slug}/blob/[0-9a-z]+/(?<file>[^#]+)#L(?<line>[0-9]+)"(>[^<]*</a> - |/>))?(?<message>.*?)}im
|
351
|
+
match = message_regexp.match(row)
|
352
|
+
|
353
|
+
if match[:line]
|
354
|
+
line = match[:line].to_i
|
355
|
+
else
|
356
|
+
line = nil
|
357
|
+
end
|
358
|
+
Violation.new(row, true, match[:file], line)
|
359
|
+
end
|
360
|
+
|
361
|
+
def markdown_link_to_message(message, hide_link)
|
362
|
+
url = "https://github.com/#{ci_source.repo_slug}/blob/#{pr_json[:head][:sha]}/#{message.file}#L#{message.line}"
|
363
|
+
|
364
|
+
if hide_link
|
365
|
+
"<span data-href=\"#{url}\"/>"
|
366
|
+
else
|
367
|
+
"[#{message.file}#L#{message.line}](#{url}) - "
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
173
371
|
# @return [String] The organisation name, is nil if it can't be detected
|
174
372
|
def organisation
|
175
373
|
matched = self.issue_json[:repository_url].match(%r{repos\/(.*)\/})
|
@@ -15,7 +15,10 @@ module Danger
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def exec(string)
|
18
|
-
|
18
|
+
require "open3"
|
19
|
+
Open3.popen2(default_env, "git #{string}") do |_stdin, stdout, _wait_thr|
|
20
|
+
stdout.read.rstrip
|
21
|
+
end
|
19
22
|
end
|
20
23
|
|
21
24
|
def head_commit
|
@@ -25,6 +28,12 @@ module Danger
|
|
25
28
|
def origins
|
26
29
|
exec("remote show origin -n").lines.grep(/Fetch URL/)[0].split(": ", 2)[1].chomp
|
27
30
|
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def default_env
|
35
|
+
{ "LANG" => "en_US.UTF-8" }
|
36
|
+
end
|
28
37
|
end
|
29
38
|
end
|
30
39
|
|
data/lib/danger/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: danger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Orta Therox
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-09-
|
12
|
+
date: 2016-09-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: claide
|
@@ -376,6 +376,7 @@ files:
|
|
376
376
|
- lib/danger/ci_source/teamcity.rb
|
377
377
|
- lib/danger/ci_source/travis.rb
|
378
378
|
- lib/danger/ci_source/xcode_server.rb
|
379
|
+
- lib/danger/clients/rubygems_client.rb
|
379
380
|
- lib/danger/commands/init.rb
|
380
381
|
- lib/danger/commands/init_helpers/interviewer.rb
|
381
382
|
- lib/danger/commands/local.rb
|
@@ -385,7 +386,9 @@ files:
|
|
385
386
|
- lib/danger/commands/plugins/plugin_readme.rb
|
386
387
|
- lib/danger/commands/runner.rb
|
387
388
|
- lib/danger/commands/systems.rb
|
389
|
+
- lib/danger/comment_generators/bitbucket_server.md.erb
|
388
390
|
- lib/danger/comment_generators/github.md.erb
|
391
|
+
- lib/danger/comment_generators/github_inline.md.erb
|
389
392
|
- lib/danger/comment_generators/gitlab.md.erb
|
390
393
|
- lib/danger/core_ext/file_list.rb
|
391
394
|
- lib/danger/core_ext/string.rb
|
@@ -393,19 +396,24 @@ files:
|
|
393
396
|
- lib/danger/danger_core/dangerfile_dsl.rb
|
394
397
|
- lib/danger/danger_core/environment_manager.rb
|
395
398
|
- lib/danger/danger_core/executor.rb
|
399
|
+
- lib/danger/danger_core/messages/markdown.rb
|
400
|
+
- lib/danger/danger_core/messages/violation.rb
|
401
|
+
- lib/danger/danger_core/plugins/dangerfile_bitbucket_server_plugin.rb
|
396
402
|
- lib/danger/danger_core/plugins/dangerfile_danger_plugin.rb
|
397
403
|
- lib/danger/danger_core/plugins/dangerfile_git_plugin.rb
|
398
404
|
- lib/danger/danger_core/plugins/dangerfile_github_plugin.rb
|
399
405
|
- lib/danger/danger_core/plugins/dangerfile_gitlab_plugin.rb
|
400
406
|
- lib/danger/danger_core/plugins/dangerfile_messaging_plugin.rb
|
401
407
|
- lib/danger/danger_core/standard_error.rb
|
402
|
-
- lib/danger/danger_core/violation.rb
|
403
408
|
- lib/danger/helpers/comments_helper.rb
|
409
|
+
- lib/danger/helpers/comments_parsing_helper.rb
|
404
410
|
- lib/danger/plugin_support/plugin.rb
|
405
411
|
- lib/danger/plugin_support/plugin_file_resolver.rb
|
406
412
|
- lib/danger/plugin_support/plugin_linter.rb
|
407
413
|
- lib/danger/plugin_support/plugin_parser.rb
|
408
414
|
- lib/danger/plugin_support/templates/readme_table.html.erb
|
415
|
+
- lib/danger/request_source/bitbucket_server.rb
|
416
|
+
- lib/danger/request_source/bitbucket_server_api.rb
|
409
417
|
- lib/danger/request_source/github.rb
|
410
418
|
- lib/danger/request_source/gitlab.rb
|
411
419
|
- lib/danger/request_source/request_source.rb
|
@@ -431,8 +439,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
431
439
|
version: '0'
|
432
440
|
requirements: []
|
433
441
|
rubyforge_project:
|
434
|
-
rubygems_version: 2.
|
442
|
+
rubygems_version: 2.4.8
|
435
443
|
signing_key:
|
436
444
|
specification_version: 4
|
437
445
|
summary: Like Unit Tests, but for your Team Culture.
|
438
446
|
test_files: []
|
447
|
+
has_rdoc:
|