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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c582d1189749d620a69040b53f7fc73c581efd772c39ab7931cd7ed886522fe8
4
- data.tar.gz: 59a22185cdd2cd993ceef9f01934e7757fa6f4f4cd4229e76634bca4b13dd5b1
3
+ metadata.gz: 5a19704ff72b8d86ac14c19bed3ccd7276c0e0c724200439b5d3a39625409c09
4
+ data.tar.gz: a4b77b3e810f26f237b2057ca4214f21b6108630036aeb61228d3f2ae2e4f163
5
5
  SHA512:
6
- metadata.gz: 9e6f93dae26ee18d328f10d1287c4dc0127b3199e8b055992795d4440180625010b7ecb5d229241b4d415918846cdecd9d46d214fc51b66ed43c399203ce0490
7
- data.tar.gz: f0ea9446eadaa3705337982b51bf1abd49ca96e9ddecf14b888c69b48cad6ceec7ff5d59cd48f18061d8128c6c12c3cc9be12d8149461eedc8d6388f65e17179
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 environemnt variable GIT_REPOSITORY_URL to construct the Bitbucket Server API URL
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 .github/main.workflow.
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
- # action "Danger" {
10
- # uses = "danger/danger"
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
@@ -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
- env.request_source.update_pull_request!(
248
- warnings: violations[:warnings].uniq,
249
- errors: violations[:errors].uniq,
250
- messages: violations[:messages].uniq,
251
- markdowns: status_report[:markdowns].uniq,
252
- danger_id: danger_id,
253
- new_comment: new_comment,
254
- remove_previous_comments: remove_previous_comments
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} Pull Request - skipping `danger` run. "
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
- self.message = message
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
- attr_accessor :message, :sticky, :file, :line
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
- self.message = message
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 eql?(other)
32
- return self == other
33
- end
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
- # @return [Boolean] returns true if is a file or line, false otherwise
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 ', '.freeze} }"
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(template)
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
- def generate_inline_comment_body(emoji, message, danger_id: "danger", resolved: [], template: "github")
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.delete! ":"
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
- @username && !@username.empty? && @password && !@password.empty?
31
- end
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 and DANGER_BITBUCKETCLOUD_PASSWORD as environment variables."
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
 
@@ -1,4 +1,4 @@
1
1
  module Danger
2
- VERSION = "6.3.1".freeze
2
+ VERSION = "8.0.1".freeze
3
3
  DESCRIPTION = "Like Unit Tests, but for your Team Culture.".freeze
4
4
  end
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: 6.3.1
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-03-03 00:00:00.000000000 Z
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.6'
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.6'
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: '0.9'
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: '0.9'
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.3.0
328
+ version: 2.4.0
318
329
  required_rubygems_version: !ruby/object:Gem::Requirement
319
330
  requirements:
320
331
  - - ">="