danger 8.0.2 → 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: fbea5f64686dec7d5badd386c112577a626fb35bea77852a15e3b420e846ce89
4
- data.tar.gz: 4e32c0d00be5247ef8c0c33383c49bbb4dcd0d51fe48ea75916d3a61f85d5708
3
+ metadata.gz: 58ac1ecdfec8029d0fee2f4d6e0b63e975c56f1ced271f24fd8cf094f3e7e647
4
+ data.tar.gz: 65ef72507496b5c6d9d34da8473d5ba6006eba4338d84618a22bd95dd928b115
5
5
  SHA512:
6
- metadata.gz: 14aaf58cf077d3e316d17ab73d1b27378ad71d284f87c6c80cfd3b0cb24aeaea7862a8a3ddf0f4a3a8d883ee8225d36f8c242576be18d3c44ce40c6181d2dfbe
7
- data.tar.gz: f832a4990da69caf5c001fdc6b4ee3231fe8a4199924bbff695725f89f29a887e54d449ad51989d80a8e29478a85775acafb0c1031b3d7f515acc519a311e39f
6
+ metadata.gz: 8717fee5c49cc4cb0835a7ce4d68755a3981f2e4e18529da14c49a7433b5a250a16097930b0ad342fbed6b0d020219b79795cb46da22b871f1d69b4aaaf3484e
7
+ data.tar.gz: 0b128cb7f4b2f6749a6e1f5c58dce9c17bf992f71660e86b3771f5b3797e1165fe4187d58cfb9eb4f343314ef4035c5fbc04936abaf2af32389c9a0522809887
@@ -50,16 +50,29 @@ module Danger
50
50
  self.repo_url = env["GIT_REPOSITORY_URL"]
51
51
 
52
52
  matcher_url = self.repo_url
53
+ self.repo_slug = repo_slug_from(self.repo_url)
54
+ end
53
55
 
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://"] = ''
56
+ def repo_slug_from(url)
57
+ if url =~ URI::regexp
58
+ # Try to parse the URL as a valid URI. This should cover the cases of http/https/ssh URLs.
59
+ begin
60
+ uri = URI.parse(url)
61
+ return uri.path.sub(/^(\/)/,'').sub(/(.git)$/,'')
62
+ rescue URI::InvalidURIError
63
+ # In case URL could not be parsed fallback to git URL parsing.
64
+ repo_slug_asgiturl(url)
65
+ end
66
+ else
67
+ # In case URL could not be parsed fallback to git URL parsing. git@github.com:organization/repo.git
68
+ repo_slug_asgiturl(url)
57
69
  end
70
+ end
58
71
 
72
+ def repo_slug_asgiturl(url)
73
+ matcher_url = url
59
74
  repo_matches = matcher_url.match(%r{([\/:])(([^\/]+\/)+[^\/]+?)(\.git$|$)})[2]
60
-
61
- self.repo_slug = repo_matches unless repo_matches.nil?
62
-
75
+ return repo_matches unless repo_matches.nil?
63
76
  end
64
77
  end
65
78
  end
@@ -1,5 +1,6 @@
1
1
  # http://docs.gitlab.com/ce/ci/variables/README.html
2
2
  require "uri"
3
+ require "danger/request_sources/github/github"
3
4
  require "danger/request_sources/gitlab"
4
5
 
5
6
  module Danger
@@ -15,10 +16,13 @@ module Danger
15
16
  # ```
16
17
  # ### Token Setup
17
18
  #
18
- # Add the `DANGER_GITLAB_API_TOKEN` to your pipeline env variables.
19
- class GitLabCI < CI
20
- attr_reader :project_url
19
+ # Add the `DANGER_GITLAB_API_TOKEN` to your pipeline env variables if you
20
+ # are hosting your code on GitLab. If you are using GitLab as a mirror
21
+ # for the purpose of CI/CD, while hosting your repo on GitHub, set the
22
+ # `DANGER_GITHUB_API_TOKEN` as well as the project repo URL to
23
+ # `DANGER_PROJECT_REPO_URL`.
21
24
 
25
+ class GitLabCI < CI
22
26
  def self.validates_as_ci?(env)
23
27
  env.key? "GITLAB_CI"
