danger 8.0.6 → 8.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b175fad0150fdcb243c42f37c0b9683a7dc00db8b3be4ef81d01be0103b90bb
4
- data.tar.gz: d422bf7eadd533e5591f1ac43cde404f38d124a3c398469a1b9563c5338e8370
3
+ metadata.gz: 58ac1ecdfec8029d0fee2f4d6e0b63e975c56f1ced271f24fd8cf094f3e7e647
4
+ data.tar.gz: 65ef72507496b5c6d9d34da8473d5ba6006eba4338d84618a22bd95dd928b115
5
5
  SHA512:
6
- metadata.gz: d36a05ed7713cf0b50936c04feaa4cb49c8ba035b539ff265c9d7e231c2670efba007f5f4a2bf58936cab24c7aa0e09d5c9a2a9edff9714689fc5b03cc31d870
7
- data.tar.gz: 611a647c06bab7c3949ded7ff12b5498b966d54f10056481ae720dfa3801f48ac27085d112f69c186feb5638b2b82b0f7583c0f020af33af8251705429fbf614
6
+ metadata.gz: 8717fee5c49cc4cb0835a7ce4d68755a3981f2e4e18529da14c49a7433b5a250a16097930b0ad342fbed6b0d020219b79795cb46da22b871f1d69b4aaaf3484e
7
+ data.tar.gz: 0b128cb7f4b2f6749a6e1f5c58dce9c17bf992f71660e86b3771f5b3797e1165fe4187d58cfb9eb4f343314ef4035c5fbc04936abaf2af32389c9a0522809887
@@ -2,6 +2,8 @@
2
2
 
3
3
  require "danger/helpers/comments_helper"
4
4
  require "danger/request_sources/bitbucket_server_api"
5
+ require "danger/request_sources/code_insights_api"
6
+ require_relative "request_source"
5
7
 
6
8
  module Danger
7
9
  module RequestSources
@@ -17,12 +19,21 @@ module Danger
17
19
  ]
18
20
  end
19
21
 
22
+ def self.optional_env_vars
23
+ ["DANGER_BITBUCKETSERVER_CODE_INSIGHTS_REPORT_KEY",
24
+ "DANGER_BITBUCKETSERVER_CODE_INSIGHTS_REPORT_TITLE",
25
+ "DANGER_BITBUCKETSERVER_CODE_INSIGHTS_REPORT_DESCRIPTION",
26
+ "DANGER_BITBUCKETSERVER_CODE_INSIGHTS_REPORT_LOGO_URL"
27
+ ]
28
+ end
29
+
20
30
  def initialize(ci_source, environment)
21
31
  self.ci_source = ci_source
22
32
  self.environment = environment
23
33
 
24
34
  project, slug = ci_source.repo_slug.split("/")
25
35
  @api = BitbucketServerAPI.new(project, slug, ci_source.pull_request_id, environment)
36
+ @code_insights = CodeInsightsAPI.new(project, slug, environment)
26
37
  end
27
38
 
28
39
  def validates_as_ci?
@@ -73,16 +84,42 @@ module Danger
73
84
  def update_pull_request!(warnings: [], errors: [], messages: [], markdowns: [], danger_id: "danger", new_comment: false, remove_previous_comments: false)
74
85
  delete_old_comments(danger_id: danger_id) if !new_comment || remove_previous_comments
75
86
 
76
- comment = generate_description(warnings: warnings, errors: errors)
87
+ # If configured, send a Code Insights API to provide the PR with a quality report
88
+ # which includes inline code violations found by Danger as Annotations.
89
+ # If no inline violations occurred, an empty, successful (green) report will be sent.
90
+ if @code_insights.ready?
91
+ inline_violations = inline_violations_group(warnings: warnings, errors: errors, messages: messages)
92
+ inline_warnings = inline_violations[:warnings] || []
93
+ inline_errors = inline_violations[:errors] || []
94
+ inline_messages = inline_violations[:messages] || []
95
+
96
+ head_commit = self.pr_json[:fromRef][:latestCommit]
97
+ @code_insights.send_report(head_commit,
98
+ inline_warnings,
99
+ inline_errors,
100
+ inline_messages)
101
+ end
102
+
103
+ # If we're sending inline comments separately via Code Insights,
104
+ # the main body comment should contain only generic, non-file specific messages.
105
+ if @code_insights.ready?
106
+ main_violations = main_violations_group(warnings: warnings, errors: errors, messages: messages)
107
+ warnings = main_violations[:warnings] || []
108
+ errors = main_violations[:errors] || []
109
+ messages = main_violations[:messages] || []
110
+ markdowns = main_violations[:markdowns] || []
111
+ end
112
+
113
+ comment = generate_description(warnings: warnings,
114
+ errors: errors)
77
115
  comment += "\n\n"
