danger 8.2.1 → 8.6.1

Sign up to get free protection for your applications and to get access to all the features.
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: []