24
28
  end
@@ -28,11 +32,12 @@ module Danger
28
32
  "GITLAB_CI", "CI_PROJECT_PATH"
29
33
  ].all? { |x| env[x] }
30
34
 
31
- exists && determine_merge_request_id(env).to_i > 0
35
+ exists && determine_pull_or_merge_request_id(env).to_i > 0
32
36
  end
33
37
 
34
- def self.determine_merge_request_id(env)
38
+ def self.determine_pull_or_merge_request_id(env)
35
39
  return env["CI_MERGE_REQUEST_IID"] if env["CI_MERGE_REQUEST_IID"]
40
+ return env["CI_EXTERNAL_PULL_REQUEST_IID"] if env["CI_EXTERNAL_PULL_REQUEST_IID"]
36
41
  return 0 unless env["CI_COMMIT_SHA"]
37
42
 
38
43
  project_path = env["CI_MERGE_REQUEST_PROJECT_PATH"] || env["CI_PROJECT_PATH"]
@@ -54,16 +59,28 @@ module Danger
54
59
 
55
60
  def initialize(env)
56
61
  @env = env
57
- @repo_slug = env["CI_MERGE_REQUEST_PROJECT_PATH"] || env["CI_PROJECT_PATH"]
58
- @project_url = env["CI_MERGE_REQUEST_PROJECT_URL"] || env["CI_PROJECT_URL"]
62
+ @repo_slug = slug_from(env)
59
63
  end
60
64
 
61
65
  def supported_request_sources
62
- @supported_request_sources ||= [Danger::RequestSources::GitLab]
66
+ @supported_request_sources ||= [
67
+ Danger::RequestSources::GitHub,
68
+ Danger::RequestSources::GitLab
69
+ ]
63
70
  end
64
71
 
65
72
  def pull_request_id
66
- @pull_request_id ||= self.class.determine_merge_request_id(@env)
73
+ @pull_request_id ||= self.class.determine_pull_or_merge_request_id(@env)
74
+ end
75
+
76
+ private
77
+
78
+ def slug_from(env)
79
+ if env["DANGER_PROJECT_REPO_URL"]
80
+ env["DANGER_PROJECT_REPO_URL"].split('/').last(2).join('/')
81
+ else
82
+ env["CI_MERGE_REQUEST_PROJECT_PATH"] || env["CI_PROJECT_PATH"]
83
+ end
67
84
  end
68
85
  end
69
86
  end
@@ -27,6 +27,8 @@ module Danger
27
27
  # branch="%teamcity.build.branch%"
28
28
  # export GITHUB_PULL_REQUEST_ID=(${branch//\// })
29
29
  # ```
30
+ # Or if you are using the pull request feature you can set an environment parameter called `GITHUB_PULL_REQUEST_ID`
31
+ # to the value of: `%teamcity.pullRequest.number`
30
32
  #
31
33
  # #### GitLab
32
34
  #
@@ -9,7 +9,7 @@ module Danger
9
9
  def include?(pattern)
10
10
  self.each do |current|
11
11
  unless current.nil?
12
- return true if File.fnmatch(pattern, current) || pattern == current
12
+ return true if File.fnmatch(pattern, current, File::FNM_EXTGLOB) || pattern == current
13
13
  end
14
14
  end
15
15
  return false
@@ -227,6 +227,23 @@ module Danger
227
227
  paths.first(paths.count - 1).join(", ") + " & " + paths.last
228
228
  end
229
229
 
230
+ # @!group Gitlab Misc
231
+ # Use to ignore inline messages which lay outside a diff's range, thereby not posting the comment.
232
+ # You can set hash to change behavior per each kinds. (ex. `{warning: true, error: false}`)
233
+ # @param [Bool] or [Hash<Symbol, Bool>] dismiss
234
+ # Ignore out of range inline messages, defaults to `true`
235
+ #
236
+ # @return [void]
237
+ def dismiss_out_of_range_messages(dismiss = true)
238
+ if dismiss.kind_of?(Hash)
239
+ @gitlab.dismiss_out_of_range_messages = dismiss
240
+ elsif dismiss.kind_of?(TrueClass)
241
+ @gitlab.dismiss_out_of_range_messages = true
242
+ elsif dismiss.kind_of?(FalseClass)
243
+ @gitlab.dismiss_out_of_range_messages = false
244
+ end
245
+ end
246
+
230
247
  %i(title body author labels json diff).each do |suffix|
