danger 8.2.1 → 8.6.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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/danger/ci_source/azure_pipelines.rb +5 -1
  4. data/lib/danger/ci_source/buildkite.rb +1 -1
  5. data/lib/danger/ci_source/codefresh.rb +7 -13
  6. data/lib/danger/ci_source/codemagic.rb +58 -0
  7. data/lib/danger/ci_source/gitlab_ci.rb +18 -17
  8. data/lib/danger/ci_source/jenkins.rb +1 -1
  9. data/lib/danger/ci_source/local_git_repo.rb +29 -37
  10. data/lib/danger/ci_source/local_only_git_repo.rb +4 -8
  11. data/lib/danger/ci_source/support/commits.rb +14 -12
  12. data/lib/danger/ci_source/support/pull_request_finder.rb +43 -40
  13. data/lib/danger/ci_source/xcode_cloud.rb +38 -0
  14. data/lib/danger/commands/dangerfile/init.rb +1 -1
  15. data/lib/danger/commands/pr.rb +2 -1
  16. data/lib/danger/comment_generators/gitlab_inline.md.erb +2 -7
  17. data/lib/danger/comment_generators/vsts_inline.md.erb +17 -0
  18. data/lib/danger/danger_core/dangerfile.rb +15 -8
  19. data/lib/danger/danger_core/environment_manager.rb +2 -0
  20. data/lib/danger/danger_core/plugins/dangerfile_git_plugin.rb +1 -1
  21. data/lib/danger/danger_core/plugins/dangerfile_github_plugin.rb +8 -0
  22. data/lib/danger/danger_core/plugins/dangerfile_gitlab_plugin.rb +16 -0
  23. data/lib/danger/helpers/array_subclass.rb +2 -2
  24. data/lib/danger/helpers/comments_helper.rb +4 -3
  25. data/lib/danger/helpers/emoji_mapper.rb +1 -1
  26. data/lib/danger/plugin_support/plugin.rb +6 -2
  27. data/lib/danger/request_sources/bitbucket_cloud.rb +0 -1
  28. data/lib/danger/request_sources/bitbucket_cloud_api.rb +7 -6
  29. data/lib/danger/request_sources/bitbucket_server.rb +20 -16
  30. data/lib/danger/request_sources/bitbucket_server_api.rb +17 -9
  31. data/lib/danger/request_sources/code_insights_api.rb +2 -3
  32. data/lib/danger/request_sources/github/github.rb +30 -26
  33. data/lib/danger/request_sources/gitlab.rb +24 -24
  34. data/lib/danger/request_sources/local_only.rb +1 -2
  35. data/lib/danger/request_sources/request_source.rb +16 -4
  36. data/lib/danger/request_sources/vsts.rb +171 -9
  37. data/lib/danger/request_sources/vsts_api.rb +34 -3
  38. data/lib/danger/scm_source/git_repo.rb +2 -1
  39. data/lib/danger/version.rb +1 -1
  40. metadata +17 -8
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Danger
2
4
  module RequestSources
3
5
  class RequestSource
4
- DANGER_REPO_NAME = "danger".freeze
6
+ DANGER_REPO_NAME = "danger"
5
7
 
6
- attr_accessor :ci_source, :environment, :scm, :host, :ignored_violations
8
+ attr_accessor :ci_source, :scm, :host, :ignored_violations
7
9
 
8
10
  def self.env_vars
9
11
  raise "Subclass and overwrite self.env_vars"
@@ -23,12 +25,12 @@ module Danger
23
25
  end
24
26
 
25
27
  def self.source_name
26
- to_s.sub("Danger::RequestSources::".freeze, "".freeze)
28
+ to_s.sub("Danger::RequestSources::", "")
27
29
  end
28
30
 
29
31
  def self.available_source_names_and_envs
30
32
  available_request_sources.map do |klass|
31
- " - #{klass.source_name}: #{klass.env_vars.join(', '.freeze).yellow}"
33
+ " - #{klass.source_name}: #{klass.env_vars.join(', ').yellow}"
32
34
  end
