danger 6.3.1 → 8.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/danger/ci_source/bitrise.rb +10 -7
- data/lib/danger/ci_source/github_actions.rb +8 -15
- data/lib/danger/commands/local.rb +3 -0
- data/lib/danger/commands/pr.rb +1 -0
- data/lib/danger/comment_generators/bitbucket_server_inline.md.erb +1 -1
- data/lib/danger/comment_generators/bitbucket_server_message_group.md.erb +12 -0
- data/lib/danger/danger_core/dangerfile.rb +21 -11
- data/lib/danger/danger_core/executor.rb +6 -1
- data/lib/danger/danger_core/message_aggregator.rb +49 -0
- data/lib/danger/danger_core/message_group.rb +68 -0
- data/lib/danger/danger_core/messages/base.rb +56 -0
- data/lib/danger/danger_core/messages/markdown.rb +11 -14
- data/lib/danger/danger_core/messages/violation.rb +18 -13
- data/lib/danger/danger_core/plugins/dangerfile_git_plugin.rb +3 -3
- data/lib/danger/danger_core/plugins/dangerfile_messaging_plugin.rb +3 -3
- data/lib/danger/helpers/comments_helper.rb +28 -3
- data/lib/danger/helpers/emoji_mapper.rb +11 -2
- data/lib/danger/helpers/message_groups_array_helper.rb +31 -0
- data/lib/danger/request_sources/bitbucket_cloud.rb +46 -0
- data/lib/danger/request_sources/bitbucket_cloud_api.rb +19 -7
- data/lib/danger/request_sources/github/github.rb +2 -2
- data/lib/danger/request_sources/gitlab.rb +8 -1
- data/lib/danger/version.rb +1 -1
- metadata +20 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a19704ff72b8d86ac14c19bed3ccd7276c0e0c724200439b5d3a39625409c09
|
4
|
+
data.tar.gz: a4b77b3e810f26f237b2057ca4214f21b6108630036aeb61228d3f2ae2e4f163
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4e2eb7edc403b2ef01c2d84cf2dabacdd79426a903084e9a22d8d3506bf0519721eebfe18cff7415d83ffce992b8cb7a416661d8c2da6880dbe0f2871d19c2a
|
7
|
+
data.tar.gz: 077f053cadf83b885c866329512426e0d8d7e31c6259b59c3e726532d3bddf28dca78c4aa2d760f20bed13ab7f116e4f736a276c7ab5c4793123572229fe4b0d
|
@@ -21,7 +21,7 @@ module Danger
|
|
21
21
|
#
|
22
22
|
# ### bitbucket server and bitrsie
|
23
23
|
#
|
24
|
-
# Danger will read the
|
24
|
+
# Danger will read the environment variable GIT_REPOSITORY_URL to construct the Bitbucket Server API URL
|
25
25
|
# finding the project and repo slug in the GIT_REPOSITORY_URL variable. This GIT_REPOSITORY_URL variable
|
26
26
|
# comes from the App Settings tab for your Bitrsie App. If you are manually setting a repo URL in the
|
27
27
|
# Git Clone Repo step, you may need to set adjust this propery in the settings tab, maybe even fake it.
|
@@ -48,13 +48,16 @@ module Danger
|
|
48
48
|
def initialize(env)
|
49
49
|
self.pull_request_id = env["BITRISE_PULL_REQUEST"]
|
50
50
|
self.repo_url = env["GIT_REPOSITORY_URL"]
|
51
|
-
|
52
|
-
if repo_url.include? ".com/"
|
53
|
-
repo_matches = self.repo_url.match(%r{\.com/(.*)})[1].split(/\.git$|$/)[0]
|
54
|
-
elsif repo_url.include? ".com:"
|
55
|
-
repo_matches = self.repo_url.match(%r{\.com:(.*)})[1].split(/\.git$|$/)[0]
|
56
|
-
end
|
57
51
|
|
52
|
+
matcher_url = self.repo_url
|
53
|
+
|
54
|
+
#If the URL contains https:// as :// leads to inaccurate matching. So we remove it and proceed to match.
|
55
|
+
if repo_url.include? "https://"
|
56
|
+
matcher_url["https://"] = ''
|
57
|
+
end
|
58
|
+
|
59
|
+
repo_matches = matcher_url.match(%r{([\/:])(([^\/]+\/)+[^\/]+?)(\.git$|$)})[2]
|
60
|
+
|
58
61
|
self.repo_slug = repo_matches unless repo_matches.nil?
|
59
62
|
|
60
63
|
end
|
@@ -3,25 +3,18 @@ require "danger/request_sources/github/github"
|
|
3
3
|
module Danger
|
4
4
|
# ### CI Setup
|
5
5
|
#
|
6
|
-
# You can use `danger/danger` Action in your
|
6
|
+
# You can use `danger/danger` Action in your `.github/workflows/xxx.yml`.
|
7
|
+
# And so, you can use GITHUB_TOKEN secret as `DANGER_GITHUB_API_TOKEN` environment variable.
|
7
8
|
#
|
8
9
|
# ```
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
10
|
+
# ...
|
11
|
+
# steps:
|
12
|
+
# - uses: actions/checkout@v1
|
13
|
+
# - uses: danger/danger@master
|
14
|
+
# env:
|
15
|
+
# DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
12
16
|
# ```
|
13
17
|
#
|
14
|
-
# ### Token Setup
|
15
|
-
#
|
16
|
-
# Set DANGER_GITHUB_API_TOKEN to secrets, or you can also use GITHUB_TOKEN.
|
17
|
-
#
|
18
|
-
# ```
|
19
|
-
# action "Danger" {
|
20
|
-
# uses = "danger/danger"
|
21
|
-
# secrets = ["GITHUB_TOKEN"]
|
22
|
-
# }
|
23
|
-
# ```
|
24
|
-
#
|
25
18
|
class GitHubActions < CI
|
26
19
|
def self.validates_as_ci?(env)
|
27
20
|
env.key? "GITHUB_ACTION"
|
@@ -66,12 +66,15 @@ module Danger
|
|
66
66
|
|
67
67
|
private
|
68
68
|
|
69
|
+
# this method is a duplicate of Commands::PR#configure_octokit
|
70
|
+
# - worth a refactor sometime?
|
69
71
|
def configure_octokit(cache_dir)
|
70
72
|
# setup caching for Github calls to hitting the API rate limit too quickly
|
71
73
|
cache_file = File.join(cache_dir, "danger_local_cache")
|
72
74
|
cache = HTTPCache.new(cache_file, clear_cache: @clear_http_cache)
|
73
75
|
Octokit.middleware = Faraday::RackBuilder.new do |builder|
|
74
76
|
builder.use Faraday::HttpCache, store: cache, serializer: Marshal, shared_cache: false
|
77
|
+
builder.use Octokit::Middleware::FollowRedirects
|
75
78
|
builder.use Octokit::Response::RaiseError
|
76
79
|
builder.adapter Faraday.default_adapter
|
77
80
|
end
|
data/lib/danger/commands/pr.rb
CHANGED
@@ -83,6 +83,7 @@ module Danger
|
|
83
83
|
end
|
84
84
|
Octokit.middleware = Faraday::RackBuilder.new do |builder|
|
85
85
|
builder.use Faraday::HttpCache, store: cache, serializer: Marshal, shared_cache: false
|
86
|
+
builder.use Octokit::Middleware::FollowRedirects
|
86
87
|
builder.use Octokit::Response::RaiseError
|
87
88
|
builder.adapter Faraday.default_adapter
|
88
89
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
<%- table[:content].each do |violation| -%>
|
3
3
|
<%= @emoji_mapper.map(table[:emoji]) if table[:emoji] %> <%= violation.message %>
|
4
4
|
<%- end -%>
|
5
|
-
<%- table[:resolved].each do |message| -%>
|
5
|
+
<%- table[:resolved] && table[:resolved].each do |message| -%>
|
6
6
|
<%= @emoji_mapper.map("white_check_mark") %> <%= message %>
|
7
7
|
<%- end -%>
|
8
8
|
<%- end -%>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<%- @message_group.messages.each do |message| -%>
|
2
|
+
<%- next if message.type == :markdown -%>
|
3
|
+
<%= @emoji_mapper.from_type(message.type) -%> <%= message.message %>
|
4
|
+
|
5
|
+
<%- end -%>
|
6
|
+
<%- @resolved.each do |message| -%>
|
7
|
+
<%= @emoji_mapper.map("white_check_mark") %> <%= message %>
|
8
|
+
<%- end -%>
|
9
|
+
|
10
|
+
<%= @message_group.markdowns.map(&:message).join("\n\n") %>
|
11
|
+
|
12
|
+
Generated by :no_entry_sign: [Danger](https://danger.systems/ "generated_by_<%= @danger_id %>")
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "danger/danger_core/dangerfile_dsl"
|
4
4
|
require "danger/danger_core/standard_error"
|
5
|
+
require "danger/danger_core/message_aggregator"
|
5
6
|
|
6
7
|
require "danger/danger_core/plugins/dangerfile_messaging_plugin"
|
7
8
|
require "danger/danger_core/plugins/dangerfile_danger_plugin"
|
@@ -60,7 +61,6 @@ module Danger
|
|
60
61
|
# that the core DSLs have, then starts looking at plugins support.
|
61
62
|
|
62
63
|
# rubocop:disable Style/MethodMissing
|
63
|
-
|
64
64
|
def method_missing(method_sym, *arguments, &_block)
|
65
65
|
@core_plugins.each do |plugin|
|
66
66
|
if plugin.public_methods(false).include?(method_sym)
|
@@ -243,16 +243,26 @@ module Danger
|
|
243
243
|
|
244
244
|
def post_results(danger_id, new_comment, remove_previous_comments)
|
245
245
|
violations = violation_report
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
246
|
+
report = {
|
247
|
+
warnings: violations[:warnings].uniq,
|
248
|
+
errors: violations[:errors].uniq,
|
249
|
+
messages: violations[:messages].uniq,
|
250
|
+
markdowns: status_report[:markdowns].uniq,
|
251
|
+
danger_id: danger_id
|
252
|
+
}
|
253
|
+
|
254
|
+
if env.request_source.respond_to?(:update_pr_by_line!) && ENV["DANGER_MESSAGE_AGGREGATION"]
|
255
|
+
env.request_source.update_pr_by_line!(message_groups: MessageAggregator.aggregate(**report),
|
256
|
+
new_comment: new_comment,
|
257
|
+
remove_previous_comments: remove_previous_comments,
|
258
|
+
danger_id: report[:danger_id])
|
259
|
+
else
|
260
|
+
env.request_source.update_pull_request!(
|
261
|
+
**report,
|
262
|
+
new_comment: new_comment,
|
263
|
+
remove_previous_comments: remove_previous_comments
|
264
|
+
)
|
265
|
+
end
|
256
266
|
end
|
257
267
|
|
258
268
|
def setup_for_running(base_branch, head_branch)
|
@@ -63,7 +63,7 @@ module Danger
|
|
63
63
|
unless EnvironmentManager.pr?(system_env)
|
64
64
|
ci_name = EnvironmentManager.local_ci_source(system_env).name.split("::").last
|
65
65
|
|
66
|
-
msg = "Not a #{ci_name}
|
66
|
+
msg = "Not a #{ci_name} #{commit_request(ci_name)} - skipping `danger` run. "
|
67
67
|
# circle won't run danger properly if the commit is pushed and build runs before the PR exists
|
68
68
|
# https://danger.systems/guides/troubleshooting.html#circle-ci-doesnt-run-my-build-consistently
|
69
69
|
# the best solution is to enable `fail_if_no_pr`, and then re-run the job once the PR is up
|
@@ -83,5 +83,10 @@ module Danger
|
|
83
83
|
def head_branch(user_specified_head_branch)
|
84
84
|
user_specified_head_branch || EnvironmentManager.danger_head_branch
|
85
85
|
end
|
86
|
+
|
87
|
+
def commit_request(ci_name)
|
88
|
+
return "Merge Request" if ci_name == 'GitLabCI'
|
89
|
+
return "Pull Request"
|
90
|
+
end
|
86
91
|
end
|
87
92
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "danger/danger_core/message_group"
|
3
|
+
require "danger/helpers/message_groups_array_helper"
|
4
|
+
|
5
|
+
module Danger
|
6
|
+
class MessageAggregator
|
7
|
+
def self.aggregate(*args)
|
8
|
+
new(*args).aggregate
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(warnings: [],
|
12
|
+
errors: [],
|
13
|
+
messages: [],
|
14
|
+
markdowns: [],
|
15
|
+
danger_id: "danger")
|
16
|
+
@messages = warnings + errors + messages + markdowns
|
17
|
+
@danger_id = danger_id
|
18
|
+
end
|
19
|
+
|
20
|
+
# aggregates the messages into an array of MessageGroups
|
21
|
+
# @return [[MessageGroup]]
|
22
|
+
def aggregate
|
23
|
+
# oookay I took some shortcuts with this one.
|
24
|
+
# first, sort messages by file and line
|
25
|
+
@messages.sort! { |a, b| a.compare_by_file_and_line(b) }
|
26
|
+
|
27
|
+
# now create an initial empty message group
|
28
|
+
first_group = MessageGroup.new(file: nil,
|
29
|
+
line: nil)
|
30
|
+
@message_groups = @messages.reduce([first_group]) do |groups, msg|
|
31
|
+
# We get to take a shortcut because we sorted the messages earlier - only
|
32
|
+
# have to see if we can append msg to the last group in the list
|
33
|
+
if groups.last << msg
|
34
|
+
# we appended it, so return groups unchanged
|
35
|
+
groups
|
36
|
+
else
|
37
|
+
# have to create a new group since msg wasn't appended to the other
|
38
|
+
# group
|
39
|
+
new_group = MessageGroup.new(file: msg.file,
|
40
|
+
line: msg.line)
|
41
|
+
new_group << msg
|
42
|
+
groups << new_group
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
@message_groups.extend(Helpers::MessageGroupsArrayHelper)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Danger
|
4
|
+
class MessageGroup
|
5
|
+
def initialize(file: nil, line: nil)
|
6
|
+
@file = file
|
7
|
+
@line = line
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns whether this `MessageGroup` is for the same line of code as
|
11
|
+
# `other`, taking which file they are in to account.
|
12
|
+
# @param other [MessageGroup, Markdown, Violation]
|
13
|
+
# @return [Boolean] whether this `MessageGroup` is for the same line of code
|
14
|
+
def same_line?(other)
|
15
|
+
other.file == file && other.line == line
|
16
|
+
end
|
17
|
+
|
18
|
+
# Merges two `MessageGroup`s that represent the same line of code
|
19
|
+
# In future, perhaps `MessageGroup` will be able to represent a group of
|
20
|
+
# messages for multiple lines.
|
21
|
+
def merge(other)
|
22
|
+
raise ArgumentError, "Cannot merge with MessageGroup for a different line" unless same_line?(other)
|
23
|
+
|
24
|
+
@messages = (messages + other.messages).uniq
|
25
|
+
end
|
26
|
+
|
27
|
+
# Adds a message to the group.
|
28
|
+
# @param message [Markdown, Violation] the message to add
|
29
|
+
def <<(message)
|
30
|
+
# TODO: insertion sort
|
31
|
+
return nil unless same_line?(message)
|
32
|
+
|
33
|
+
inserted = false
|
34
|
+
messages.each.with_index do |other, idx|
|
35
|
+
if (message <=> other) == -1
|
36
|
+
inserted = true
|
37
|
+
messages.insert(idx, message)
|
38
|
+
break
|
39
|
+
end
|
40
|
+
end
|
41
|
+
messages << message unless inserted
|
42
|
+
messages
|
43
|
+
end
|
44
|
+
|
45
|
+
# The list of messages in this group. This list will be sorted in decreasing
|
46
|
+
# order of severity (error, warning, message, markdown)
|
47
|
+
def messages
|
48
|
+
@messages ||= []
|
49
|
+
end
|
50
|
+
|
51
|
+
attr_reader :file, :line
|
52
|
+
|
53
|
+
# @return a hash of statistics. Currently only :warnings_count and
|
54
|
+
# :errors_count
|
55
|
+
def stats
|
56
|
+
stats = { warnings_count: 0, errors_count: 0 }
|
57
|
+
messages.each do |msg|
|
58
|
+
stats[:warnings_count] += 1 if msg.type == :warning
|
59
|
+
stats[:errors_count] += 1 if msg.type == :error
|
60
|
+
end
|
61
|
+
stats
|
62
|
+
end
|
63
|
+
|
64
|
+
def markdowns
|
65
|
+
messages.select { |x| x.type == :markdown }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Danger
|
2
|
+
class BaseMessage
|
3
|
+
attr_accessor :message, :file, :line, :type
|
4
|
+
|
5
|
+
def initialize(type:, message:, file: nil, line: nil)
|
6
|
+
@type = type
|
7
|
+
@message = message
|
8
|
+
@file = file
|
9
|
+
@line = line
|
10
|
+
end
|
11
|
+
|
12
|
+
def compare_by_file_and_line(other)
|
13
|
+
order = cmp_nils(file, other.file)
|
14
|
+
return order unless order.nil?
|
15
|
+
|
16
|
+
order = file <=> other.file
|
17
|
+
return order unless order.zero?
|
18
|
+
|
19
|
+
order = cmp_nils(line, other.line)
|
20
|
+
return order unless order.nil?
|
21
|
+
|
22
|
+
line <=> other.line
|
23
|
+
end
|
24
|
+
# compares a and b based entirely on whether one or the other is nil
|
25
|
+
# arguments are in the same order as `a <=> b`
|
26
|
+
# nil is sorted earlier - so cmp_nils(nil, 1) => -1
|
27
|
+
#
|
28
|
+
# If neither are nil, rather than returning `a <=> b` which would seem
|
29
|
+
# like the obvious shortcut, `nil` is returned.
|
30
|
+
# This allows us to distinguish between cmp_nils returning 0 for a
|
31
|
+
# comparison of filenames, which means "a comparison on the lines is
|
32
|
+
# meaningless - you cannot have a line number for a nil file - so they
|
33
|
+
# should be sorted the same", and a <=> b returning 0, which means "the
|
34
|
+
# files are the same, so compare on the lines"
|
35
|
+
#
|
36
|
+
# @return 0, 1, -1, or nil
|
37
|
+
def cmp_nils(a, b)
|
38
|
+
if a.nil? && b.nil?
|
39
|
+
0
|
40
|
+
elsif a.nil?
|
41
|
+
-1
|
42
|
+
elsif b.nil?
|
43
|
+
1
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def eql?(other)
|
48
|
+
return self == other
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [Boolean] returns true if is a file or line, false otherwise
|
52
|
+
def inline?
|
53
|
+
file || line
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -1,11 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "danger/danger_core/messages/base"
|
3
|
+
|
1
4
|
module Danger
|
2
|
-
class Markdown
|
3
|
-
attr_accessor :message, :file, :line
|
5
|
+
class Markdown < BaseMessage
|
4
6
|
|
5
7
|
def initialize(message, file = nil, line = nil)
|
6
|
-
|
7
|
-
self.file = file
|
8
|
-
self.line = line
|
8
|
+
super(type: :markdown, message: message, file: file, line: line)
|
9
9
|
end
|
10
10
|
|
11
11
|
def ==(other)
|
@@ -25,15 +25,6 @@ module Danger
|
|
25
25
|
h
|
26
26
|
end
|
27
27
|
|
28
|
-
def eql?(other)
|
29
|
-
return self == other
|
30
|
-
end
|
31
|
-
|
32
|
-
# @return [Boolean] returns true if is a file or line, false otherwise
|
33
|
-
def inline?
|
34
|
-
file || line
|
35
|
-
end
|
36
|
-
|
37
28
|
def to_s
|
38
29
|
extra = []
|
39
30
|
extra << "file: #{file}" unless file
|
@@ -41,5 +32,11 @@ module Danger
|
|
41
32
|
|
42
33
|
"Markdown #{message} { #{extra.join ', '.freeze} }"
|
43
34
|
end
|
35
|
+
|
36
|
+
def <=>(other)
|
37
|
+
return 1 if other.type != :markdown
|
38
|
+
|
39
|
+
compare_by_file_and_line(other)
|
40
|
+
end
|
44
41
|
end
|
45
42
|
end
|
@@ -1,12 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "danger/danger_core/messages/base"
|
4
|
+
|
1
5
|
module Danger
|
2
|
-
class Violation
|
3
|
-
|
6
|
+
class Violation < BaseMessage
|
7
|
+
VALID_TYPES = %I[error warning message].freeze
|
8
|
+
attr_accessor :sticky
|
4
9
|
|
5
|
-
def initialize(message, sticky, file = nil, line = nil)
|
6
|
-
|
10
|
+
def initialize(message, sticky, file = nil, line = nil, type: :warning)
|
11
|
+
raise ArgumentError unless VALID_TYPES.include?(type)
|
12
|
+
|
13
|
+
super(type: type, message: message, file: file, line: line)
|
7
14
|
self.sticky = sticky
|
8
|
-
self.file = file
|
9
|
-
self.line = line
|
10
15
|
end
|
11
16
|
|
12
17
|
def ==(other)
|
@@ -28,13 +33,12 @@ module Danger
|
|
28
33
|
h
|
29
34
|
end
|
30
35
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
36
|
+
def <=>(other)
|
37
|
+
types = VALID_TYPES + [:markdown]
|
38
|
+
order = types.index(type) <=> types.index(other.type)
|
39
|
+
return order unless order.zero?
|
34
40
|
|
35
|
-
|
36
|
-
def inline?
|
37
|
-
file || line
|
41
|
+
compare_by_file_and_line(other)
|
38
42
|
end
|
39
43
|
|
40
44
|
def to_s
|
@@ -42,8 +46,9 @@ module Danger
|
|
42
46
|
extra << "sticky: #{sticky}"
|
43
47
|
extra << "file: #{file}" if file
|
44
48
|
extra << "line: #{line}" if line
|
49
|
+
extra << "type: #{type}"
|
45
50
|
|
46
|
-
"Violation #{message} { #{extra.join ', '
|
51
|
+
"Violation #{message} { #{extra.join ', '} }"
|
47
52
|
end
|
48
53
|
end
|
49
54
|
end
|
@@ -136,14 +136,14 @@ module Danger
|
|
136
136
|
# @return [Hash] with keys `:insertions`, `:deletions` giving line counts, and `:before`, `:after` giving file contents, or nil if the file has no changes or does not exist
|
137
137
|
#
|
138
138
|
def info_for_file(file)
|
139
|
-
return nil unless modified_files.include?(file)
|
139
|
+
return nil unless modified_files.include?(file) || added_files.include?(file) || deleted_files.include?(file)
|
140
140
|
stats = @git.diff.stats[:files][file]
|
141
141
|
diff = @git.diff[file]
|
142
142
|
{
|
143
143
|
insertions: stats[:insertions],
|
144
144
|
deletions: stats[:deletions],
|
145
|
-
before: diff.blob(:src).contents,
|
146
|
-
after: diff.blob(:dst).contents
|
145
|
+
before: added_files.include?(file) || deleted_files.include?(file) ? nil : diff.blob(:src).contents,
|
146
|
+
after: added_files.include?(file) || deleted_files.include?(file) ? nil : diff.blob(:dst).contents
|
147
147
|
}
|
148
148
|
end
|
149
149
|
|
@@ -124,7 +124,7 @@ module Danger
|
|
124
124
|
line = options.fetch(:line, nil)
|
125
125
|
|
126
126
|
messages.flatten.each do |message|
|
127
|
-
@messages << Violation.new(message, sticky, file, line) if message
|
127
|
+
@messages << Violation.new(message, sticky, file, line, type: :message) if message
|
128
128
|
end
|
129
129
|
end
|
130
130
|
|
@@ -149,7 +149,7 @@ module Danger
|
|
149
149
|
|
150
150
|
warnings.flatten.each do |warning|
|
151
151
|
next if should_ignore_violation(warning)
|
152
|
-
@warnings << Violation.new(warning, sticky, file, line) if warning
|
152
|
+
@warnings << Violation.new(warning, sticky, file, line, type: :warning) if warning
|
153
153
|
end
|
154
154
|
end
|
155
155
|
|
@@ -174,7 +174,7 @@ module Danger
|
|
174
174
|
|
175
175
|
failures.flatten.each do |failure|
|
176
176
|
next if should_ignore_violation(failure)
|
177
|
-
@errors << Violation.new(failure, sticky, file, line) if failure
|
177
|
+
@errors << Violation.new(failure, sticky, file, line, type: :error) if failure
|
178
178
|
end
|
179
179
|
end
|
180
180
|
|
@@ -76,7 +76,7 @@ module Danger
|
|
76
76
|
}
|
77
77
|
end
|
78
78
|
|
79
|
-
def apply_template(tables: [], markdowns: [], danger_id: "danger", template: "github")
|
79
|
+
def apply_template(tables: [], markdowns: [], danger_id: "danger", template: "github", request_source: template)
|
80
80
|
require "erb"
|
81
81
|
|
82
82
|
md_template = File.join(Danger.gem_path, "lib/danger/comment_generators/#{template}.md.erb")
|
@@ -86,7 +86,7 @@ module Danger
|
|
86
86
|
@tables = tables
|
87
87
|
@markdowns = markdowns.map(&:message)
|
88
88
|
@danger_id = danger_id
|
89
|
-
@emoji_mapper = EmojiMapper.new(
|
89
|
+
@emoji_mapper = EmojiMapper.new(request_source.sub("_inline",""))
|
90
90
|
|
91
91
|
return ERB.new(File.read(md_template), 0, "-").result(binding)
|
92
92
|
end
|
@@ -104,7 +104,32 @@ module Danger
|
|
104
104
|
)
|
105
105
|
end
|
106
106
|
|
107
|
-
|
107
|
+
# resolved is essentially reserved for future use - eventually we might
|
108
|
+
# have some nice generic resolved-thing going :)
|
109
|
+
def generate_message_group_comment(message_group:,
|
110
|
+
danger_id: "danger",
|
111
|
+
resolved: [],
|
112
|
+
template: "github")
|
113
|
+
# cheating a bit - I don't want to alter the apply_template API
|
114
|
+
# so just sneak around behind its back setting some instance variables
|
115
|
+
# to get them to show up in the template
|
116
|
+
@message_group = message_group
|
117
|
+
@resolved = resolved
|
118
|
+
request_source_name = template.sub("_message_group", "")
|
119
|
+
|
120
|
+
|
121
|
+
apply_template(danger_id: danger_id,
|
122
|
+
markdowns: message_group.markdowns,
|
123
|
+
template: template,
|
124
|
+
request_source: request_source_name)
|
125
|
+
.sub(/\A\n*/, "")
|
126
|
+
end
|
127
|
+
|
128
|
+
def generate_inline_comment_body(emoji,
|
129
|
+
message,
|
130
|
+
danger_id: "danger",
|
131
|
+
resolved: false,
|
132
|
+
template: "github")
|
108
133
|
apply_template(
|
109
134
|
tables: [{ content: [message], resolved: resolved, emoji: emoji }],
|
110
135
|
danger_id: danger_id,
|
@@ -15,16 +15,25 @@ module Danger
|
|
15
15
|
}
|
16
16
|
}.freeze
|
17
17
|
|
18
|
+
TYPE_TO_EMOJI = {
|
19
|
+
error: "no_entry_sign",
|
20
|
+
warning: "warning",
|
21
|
+
message: "book",
|
22
|
+
}.freeze
|
23
|
+
|
18
24
|
def initialize(template)
|
19
|
-
template.sub!('_inline', '')
|
20
25
|
@template = DATA.has_key?(template) ? template : "github"
|
21
26
|
end
|
22
27
|
|
23
28
|
def map(emoji)
|
24
|
-
emoji
|
29
|
+
emoji&.delete! ":"
|
25
30
|
DATA[template][emoji]
|
26
31
|
end
|
27
32
|
|
33
|
+
def from_type(type)
|
34
|
+
map(TYPE_TO_EMOJI[type])
|
35
|
+
end
|
36
|
+
|
28
37
|
private
|
29
38
|
|
30
39
|
attr_reader :template
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Danger
|
2
|
+
module Helpers
|
3
|
+
module MessageGroupsArrayHelper
|
4
|
+
FakeArray = Struct.new(:count) do
|
5
|
+
def empty?
|
6
|
+
count.zero?
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def fake_warnings_array
|
11
|
+
FakeArray.new(counts[:warnings])
|
12
|
+
end
|
13
|
+
|
14
|
+
def fake_errors_array
|
15
|
+
FakeArray.new(counts[:errors])
|
16
|
+
end
|
17
|
+
|
18
|
+
def counts
|
19
|
+
return @counts if @counts
|
20
|
+
|
21
|
+
@counts = { warnings: 0, errors: 0 }
|
22
|
+
each do |message_group, counts|
|
23
|
+
group_stats = message_group.stats
|
24
|
+
@counts[:warnings] += group_stats[:warnings_count]
|
25
|
+
@counts[:errors] += group_stats[:errors_count]
|
26
|
+
end
|
27
|
+
@counts
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "danger/helpers/comments_helper"
|
4
4
|
require "danger/request_sources/bitbucket_cloud_api"
|
5
|
+
require "danger/danger_core/message_group"
|
5
6
|
|
6
7
|
module Danger
|
7
8
|
module RequestSources
|
@@ -12,6 +13,7 @@ module Danger
|
|
12
13
|
def self.env_vars
|
13
14
|
[
|
14
15
|
"DANGER_BITBUCKETCLOUD_USERNAME",
|
16
|
+
"DANGER_BITBUCKETCLOUD_UUID",
|
15
17
|
"DANGER_BITBUCKETCLOUD_PASSWORD"
|
16
18
|
]
|
17
19
|
end
|
@@ -90,6 +92,50 @@ module Danger
|
|
90
92
|
@api.post_comment(comment)
|
91
93
|
end
|
92
94
|
|
95
|
+
def update_pr_by_line!(message_groups:,
|
96
|
+
danger_id: "danger",
|
97
|
+
new_comment: false,
|
98
|
+
remove_previous_comments: false)
|
99
|
+
if !new_comment || remove_previous_comments
|
100
|
+
delete_old_comments(danger_id: danger_id)
|
101
|
+
end
|
102
|
+
|
103
|
+
summary_body = generate_description(warnings: message_groups.fake_warnings_array,
|
104
|
+
errors: message_groups.fake_errors_array,
|
105
|
+
template: "bitbucket_server")
|
106
|
+
summary_body += "\n\n"
|
107
|
+
|
108
|
+
|
109
|
+
# this isn't the most elegant thing in the world, but we need the group
|
110
|
+
# with file: nil, line: nil so we can combine its info in with the
|
111
|
+
# summary_body
|
112
|
+
summary_group = message_groups.first
|
113
|
+
if summary_group && summary_group.file.nil? && summary_group.line.nil?
|
114
|
+
# remove summary_group from message_groups so it doesn't get a
|
115
|
+
# duplicate comment posted in the message_groups loop below
|
116
|
+
message_groups.shift
|
117
|
+
else
|
118
|
+
summary_group = MessageGroup.new(file: nil, line: nil)
|
119
|
+
end
|
120
|
+
|
121
|
+
summary_body += generate_message_group_comment(
|
122
|
+
message_group: summary_group,
|
123
|
+
danger_id: danger_id,
|
124
|
+
template: "bitbucket_server_message_group"
|
125
|
+
)
|
126
|
+
|
127
|
+
@api.post_comment(summary_body)
|
128
|
+
|
129
|
+
message_groups.each do |message_group|
|
130
|
+
body = generate_message_group_comment(message_group: message_group,
|
131
|
+
danger_id: danger_id,
|
132
|
+
template: "bitbucket_server_message_group")
|
133
|
+
@api.post_comment(body,
|
134
|
+
file: message_group.file,
|
135
|
+
line: message_group.line)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
93
139
|
def update_inline_comments_for_kind!(kind, messages, danger_id: "danger")
|
94
140
|
emoji = { warnings: "warning", errors: "no_entry_sign", messages: "book" }[kind]
|
95
141
|
|
@@ -6,8 +6,10 @@ module Danger
|
|
6
6
|
module RequestSources
|
7
7
|
class BitbucketCloudAPI
|
8
8
|
attr_accessor :host, :project, :slug, :access_token, :pull_request_id
|
9
|
+
attr_reader :my_uuid
|
9
10
|
|
10
11
|
def initialize(repo_slug, pull_request_id, branch_name, environment)
|
12
|
+
initialize_my_uuid(environment["DANGER_BITBUCKETCLOUD_UUID"])
|
11
13
|
@username = environment["DANGER_BITBUCKETCLOUD_USERNAME"]
|
12
14
|
@password = environment["DANGER_BITBUCKETCLOUD_PASSWORD"]
|
13
15
|
self.project, self.slug = repo_slug.split("/")
|
@@ -16,6 +18,17 @@ module Danger
|
|
16
18
|
self.host = "https://bitbucket.org/"
|
17
19
|
end
|
18
20
|
|
21
|
+
def initialize_my_uuid(uuid)
|
22
|
+
return if uuid.nil?
|
23
|
+
return @my_uuid = uuid if uuid.empty?
|
24
|
+
|
25
|
+
if uuid.start_with?("{") && uuid.end_with?("}")
|
26
|
+
@my_uuid = uuid
|
27
|
+
else
|
28
|
+
@my_uuid = "{#{uuid}}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
19
32
|
def inspect
|
20
33
|
inspected = super
|
21
34
|
|
@@ -27,12 +40,9 @@ module Danger
|
|
27
40
|
end
|
28
41
|
|
29
42
|
def credentials_given?
|
30
|
-
@
|
31
|
-
|
32
|
-
|
33
|
-
def my_uuid
|
34
|
-
uri = URI("https://api.bitbucket.org/2.0/users/#{@username}")
|
35
|
-
@my_uuid ||= fetch_json(uri)[:uuid]
|
43
|
+
@my_uuid && !@my_uuid.empty? &&
|
44
|
+
@username && !@username.empty? &&
|
45
|
+
@password && !@password.empty?
|
36
46
|
end
|
37
47
|
|
38
48
|
def pull_request(*)
|
@@ -157,7 +167,9 @@ module Danger
|
|
157
167
|
end
|
158
168
|
|
159
169
|
def credentials_not_available
|
160
|
-
"Credentials not available. Provide DANGER_BITBUCKETCLOUD_USERNAME
|
170
|
+
"Credentials not available. Provide DANGER_BITBUCKETCLOUD_USERNAME, " \
|
171
|
+
"DANGER_BITBUCKETCLOUD_UUID, and DANGER_BITBUCKETCLOUD_PASSWORD " \
|
172
|
+
"as environment variables."
|
161
173
|
end
|
162
174
|
|
163
175
|
def error_fetching_json(url, status_code)
|
@@ -170,7 +170,7 @@ module Danger
|
|
170
170
|
rest_inline_violations = submit_inline_comments!({
|
171
171
|
danger_id: danger_id,
|
172
172
|
previous_violations: previous_violations
|
173
|
-
}.merge(inline_violations))
|
173
|
+
}.merge(**inline_violations))
|
174
174
|
|
175
175
|
main_violations = merge_violations(
|
176
176
|
regular_violations, rest_inline_violations
|
@@ -189,7 +189,7 @@ module Danger
|
|
189
189
|
template: "github",
|
190
190
|
danger_id: danger_id,
|
191
191
|
previous_violations: previous_violations
|
192
|
-
}.merge(main_violations))
|
192
|
+
}.merge(**main_violations))
|
193
193
|
|
194
194
|
comment_result =
|
195
195
|
if should_create_new_comment
|
@@ -50,7 +50,11 @@ module Danger
|
|
50
50
|
includes_port = self.host.include? ":"
|
51
51
|
raise "Port number included in `DANGER_GITLAB_HOST`, this will fail with GitLab CI Runners" if includes_port
|
52
52
|
|
53
|
-
super
|
53
|
+
# We don't call super because in some cases the Git remote doesn't match the GitLab instance host.
|
54
|
+
# In Danger::EnvironmentManager#initialize we still check that the request source is #validates_as_api_source?
|
55
|
+
# so that should be sufficient to validate GitLab as request source.
|
56
|
+
# See https://github.com/danger/danger/issues/1231 and https://gitlab.com/gitlab-com/gl-infra/infrastructure/-/issues/10069.
|
57
|
+
true
|
54
58
|
end
|
55
59
|
|
56
60
|
def validates_as_api_source?
|
@@ -302,6 +306,9 @@ module Danger
|
|
302
306
|
def file_url(organisation: nil, repository: nil, branch: nil, path: nil)
|
303
307
|
branch ||= 'master'
|
304
308
|
token = @environment["DANGER_GITLAB_API_TOKEN"]
|
309
|
+
# According to GitLab Repositories API docs path and id(slug) should be encoded.
|
310
|
+
path = URI.encode_www_form_component(path)
|
311
|
+
repository = URI.encode_www_form_component(repository)
|
305
312
|
"#{endpoint}/projects/#{repository}/repository/files/#{path}/raw?ref=#{branch}&private_token=#{token}"
|
306
313
|
end
|
307
314
|
|
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:
|
4
|
+
version: 8.0.1
|
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: 2020-
|
12
|
+
date: 2020-06-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: claide
|
@@ -45,14 +45,14 @@ dependencies:
|
|
45
45
|
requirements:
|
46
46
|
- - "~>"
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version: '1.
|
48
|
+
version: '1.7'
|
49
49
|
type: :runtime
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
53
|
- - "~>"
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version: '1.
|
55
|
+
version: '1.7'
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
57
|
name: colored2
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
@@ -71,16 +71,22 @@ dependencies:
|
|
71
71
|
name: faraday
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
73
73
|
requirements:
|
74
|
-
- - "
|
74
|
+
- - ">="
|
75
75
|
- !ruby/object:Gem::Version
|
76
|
-
version:
|
76
|
+
version: 0.9.0
|
77
|
+
- - "<"
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '2.0'
|
77
80
|
type: :runtime
|
78
81
|
prerelease: false
|
79
82
|
version_requirements: !ruby/object:Gem::Requirement
|
80
83
|
requirements:
|
81
|
-
- - "
|
84
|
+
- - ">="
|
82
85
|
- !ruby/object:Gem::Version
|
83
|
-
version:
|
86
|
+
version: 0.9.0
|
87
|
+
- - "<"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.0'
|
84
90
|
- !ruby/object:Gem::Dependency
|
85
91
|
name: faraday-http-cache
|
86
92
|
requirement: !ruby/object:Gem::Requirement
|
@@ -250,6 +256,7 @@ files:
|
|
250
256
|
- lib/danger/commands/systems.rb
|
251
257
|
- lib/danger/comment_generators/bitbucket_server.md.erb
|
252
258
|
- lib/danger/comment_generators/bitbucket_server_inline.md.erb
|
259
|
+
- lib/danger/comment_generators/bitbucket_server_message_group.md.erb
|
253
260
|
- lib/danger/comment_generators/github.md.erb
|
254
261
|
- lib/danger/comment_generators/github_inline.md.erb
|
255
262
|
- lib/danger/comment_generators/gitlab.md.erb
|
@@ -262,6 +269,9 @@ files:
|
|
262
269
|
- lib/danger/danger_core/dangerfile_generator.rb
|
263
270
|
- lib/danger/danger_core/environment_manager.rb
|
264
271
|
- lib/danger/danger_core/executor.rb
|
272
|
+
- lib/danger/danger_core/message_aggregator.rb
|
273
|
+
- lib/danger/danger_core/message_group.rb
|
274
|
+
- lib/danger/danger_core/messages/base.rb
|
265
275
|
- lib/danger/danger_core/messages/markdown.rb
|
266
276
|
- lib/danger/danger_core/messages/violation.rb
|
267
277
|
- lib/danger/danger_core/plugins/dangerfile_bitbucket_cloud_plugin.rb
|
@@ -280,6 +290,7 @@ files:
|
|
280
290
|
- lib/danger/helpers/comments_parsing_helper.rb
|
281
291
|
- lib/danger/helpers/emoji_mapper.rb
|
282
292
|
- lib/danger/helpers/find_max_num_violations.rb
|
293
|
+
- lib/danger/helpers/message_groups_array_helper.rb
|
283
294
|
- lib/danger/plugin_support/gems_resolver.rb
|
284
295
|
- lib/danger/plugin_support/plugin.rb
|
285
296
|
- lib/danger/plugin_support/plugin_file_resolver.rb
|
@@ -314,7 +325,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
314
325
|
requirements:
|
315
326
|
- - ">="
|
316
327
|
- !ruby/object:Gem::Version
|
317
|
-
version: 2.
|
328
|
+
version: 2.4.0
|
318
329
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
319
330
|
requirements:
|
320
331
|
- - ">="
|