231
248
  alias_method "pr_#{suffix}".to_sym, "mr_#{suffix}".to_sym
232
249
  end
@@ -11,7 +11,7 @@ module Danger
11
11
  include Danger::Helpers::CommentsParsingHelper
12
12
 
13
13
  def markdown_parser(text)
14
- Kramdown::Document.new(text, input: "GFM")
14
+ Kramdown::Document.new(text, input: "GFM", smart_quotes: %w[apos apos quot quot])
15
15
  end
16
16
 
17
17
  # !@group Extension points
@@ -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
@@ -40,6 +40,10 @@ module Danger
40
40
  end
41
41
  end
42
42
 
43
+ def validates_as_ci?
44
+ true
45
+ end
46
+
43
47
  def validates_as_api_source?
44
48
  (@token && !@token.empty?) || self.environment["DANGER_USE_LOCAL_GIT"]
45
49
  end
@@ -167,10 +171,10 @@ module Danger
167
171
  markdowns: markdowns
168
172
  )
169
173
 
170
- rest_inline_violations = submit_inline_comments!({
174
+ rest_inline_violations = submit_inline_comments!(**{
171
175
  danger_id: danger_id,
172
176
  previous_violations: previous_violations
173
- }.merge(**inline_violations))
177
+ }.merge(inline_violations))
174
178
 
175
179
  main_violations = merge_violations(
176
180
  regular_violations, rest_inline_violations
@@ -185,11 +189,11 @@ module Danger
185
189
 
186
190
  # If there are still violations to show
187
191
  if main_violations_sum.any?
188
- body = generate_comment({
192
+ body = generate_comment(**{
189
193
  template: "github",
190
194
  danger_id: danger_id,
191
195
  previous_violations: previous_violations
192
- }.merge(**main_violations))
196
+ }.merge(main_violations))
193
197
 
194
198
  comment_result =
195
199
  if should_create_new_comment
@@ -8,7 +8,7 @@ module Danger
8
8
  module RequestSources
9
9
  class GitLab < RequestSource
10
10
  include Danger::Helpers::CommentsHelper
11
- attr_accessor :mr_json, :commits_json
11
+ attr_accessor :mr_json, :commits_json, :dismiss_out_of_range_messages
12
12
 
13
13
  FIRST_GITLAB_GEM_WITH_VERSION_CHECK = Gem::Version.new("4.6.0")
14
14
  FIRST_VERSION_WITH_INLINE_COMMENTS = Gem::Version.new("10.8.0")
@@ -24,6 +24,7 @@ module Danger
24
24
  def initialize(ci_source, environment)
25
25
  self.ci_source = ci_source
26
26
  self.environment = environment
27
+ self.dismiss_out_of_range_messages = false
27
28
 
28
29
  @token = @environment["DANGER_GITLAB_API_TOKEN"]
29
30
  end
@@ -208,7 +209,7 @@ module Danger
208
209
  rest_inline_violations = submit_inline_comments!({
209
210
  danger_id: danger_id,
210
211
  previous_violations: previous_violations
211
- }.merge(inline_violations))
212
+ }.merge(**inline_violations))
212
213
 
213
214
  main_violations = merge_violations(
214
215
  regular_violations, rest_inline_violations
@@ -227,7 +228,7 @@ module Danger
227
228
  template: "gitlab",
228
229
  danger_id: danger_id,
229
230
  previous_violations: previous_violations
230
- }.merge(main_violations))
231
+ }.merge(**main_violations))
231
232
 
232
233
  comment_result =
233
234
  if should_create_new_comment
@@ -302,6 +303,16 @@ module Danger
302
303
  nil # TODO: Implement this
303
304
  end
304
305
 