33
35
  end
34
36
 
@@ -36,6 +38,16 @@ module Danger
36
38
  raise "Subclass and overwrite initialize"
37
39
  end
38
40
 
41
+ def inspect
42
+ inspected = super
43
+
44
+ inspected.gsub!(@token, "********") if @token
45
+ inspected.gsub!(@access_token, "********") if @access_token
46
+ inspected.gsub!(@bearer_token, "********") if @bearer_token
47
+
48
+ inspected
49
+ end
50
+
39
51
  # @return [Boolean] whether scm.origins is a valid git repository or not
40
52
  def validates_as_ci?
41
53
  !!self.scm.origins.match(%r{#{Regexp.escape self.host}(:|/)(.+/.+?)(?:\.git)?$})
@@ -24,7 +24,6 @@ module Danger
24
24
 
25
25
  def initialize(ci_source, environment)
26
26
  self.ci_source = ci_source
27
- self.environment = environment
28
27
 
29
28
  @is_vsts_git = environment["BUILD_REPOSITORY_PROVIDER"] == "TfsGit"
30
29
 
@@ -44,6 +43,10 @@ module Danger
44
43
  @scm ||= GitRepo.new
45
44
  end
46
45
 
46
+ def client
47
+ @api
48
+ end
49
+
47
50
  def host
48
51
  @host ||= @api.host
49
52
  end
@@ -77,15 +80,36 @@ module Danger
77
80
  return
78
81
  end
79
82
 
80
- comment = generate_description(warnings: warnings, errors: errors)
83
+ regular_violations = regular_violations_group(
84
+ warnings: warnings,
85
+ errors: errors,
86
+ messages: messages,
87
+ markdowns: markdowns
88
+ )
89
+
90
+ inline_violations = inline_violations_group(
91
+ warnings: warnings,
92
+ errors: errors,
93
+ messages: messages,
94
+ markdowns: markdowns
95
+ )
96
+
97
+ rest_inline_violations = submit_inline_comments!(**{
98
+ danger_id: danger_id,
99
+ previous_violations: {}
100
+ }.merge(inline_violations))
101
+
102
+ main_violations = merge_violations(
103
+ regular_violations, rest_inline_violations
104
+ )
105
+
106
+ comment = generate_description(warnings: main_violations[:warnings], errors: main_violations[:errors])
81
107
  comment += "\n\n"
82
- comment += generate_comment(warnings: warnings,
83
- errors: errors,
84
- messages: messages,
85
- markdowns: markdowns,
86
- previous_violations: {},
87
- danger_id: danger_id,
88
- template: "vsts")
108
+ comment += generate_comment(**{
109
+ previous_violations: {},
110
+ danger_id: danger_id,
111
+ template: "vsts"
112
+ }.merge(main_violations))
89
113
  if new_comment || remove_previous_comments
90
114
  post_new_comment(comment)
91
115
  else
@@ -106,6 +130,8 @@ module Danger
106
130
  comment_content = comment[:content].nil? ? "" : comment[:content]
107
131
  # Skip the comment if it wasn't posted by danger
108
132
  next unless comment_content.include?("generated_by_#{danger_id}")
133
+ # Skip the comment if it's an inline comment
134
+ next unless c[:threadContext].nil?
109
135
  # Updated the danger posted comment
110
136
  @api.update_comment(thread_id, comment_id, new_comment)
111
137
  comment_updated = true
@@ -113,6 +139,142 @@ module Danger
113
139
  # If no comment was updated, post a new one
114
140
  post_new_comment(new_comment) unless comment_updated
115
141
  end
142
+
143
+ def submit_inline_comments!(warnings: [], errors: [], messages: [], markdowns: [], previous_violations: [], danger_id: "danger")
144
+ # Avoid doing any fetchs if there's no inline comments
145
+ return {} if (warnings + errors + messages + markdowns).select(&:inline?).empty?
146
+
147
+ pr_threads = @api.fetch_last_comments
148
+ danger_threads = pr_threads.select do |thread|
149
+ comment = thread[:comments].first
150
+ comment_content = comment[:content].nil? ? "" : comment[:content]
151
+
152
+ next comment_content.include?("generated_by_#{danger_id}")
153
+ end
154
+ non_danger_threads = pr_threads - danger_threads
155
+
156
+ warnings = submit_inline_comments_for_kind!(:warning, warnings, danger_threads, previous_violations["warning"], danger_id: danger_id)
157
+ errors = submit_inline_comments_for_kind!(:error, errors, danger_threads, previous_violations["error"], danger_id: danger_id)
158
+ messages = submit_inline_comments_for_kind!(:message, messages, danger_threads, previous_violations["message"], danger_id: danger_id)
159
+ markdowns = submit_inline_comments_for_kind!(:markdown, markdowns, danger_threads, [], danger_id: danger_id)
160
+
161
+ # submit removes from the array all comments that are still in force
162
+ # so we strike out all remaining ones
163
+ danger_threads.each do |thread|
164
+ violation = violations_from_table(thread[:comments].first[:content]).first
165
+ if !violation.nil? && violation.sticky
166
+ body = generate_inline_comment_body("white_check_mark", violation, danger_id: danger_id, resolved: true, template: "github")
167
+ @api.update_comment(thread[:id], thread[:comments].first[:id], body)
168
+ end
169
+ end
170
+
171
+ {
172
+ warnings: warnings,
173
+ errors: errors,
174
+ messages: messages,
175
+ markdowns: markdowns
176
+ }
177
+ end
178
+
179
+ def messages_are_equivalent(m1, m2)
180
+ blob_regexp = %r{blob/[0-9a-z]+/}
181
+ m1.file == m2.file && m1.line == m2.line &&
182
+ m1.message.sub(blob_regexp, "") == m2.message.sub(blob_regexp, "")
183
+ end
184
+
185
+ def submit_inline_comments_for_kind!(kind, messages, danger_threads, previous_violations, danger_id: "danger")
186
+ previous_violations ||= []
187
+ is_markdown_content = kind == :markdown
188
+ emoji = { warning: "warning", error: "no_entry_sign", message: "book" }[kind]
189
+
190
+ messages.reject do |m|
191
+ next false unless m.file && m.line
192
+
193
+ # Once we know we're gonna submit it, we format it
194
+ if is_markdown_content
195
+ body = generate_inline_markdown_body(m, danger_id: danger_id, template: "vsts")
196
+ else
197
+ # Hide the inline link behind a span
198
+ m.message.gsub!("\n", "<br />")
199
+ m = process_markdown(m, true)
200
+ body = generate_inline_comment_body(emoji, m, danger_id: danger_id, template: "vsts")
201
+ # A comment might be in previous_violations because only now it's part of the unified diff
202
+ # We remove from the array since it won't have a place in the table anymore
203
+ previous_violations.reject! { |v| messages_are_equivalent(v, m) }
204
+ end
205
+
206
+ matching_threads = danger_threads.select do |comment_data|
207
+ if comment_data.key?(:threadContext) && !comment_data[:threadContext].nil? &&
208
+ comment_data[:threadContext][:filePath] == m.file &&
209
+ comment_data[:threadContext].key?(:rightFileStart) &&
210
+ comment_data[:threadContext][:rightFileStart][:line] == m.line
211
+ # Parse it to avoid problems with strikethrough
212
+ violation = violations_from_table(comment_data[:comments].first[:content]).first
213
+ if violation
214
+ messages_are_equivalent(violation, m)
215
+ else
216
+ blob_regexp = %r{blob/[0-9a-z]+/}
217
+ comment_data[:comments].first[:content].sub(blob_regexp, "") == body.sub(blob_regexp, "")
218
+ end
219
+ else
220
+ false
221
+ end
222
+ end
223
+
224
+ if matching_threads.empty?
225
+ @api.post_inline_comment(body, m.file, m.line)
226
+
227
+ # Not reject because this comment has not completed
228
+ next false
229
+ else
230
+ # Remove the surviving comment so we don't strike it out
231
+ danger_threads.reject! { |c| matching_threads.include? c }
232
+
233
+ # Update the comment to remove the strikethrough if present
234
+ thread = matching_threads.first
235
+ @api.update_comment(thread[:id], thread[:comments].first[:id], body)
236
+ end
237
+
238
+ # Remove this element from the array
239
+ next true
240
+ end
241
+ end
242
+
243
+ private
244
+
245
+ def regular_violations_group(warnings: [], errors: [], messages: [], markdowns: [])
246
+ {
247
+ warnings: warnings.reject(&:inline?),
248
+ errors: errors.reject(&:inline?),
249
+ messages: messages.reject(&:inline?),
250
+ markdowns: markdowns.reject(&:inline?)
251
+ }
252
+ end
253
+
254
+ def inline_violations_group(warnings: [], errors: [], messages: [], markdowns: [])
255
+ cmp = proc do |a, b|
256
+ next -1 unless a.file && a.line
257
+ next 1 unless b.file && b.line
258
+
259
+ next a.line <=> b.line if a.file == b.file
260
+ next a.file <=> b.file
261
+ end
262
+
263
+ # Sort to group inline comments by file
264
+ {
265
+ warnings: warnings.select(&:inline?).sort(&cmp),
266
+ errors: errors.select(&:inline?).sort(&cmp),
267
+ messages: messages.select(&:inline?).sort(&cmp),
268
+ markdowns: markdowns.select(&:inline?).sort(&cmp)
269
+ }
270
+ end
271
+
272
+ def merge_violations(*violation_groups)
273
+ violation_groups.inject({}) do |accumulator, group|
274
+ accumulator.merge(group) { |_, old, fresh| old + fresh }
275
+ end
276
+ end
277
+
116
278
  end
117
279
  end
118
280
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # coding: utf-8
2
3
 
3
4
  require "base64"
@@ -35,9 +36,7 @@ module Danger
35
36
  def inspect
36
37
  inspected = super
37
38
 
38
- if @token
39
- inspected = inspected.sub! @token, "********".freeze
40
- end
39
+ inspected.gsub!(@token, "********") if @token
41
40
 
42
41
  inspected
43
42
  end
@@ -77,6 +76,38 @@ module Danger
77
76
  post(uri, body)
78
77
  end
79
78
 
79
+ def post_inline_comment(text, file, line)
80
+ uri = URI("#{pr_api_endpoint}/threads?api-version=#{@api_version}")
81
+ body = {
82
+ "comments" => [
83
+ {
84
+ "parentCommentId" => 0,
85
+ "content" => text,
86
+ "commentType" => 1
87
+ }
88
+ ],
89
+ "properties" => {
90
+ "Microsoft.TeamFoundation.Discussion.SupportsMarkdown" => {
91
+ "type" => "System.Int32",
92
+ "value" => 1
93
+ }
94
+ },
95
+ "status" => 1,
96
+ "threadContext" => {
97
+ "filePath" => file,
98
+ "rightFileEnd" => {
99
+ "line" => line + 1,
100
+ "offset" => 1
101
+ },
102
+ "rightFileStart" => {
103
+ "line" => line,
104
+ "offset" => 1
105
+ }
106
+ }
107
+ }.to_json
108
+ post(uri, body)
109
+ end
110
+
80
111
  def update_comment(thread, id, new_comment)
81
112
  uri = URI("#{pr_api_endpoint}/threads/#{thread}/comments/#{id}?api-version=#{@api_version}")
82
113
  body = {
@@ -55,7 +55,8 @@ module Danger
55
55
  def exec(string)
56
56
  require "open3"
57
57
  Dir.chdir(self.folder || ".") do
58
- Open3.popen2(default_env, "git #{string}") do |_stdin, stdout, _wait_thr|
58
+ git_command = string.split(" ").dup.unshift("git")
59
+ Open3.popen2(default_env, *git_command) do |_stdin, stdout, _wait_thr|
59
60
  stdout.read.rstrip
60
61
  end
61
62
  end
@@ -1,4 +1,4 @@
1
1
  module Danger
2
- VERSION = "8.2.1".freeze
2
+ VERSION = "8.6.1".freeze
3
3
  DESCRIPTION = "Like Unit Tests, but for your Team Culture.".freeze
4
4
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: danger
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.2.1
4
+ version: 8.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Orta Therox
8
8
  - Juanito Fatas
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-11-09 00:00:00.000000000 Z
12
+ date: 2022-04-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: claide
@@ -147,16 +147,22 @@ dependencies:
147
147
  name: terminal-table
148
148
  requirement: !ruby/object:Gem::Requirement
149
149
  requirements:
150
- - - "~>"
150
+ - - ">="
151
151
  - !ruby/object:Gem::Version
152
152
  version: '1'
153
+ - - "<"
154
+ - !ruby/object:Gem::Version
155
+ version: '4'
153
156
  type: :runtime
154
157
  prerelease: false
155
158
  version_requirements: !ruby/object:Gem::Requirement
156
159
  requirements:
157
- - - "~>"
160
+ - - ">="
158
161
  - !ruby/object:Gem::Version
159
162
  version: '1'
163
+ - - "<"
164
+ - !ruby/object:Gem::Version
165
+ version: '4'
160
166
  - !ruby/object:Gem::Dependency
161
167
  name: cork
162
168
  requirement: !ruby/object:Gem::Requirement
@@ -213,6 +219,7 @@ files:
213
219
  - lib/danger/ci_source/cirrus.rb
214
220
  - lib/danger/ci_source/code_build.rb
215
221
  - lib/danger/ci_source/codefresh.rb
222
+ - lib/danger/ci_source/codemagic.rb
216
223
  - lib/danger/ci_source/codeship.rb
217
224
  - lib/danger/ci_source/concourse.rb
218
225
  - lib/danger/ci_source/dotci.rb
@@ -237,6 +244,7 @@ files:
237
244
  - lib/danger/ci_source/teamcity.rb
238
245
  - lib/danger/ci_source/travis.rb
239
246
  - lib/danger/ci_source/vsts.rb
247
+ - lib/danger/ci_source/xcode_cloud.rb
240
248
  - lib/danger/ci_source/xcode_server.rb
241
249
  - lib/danger/clients/rubygems_client.rb
242
250
  - lib/danger/commands/dangerfile/gem.rb
@@ -263,6 +271,7 @@ files:
263
271
  - lib/danger/comment_generators/gitlab.md.erb
264
272
  - lib/danger/comment_generators/gitlab_inline.md.erb
265
273
  - lib/danger/comment_generators/vsts.md.erb
274
+ - lib/danger/comment_generators/vsts_inline.md.erb
266
275
  - lib/danger/core_ext/file_list.rb
267
276
  - lib/danger/core_ext/string.rb
268
277
  - lib/danger/danger_core/dangerfile.rb
@@ -319,7 +328,7 @@ homepage: https://github.com/danger/danger
319
328
  licenses:
320
329
  - MIT
321
330
  metadata: {}
322
- post_install_message:
331
+ post_install_message:
323
332
  rdoc_options: []
324
333
  require_paths:
325
334
  - lib
@@ -334,8 +343,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
334
343
  - !ruby/object:Gem::Version
335
344
  version: '0'
336
345
  requirements: []
337
- rubygems_version: 3.1.2
338
- signing_key:
346
+ rubygems_version: 3.1.4
347
+ signing_key:
339
348
  specification_version: 4
340
349
  summary: Like Unit Tests, but for your Team Culture.
341
350
  test_files: []