78
116
  comment += generate_comment(warnings: warnings,
79
- errors: errors,
80
- messages: messages,
81
- markdowns: markdowns,
82
- previous_violations: {},
83
- danger_id: danger_id,
84
- template: "bitbucket_server")
85
-
117
+ errors: errors,
118
+ messages: messages,
119
+ markdowns: markdowns,
120
+ previous_violations: {},
121
+ danger_id: danger_id,
122
+ template: "bitbucket_server")
86
123
  @api.post_comment(comment)
87
124
  end
88
125
 
@@ -91,7 +128,34 @@ module Danger
91
128
  @api.delete_comment(c[:id], c[:version]) if c[:text] =~ /generated_by_#{danger_id}/
92
129
  end
93
130
  end
94
-
131
+
132
+ def main_violations_group(warnings: [], errors: [], messages: [], markdowns: [])
133
+ {
134
+ warnings: warnings.reject(&:inline?),
135
+ errors: errors.reject(&:inline?),
136
+ messages: messages.reject(&:inline?),
137
+ markdowns: markdowns.reject(&:inline?)
138
+ }
139
+ end
140
+
141
+ def inline_violations_group(warnings: [], errors: [], messages: [], markdowns: [])
142
+ cmp = proc do |a, b|
143
+ next -1 unless a.file && a.line
144
+ next 1 unless b.file && b.line
145
+
146
+ next a.line <=> b.line if a.file == b.file
147
+ next a.file <=> b.file
148
+ end
149
+
150
+ # Sort to group inline comments by file
151
+ {
152
+ warnings: warnings.select(&:inline?).sort(&cmp),
153
+ errors: errors.select(&:inline?).sort(&cmp),
154
+ messages: messages.select(&:inline?).sort(&cmp),
155
+ markdowns: markdowns.select(&:inline?).sort(&cmp)
156
+ }
157
+ end
158
+
95
159
  def update_pr_build_status(status, build_job_link, description)
96
160
  changeset = self.pr_json[:fromRef][:latestCommit]
97
161
  # Support for older versions of Bitbucket Server
