danger 6.3.1 → 8.0.1
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/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
|
- - ">="
|