306
+ def dismiss_out_of_range_messages_for(kind)
307
+ if self.dismiss_out_of_range_messages.kind_of?(Hash) && self.dismiss_out_of_range_messages[kind]
308
+ self.dismiss_out_of_range_messages[kind]
309
+ elsif self.dismiss_out_of_range_messages == true
310
+ self.dismiss_out_of_range_messages
311
+ else
312
+ false
313
+ end
314
+ end
315
+
305
316
  # @return [String] A URL to the specific file, ready to be downloaded
306
317
  def file_url(organisation: nil, repository: nil, branch: nil, path: nil)
307
318
  branch ||= 'master'
@@ -397,10 +408,9 @@ module Danger
397
408
 
398
409
  messages.reject do |m|
399
410
  next false unless m.file && m.line
400
-
401
- # Keep the change it's in a file changed in this diff
402
- next if !mr_changed_paths.include?(m.file)
403
-
411
+ # Reject if it's out of range and in dismiss mode
412
+ next true if dismiss_out_of_range_messages_for(kind) && is_out_of_range(mr_changes.changes, m)
413
+
404
414
  # Once we know we're gonna submit it, we format it
405
415
  if is_markdown_content
406
416
  body = generate_inline_markdown_body(m, danger_id: danger_id, template: "gitlab")
@@ -473,7 +483,6 @@ module Danger
473
483
  range_header_regexp = /@@ -(?<old>[0-9]+)(,([0-9]+))? \+(?<new>[0-9]+)(,([0-9]+))? @@.*/
474
484
 
475
485
  change = changes.find { |c| c["new_path"] == message.file }
476
-
477
486
  # If there is no changes or rename only or deleted, return nil.
478
487
  return nil if change.nil? || change["diff"].empty? || change["deleted_file"]
479
488
 
@@ -520,6 +529,41 @@ module Danger
520
529
  line: current_old_line - current_new_line + message.line.to_i
521
530
  }
522
531
  end
532
+
533
+ def is_out_of_range(changes, message)
534
+ change = changes.find { |c| c["new_path"] == message.file }
535
+ # If there is no changes or rename only or deleted, return out of range.
536
+ return true if change.nil? || change["diff"].empty? || change["deleted_file"]
537
+
538
+ # If new file then return in range
539
+ return false if change["new_file"]
540
+
541
+ addition_lines = generate_addition_lines(change["diff"])
542
+ return false if addition_lines.include?(message.line.to_i)
543
+
544
+ return true
545
+ end
546
+
547
+ def generate_addition_lines(diff)
548
+ range_header_regexp = /@@ -(?<old>[0-9]+)(,([0-9]+))? \+(?<new>[0-9]+)(,([0-9]+))? @@.*/
549
+ addition_lines = []
550
+ line_number = 0
551
+ diff.each_line do |line|
552
+ if line.match range_header_regexp
553
+ line = line.split('+').last
554
+ line = line.split(' ').first
555
+ range_string = line.split(',')
556
+ line_number = range_string[0].to_i - 1
557
+ elsif line.start_with?('+')
558
+ addition_lines.push(line_number)
559
+ elsif line.start_with?('-')
560
+ line_number=line_number-1
561
+ end
562
+ line_number=line_number+1
563
+ end
564
+ addition_lines
565
+ end
566
+
523
567
  end
524
568
  end
525
569
  end
@@ -135,7 +135,7 @@ module Danger
135
135
  git_in_depth_fetch
136
136
  possible_merge_base = possible_merge_base(repo, from, to)
137
137
 
138
- raise "Cannot find a merge base between #{from} and #{to}." unless possible_merge_base
138
+ raise "Cannot find a merge base between #{from} and #{to}. If you are using shallow clone/fetch, try increasing the --depth" unless possible_merge_base
139
139
 
140
140
  possible_merge_base
141
141
  end
@@ -1,4 +1,4 @@
1
1
  module Danger
2
- VERSION = "8.0.2".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.2
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-06-17 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
@@ -107,14 +107,14 @@ dependencies:
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '2.0'
110
+ version: '2.3'
111
111
  type: :runtime
112
112
  prerelease: false
113
113
  version_requirements: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: '2.0'
117
+ version: '2.3'
118
118
  - !ruby/object:Gem::Dependency
119
119
  name: kramdown-parser-gfm
120
120
  requirement: !ruby/object:Gem::Requirement
@@ -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