@@ -0,0 +1,147 @@
1
+ # coding: utf-8
2
+
3
+ module Danger
4
+ module RequestSources
5
+ #
6
+ # Provides ability for Danger to interact with Atlassian's Code Insights API in order to provide code quality
7
+ # reports along with inline comments for specific lines in specific files.
8
+ # See https://developer.atlassian.com/server/bitbucket/how-tos/code-insights/ for more details.
9
+ #
10
+ # Currently this functionality is implemented only for Bitbucket Server request source.
11
+ class CodeInsightsAPI
12
+ attr_accessor :username, :password, :host, :report_key, :report_title, :report_description, :logo_url
13
+
14
+ def initialize(project, slug, environment)
15
+ @username = environment["DANGER_BITBUCKETSERVER_USERNAME"] || ""
16
+ @password = environment["DANGER_BITBUCKETSERVER_PASSWORD"] || ""
17
+ @host = environment["DANGER_BITBUCKETSERVER_HOST"] || ""
18
+ @report_key = environment["DANGER_BITBUCKETSERVER_CODE_INSIGHTS_REPORT_KEY"] || ""
19
+ @report_title = environment["DANGER_BITBUCKETSERVER_CODE_INSIGHTS_REPORT_TITLE"] || ""
20
+ @report_description = environment["DANGER_BITBUCKETSERVER_CODE_INSIGHTS_REPORT_DESCRIPTION"] || ""
21
+ @logo_url = environment["DANGER_BITBUCKETSERVER_CODE_INSIGHTS_REPORT_LOGO_URL"] || ""
22
+ @project = project
23
+ @slug = slug
24
+ end
25
+
26
+ def inspect
27
+ inspected = super
28
+
29
+ if @password
30
+ inspected = inspected.sub! @password, "********".freeze
31
+ end
32
+
33
+ inspected
34
+ end
35
+
36
+ def ready?
37
+ !(@report_key.empty? || @report_title.empty? || @report_description.empty? || @username.empty? || @password.empty? || @host.empty?)
38
+ end
39
+
40
+ def delete_report(commit)
41
+ uri = URI(report_endpoint_at_commit(commit))
42
+ request = Net::HTTP::Delete.new(uri.request_uri, {"Content-Type" => "application/json"})
43
+ request.basic_auth @username, @password
44
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: use_ssl) do |http|
45
+ http.request(request)
46
+ end
47
+
48
+ # show failure when server returns an error
49
+ case response
50
+ when Net::HTTPClientError, Net::HTTPServerError
51
+ # HTTP 4xx - 5xx
52
+ abort "\nError deleting report from Code Insights API: #{response.code} (#{response.message}) - #{response.body}\n\n"
53
+ end
54
+
55
+ end
56
+
57
+ def send_report(commit, inline_warnings, inline_errors, inline_messages)
58
+ delete_report(commit)
59
+ put_report(commit, inline_errors.count)
60
+ should_post_annotations = !(inline_warnings + inline_errors + inline_messages).empty?
61
+ if should_post_annotations
62
+ post_annotations(commit, inline_warnings, inline_errors, inline_messages)
63
+ end
64
+ end
65
+
66
+ def put_report(commit, inline_errors_count)
67
+ uri = URI(report_endpoint_at_commit(commit))
68
+ request = Net::HTTP::Put.new(uri.request_uri, {"Content-Type" => "application/json"})
69
+ request.basic_auth @username, @password
70
+ request.body = {"title": @report_title,
71
+ "details": @report_description,
72
+ "result": (inline_errors_count > 0) ? "FAIL" : "PASS",
73
+ "reporter": @username,
74
+ "link": "https://github.com/danger/danger",
75
+ "logoURL": @logo_url
76
+ }.to_json
77
+
78
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: use_ssl) do |http|
79
+ http.request(request)
80
+ end
81
+
82
+ # show failure when server returns an error
83
+ case response
84
+ when Net::HTTPClientError, Net::HTTPServerError
85
+ # HTTP 4xx - 5xx
86
+ abort "\nError putting report to Code Insights API: #{response.code} (#{response.message}) - #{response.body}\n\n"
87
+ end
88
+ end
89
+
90
+ def post_annotations(commit, inline_warnings, inline_errors, inline_messages)
91
+ uri = URI(annotation_endpoint_at_commit(commit))
92
+
93
+ annotations = []
94
+
95
+ inline_messages.each do |violation|
96
+ annotations << violation_hash_with_severity(violation, "LOW")
97
+ end
98
+
99
+ inline_warnings.each do |violation|
100
+ annotations << violation_hash_with_severity(violation, "MEDIUM")
101
+ end
102
+
103
+ inline_errors.each do |violation|
104
+ annotations << violation_hash_with_severity(violation, "HIGH")
105
+ end
106
+
107
+ body = {annotations: annotations}.to_json
108
+ request = Net::HTTP::Post.new(uri.request_uri, {"Content-Type" => "application/json"})
109
+ request.basic_auth @username, @password
110
+ request.body = body
111
+
112
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: use_ssl) do |http|
113
+ http.request(request)
114
+ end
115
+
116
+ # show failure when server returns an error
117
+ case response
118
+ when Net::HTTPClientError, Net::HTTPServerError
119
+ # HTTP 4xx - 5xx
120
+ abort "\nError posting comment to Code Insights API: #{response.code} (#{response.message}) - #{response.body}\n\n"
121
+ end
122
+ end
123
+
124
+ def violation_hash_with_severity(violation, severity)
125
+ annotation = {}
126
+ annotation["message"] = violation.message
127
+ annotation["severity"] = severity
128
+ annotation["path"] = violation.file
129
+ annotation["line"] = violation.line.to_i
130
+ return annotation
131
+ end
132
+
133
+ def report_endpoint_at_commit(commit)
134
+ "#{@host}/rest/insights/1.0/projects/#{@project}/repos/#{@slug}/commits/#{commit}/reports/#{@report_key}"
135
+ end
136
+
137
+ def annotation_endpoint_at_commit(commit)
138
+ report_endpoint_at_commit(commit) + "/annotations"
139
+ end
140
+
141
+ def use_ssl
142
+ @host.include? "https://"
143
+ end
144
+
145
+ end
146
+ end
147
+ end
@@ -171,10 +171,10 @@ module Danger
171
171
  markdowns: markdowns
172
172
  )
173
173
 
174
- rest_inline_violations = submit_inline_comments!({
174
+ rest_inline_violations = submit_inline_comments!(**{
175
175
  danger_id: danger_id,
176
176
  previous_violations: previous_violations
177
- }.merge(**inline_violations))
177
+ }.merge(inline_violations))
178
178
 
179
179
  main_violations = merge_violations(
180
180
  regular_violations, rest_inline_violations
@@ -189,11 +189,11 @@ module Danger
189
189
 
190
190
  # If there are still violations to show
191
191
  if main_violations_sum.any?
192
- body = generate_comment({
192
+ body = generate_comment(**{
193
193
  template: "github",
194
194
  danger_id: danger_id,
195
195
  previous_violations: previous_violations
196
- }.merge(**main_violations))
196
+ }.merge(main_violations))
197
197
 
198
198
  comment_result =
199
199
  if should_create_new_comment
@@ -1,4 +1,4 @@
1
1
  module Danger
2
- VERSION = "8.0.6".freeze
2
+ VERSION = "8.1.0".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: 8.0.6
4
+ version: 8.1.0
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-10-05 00:00:00.000000000 Z
12
+ date: 2020-10-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: claide
@@ -301,6 +301,7 @@ files:
301
301
  - lib/danger/request_sources/bitbucket_cloud_api.rb
302
302
  - lib/danger/request_sources/bitbucket_server.rb
303
303
  - lib/danger/request_sources/bitbucket_server_api.rb
304
+ - lib/danger/request_sources/code_insights_api.rb
304
305
  - lib/danger/request_sources/github/github.rb
305
306
  - lib/danger/request_sources/github/github_review.rb
306
307
  - lib/danger/request_sources/github/github_review_resolver.rb