danger 8.4.3 → 8.5.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: bcdadafd7f8d81e366907b31b4c0530258864a6f18aa22e801d4467bc1ab35b0
4
- data.tar.gz: 1b4e340420a10006b36a55bdbc30570f664c6c88fc2b8ad253f299e1aae30bd5
3
+ metadata.gz: c8d6272f0ed9095d67e585d262669d8d11705a8a5cce460c02122a9b826d5cd7
4
+ data.tar.gz: 42699026115d900c5e32f057e206697472be7ba80d6d1686e7eb07d5262100c6
5
5
  SHA512:
6
- metadata.gz: 8e5f2b3a004f5f205a672ddcef8bc6fc565e3b8d2ef20a14d4f3b9aca06a8d9f069e16e1369a63b14ad226c9f8f1b89b46fe7aa43b02be34d7da574245c7db55
7
- data.tar.gz: 2e5d5bdf35c5425e2c4012002f191df212d2cbc00a73e63eaa5012ab32340a60b3b39df7c9b1527ef70641c739232a4620545e9c478548f6a628ad993366aebd
6
+ metadata.gz: 3981a24f19fb7b63737b80435d8d2685fa187630aeb82edb28042c8d0db74f23412e588daf16732f045563cc52f9d85432682c76fe1913c4cde57d671b99cb8b
7
+ data.tar.gz: 4b7f3f4fb90975da2faa9cbdff6e5cb95e27ea06447fa95a749df6df77bec14de7c47c6e52fc2d98949e160cb4dd12a96d9babefec2798be8cdcd1cde77f0ebd
@@ -31,23 +31,17 @@ module Danger
31
31
  @supported_request_sources ||= [Danger::RequestSources::GitHub]
32
32
  end
33
33
 
34
- def repo_slug
35
- return "" if @env["CF_REPO_OWNER"].to_s.empty?
36
- return "" if @env["CF_REPO_NAME"].to_s.empty?
37
- "#{@env['CF_REPO_OWNER']}/#{@env['CF_REPO_NAME']}".downcase!
38
- end
39
-
40
- def repo_url
41
- return "" if @env["CF_COMMIT_URL"].to_s.empty?
42
- @env["CF_COMMIT_URL"].gsub(/\/commit.+$/, "")
43
- end
34
+ def self.slug_from(env)
35
+ return "" if env["CF_REPO_OWNER"].to_s.empty?
36
+ return "" if env["CF_REPO_NAME"].to_s.empty?
44
37
 
45
- def pull_request_id
46
- @env["CF_PULL_REQUEST_NUMBER"]
38
+ "#{env['CF_REPO_OWNER']}/#{env['CF_REPO_NAME']}".downcase!
47
39
  end
48
40
 
49
41
  def initialize(env)
50
- @env = env
42
+ self.repo_url = env["CF_COMMIT_URL"].to_s.gsub(/\/commit.+$/, "")
43
+ self.repo_slug = self.class.slug_from(env)
44
+ self.pull_request_id = env["CF_PULL_REQUEST_NUMBER"]
51
45
  end
52
46
  end
53
47
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # For more info see: https://github.com/schacon/ruby-git
2
3
 
3
4
  require "git"
@@ -37,8 +38,8 @@ module Danger
37
38
  end
38
39
 
39
40
  def initialize(env = {})
40
- @env = env
41
-
41
+ @remote_info = find_remote_info(env)
42
+ @found_pull_request = find_pull_request(env)
42
43
  self.repo_slug = remote_info.slug
43
44
  raise_error_for_missing_remote if remote_info.kind_of?(NoRepoInfo)
44
45
 
@@ -55,7 +56,7 @@ module Danger
55
56
 
56
57
  private
57
58
 
58
- attr_reader :env
59
+ attr_reader :remote_info, :found_pull_request
59
60
 
60
61
  def raise_error_for_missing_remote
61
62
  raise missing_remote_error_message
@@ -66,45 +67,36 @@ module Danger
66
67
  "And the repository must host on GitHub.com or GitHub Enterprise."
67
68
  end
68
69
 
69
- def remote_info
70
- @_remote_info ||= begin
71
- remote_info = begin
72
- if given_pull_request_url?
73
- FindRepoInfoFromURL.new(env["LOCAL_GIT_PR_URL"]).call
74
- else
75
- FindRepoInfoFromLogs.new(
76
- env["DANGER_GITHUB_HOST"] || "github.com".freeze,
77
- run_git("remote show origin -n".freeze)
78
- ).call
79
- end
80
- end
81
-
82
- remote_info || NoRepoInfo.new
83
- end
70
+ def find_remote_info(env)
71
+ if given_pull_request_url?(env)
72
+ FindRepoInfoFromURL.new(env["LOCAL_GIT_PR_URL"]).call
73
+ else
74
+ FindRepoInfoFromLogs.new(
75
+ env["DANGER_GITHUB_HOST"] || "github.com",
76
+ run_git("remote show origin -n")
77
+ ).call
78
+ end || NoRepoInfo.new
84
79
  end
85
80
 
86
- def found_pull_request
87
- @_found_pull_request ||= begin
88
- if given_pull_request_url?
89
- PullRequestFinder.new(
90
- remote_info.id,
91
- remote_info.slug,
92
- remote: true,
93
- remote_url: env["LOCAL_GIT_PR_URL"],
94
- env: env
95
- ).call
96
- else
97
- PullRequestFinder.new(
98
- env.fetch("LOCAL_GIT_PR_ID") { "".freeze },
99
- remote_info.slug,
100
- remote: false,
101
- git_logs: run_git("log --oneline -1000000".freeze)
102
- ).call
103
- end
81
+ def find_pull_request(env)
82
+ if given_pull_request_url?(env)
83
+ PullRequestFinder.new(
84
+ remote_info.id,
85
+ remote_info.slug,
86
+ remote: true,
87
+ remote_url: env["LOCAL_GIT_PR_URL"]
88
+ ).call(env: env)
89
+ else
90
+ PullRequestFinder.new(
91
+ env.fetch("LOCAL_GIT_PR_ID") { "" },
92
+ remote_info.slug,
93
+ remote: false,
94
+ git_logs: run_git("log --oneline -1000000")
95
+ ).call(env: env)
104
96
  end
105
97
  end
106
98
 
107
- def given_pull_request_url?
99
+ def given_pull_request_url?(env)
108
100
  env["LOCAL_GIT_PR_URL"] && !env["LOCAL_GIT_PR_URL"].empty?
109
101
  end
110
102
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "git"
2
4
  require "danger/request_sources/local_only"
3
5
 
@@ -9,8 +11,8 @@ module Danger
9
11
  #
10
12
  class LocalOnlyGitRepo < CI
11
13
  attr_accessor :base_commit, :head_commit
12
- HEAD_VAR = "DANGER_LOCAL_HEAD".freeze
13
- BASE_VAR = "DANGER_LOCAL_BASE".freeze
14
+ HEAD_VAR = "DANGER_LOCAL_HEAD"
15
+ BASE_VAR = "DANGER_LOCAL_BASE"
14
16
 
15
17
  def self.validates_as_ci?(env)
16
18
  env.key? "DANGER_USE_LOCAL_ONLY_GIT"
@@ -33,15 +35,9 @@ module Danger
33
35
  end
34
36
 
35
37
  def initialize(env = {})
36
- @env = env
37
-
38
38
  # expects --base/--head specified OR origin/master to be base and HEAD head
39
39
  self.base_commit = env[BASE_VAR] || run_git("rev-parse --abbrev-ref origin/master")
40
40
  self.head_commit = env[HEAD_VAR] || run_git("rev-parse --abbrev-ref HEAD")
41
41
  end
42
-
43
- private
44
-
45
- attr_reader :env
46
42
  end
47
43
  end
@@ -1,67 +1,65 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "danger/ci_source/support/local_pull_request"
2
4
  require "danger/ci_source/support/remote_pull_request"
3
5
  require "danger/ci_source/support/no_pull_request"
4
6
 
5
7
  module Danger
6
8
  class PullRequestFinder
7
- def initialize(specific_pull_request_id, repo_slug = nil, remote: false, git_logs: "", remote_url: "", env: nil)
9
+ def initialize(specific_pull_request_id, repo_slug = nil, remote: false, git_logs: "", remote_url: "")
8
10
  @specific_pull_request_id = specific_pull_request_id
9
11
  @git_logs = git_logs
10
12
  @repo_slug = repo_slug
11
13
  @remote = to_boolean(remote)
12
14
  @remote_url = remote_url
13
- @env = env
14
15
  end
15
16
 
16
- def call
17
- check_if_any_pull_request!
18
-
19
- pull_request
17
+ def call(env: nil)
18
+ find_pull_request(env).tap do |pull_request|
19
+ raise_pull_request_not_found!(pull_request) unless pull_request.valid?
20
+ end
20
21
  end
21
22
 
22
23
  private
23
24
 
24
- attr_reader :specific_pull_request_id, :git_logs, :repo_slug, :remote, :remote_url, :env
25
+ attr_reader :specific_pull_request_id, :git_logs, :repo_slug, :remote, :remote_url
25
26
 
26
27
  def to_boolean(maybe_string)
27
28
  ["true", "1", "yes", "y", true].include?(maybe_string)
28
29
  end
29
30
 
30
- def check_if_any_pull_request!
31
- unless pull_request.valid?
32
- if !specific_pull_request_id.empty?
33
- raise "Could not find the Pull Request (#{specific_pull_request_id}) inside the git history for this repo."
34
- else
35
- raise "No recent Pull Requests found for this repo, danger requires at least one Pull Request for the local mode.".freeze
36
- end
31
+ def raise_pull_request_not_found!(pull_request)
32
+ if specific_pull_request_id.empty?
33
+ raise "No recent Pull Requests found for this repo, danger requires at least one Pull Request for the local mode."
34
+ else
35
+ raise "Could not find the Pull Request (#{specific_pull_request_id}) inside the git history for this repo."
37
36
  end
38
37
  end
39
38
 
40
39
  # @return [String] Log line of most recent merged Pull Request
41
- def pull_request
42
- @pull_request ||= begin
43
- return if pull_request_ref.empty?
44
-
45
- if both_present?
46
- LocalPullRequest.new(pick_the_most_recent_one_from_two_matches)
47
- elsif only_merged_pull_request_present?
48
- LocalPullRequest.new(most_recent_merged_pull_request)
49
- elsif only_squash_and_merged_pull_request_present?
50
- LocalPullRequest.new(most_recent_squash_and_merged_pull_request)
51
- elsif remote && remote_pull_request
52
- generate_remote_pull_request
53
- else
54
- NoPullRequest.new
55
- end
40
+ def find_pull_request(env)
41
+ return if pull_request_ref.empty?
42
+
43
+ if both_present?
44
+ LocalPullRequest.new(pick_the_most_recent_one_from_two_matches)
45
+ elsif only_merged_pull_request_present?
46
+ LocalPullRequest.new(most_recent_merged_pull_request)
47
+ elsif only_squash_and_merged_pull_request_present?
48
+ LocalPullRequest.new(most_recent_squash_and_merged_pull_request)
49
+ elsif remote
50
+ remote_pull_request = find_remote_pull_request(env)
51
+ remote_pull_request ? generate_remote_pull_request(remote_pull_request) : NoPullRequest.new
52
+ else
53
+ NoPullRequest.new
56
54
  end
57
55
  end
58
56
 
59
57
  # @return [String] "#42"
60
58
  def pull_request_ref
61
- !specific_pull_request_id.empty? ? "##{specific_pull_request_id}" : "#\\d+".freeze
59
+ !specific_pull_request_id.empty? ? "##{specific_pull_request_id}" : "#\\d+"
62
60
  end
63
61
 
64
- def generate_remote_pull_request
62
+ def generate_remote_pull_request(remote_pull_request)
65
63
  scm_provider = find_scm_provider(remote_url)
66
64
 
67
65
  case scm_provider
@@ -88,10 +86,8 @@ module Danger
88
86
  end
89
87
  end
90
88
 
91
- def remote_pull_request
92
- @_remote_pull_request ||= begin
93
- client.pull_request(repo_slug, specific_pull_request_id)
94
- end
89
+ def find_remote_pull_request(env)
90
+ client(env).pull_request(repo_slug, specific_pull_request_id)
95
91
  end
96
92
 
97
93
  def both_present?
@@ -135,7 +131,7 @@ module Danger
135
131
  !most_recent_squash_and_merged_pull_request.nil? && !most_recent_squash_and_merged_pull_request.empty?
136
132
  end
137
133
 
138
- def client
134
+ def client(env)
139
135
  scm_provider = find_scm_provider(remote_url)
140
136
 
141
137
  case scm_provider
@@ -168,7 +164,7 @@ module Danger
168
164
  def api_url
169
165
  ENV.fetch("DANGER_GITHUB_API_HOST") do
170
166
  ENV.fetch("DANGER_GITHUB_API_BASE_URL") do
171
- "https://api.github.com/".freeze
167
+ "https://api.github.com/"
172
168
  end
173
169
  end
174
170
  end
@@ -0,0 +1,17 @@
1
+ <%- @tables.each do |table| -%>
2
+ <%- if table[:content].any? || table[:resolved].any? -%>
3
+ | | |
4
+ |---|---|
5
+ <%- table[:content].each do |violation| -%>
6
+ | <%= @emoji_mapper.map(table[:emoji]) %> | <%= "~~" if table[:resolved] %><%= violation.message %><%= "~~" if table[:resolved] %> |
7
+ <%- end -%>
8
+
9
+ <%- end -%>
10
+ <%- end -%>
11
+
12
+ <%- @markdowns.each do |current| -%>
13
+ <%= current %>
14
+ <%# the previous line has to be aligned far to the left, otherwise markdown can break easily %>
15
+ <%- end -%>
16
+
17
+ Generated by :no_entry_sign: [Danger](https://danger.systems/ "generated_by_<%= @danger_id %>")
@@ -289,7 +289,7 @@ module Danger
289
289
  # Push results to the API
290
290
  # Pass along the details of the run to the request source
291
291
  # to send back to the code review site.
292
- post_results(danger_id, new_comment, remove_previous_comments) unless danger_id.nil?
292
+ post_results(danger_id, new_comment, remove_previous_comments)
293
293
 
294
294
  # Print results in the terminal
295
295
  print_results
@@ -335,6 +335,9 @@ module Danger
335
335
  end
336
336
 
337
337
  def post_exception(ex, danger_id, new_comment)
338
+ return if ENV["DANGER_DO_NOT_POST_INVALID_DANGERFILE_ERROR"]
339
+ return if danger_id.nil?
340
+
338
341
  env.request_source.update_pull_request!(
339
342
  danger_id: danger_id,
340
343
  new_comment: new_comment,
@@ -24,7 +24,8 @@ module Danger
24
24
  # @param [Bool] Should hide any generated link created
25
25
  #
26
26
  # @return [String] The Markdown compatible link
27
- def markdown_link_to_message(message, _)
27
+ def markdown_link_to_message(message, hide_link)
28
+ return "" if hide_link
28
29
  "#{message.file}#L#{message.line}"
29
30
  end
30
31
 
@@ -147,7 +148,7 @@ module Danger
147
148
 
148
149
  def generate_description(warnings: nil, errors: nil, template: "github")
149
150
  emoji_mapper = EmojiMapper.new(template)
150
- if errors.empty? && warnings.empty?
151
+ if (errors.nil? || errors.empty?) && (warnings.nil? || warnings.empty?)
151
152
  return ENV['DANGER_SUCCESS_MESSAGE'] || "All green. #{random_compliment}"
152
153
  else
153
154
  message = "#{emoji_mapper.map('warning')} "
@@ -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
  @api = BitbucketCloudAPI.new(ci_source.repo_slug, ci_source.pull_request_id, nil, environment)
30
29
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # coding: utf-8
2
3
 
3
4
  require "danger/helpers/comments_helper"
@@ -32,9 +33,8 @@ module Danger
32
33
  def inspect
33
34
  inspected = super
34
35
 
35
- if @password
36
- inspected = inspected.sub! @password, "********".freeze
37
- end
36
+ inspected.gsub!(@password, "********") if @password
37
+ inspected.gsub!(@access_token, "********") if @access_token
38
38
 
39
39
  inspected
40
40
  end
@@ -31,7 +31,6 @@ module Danger
31
31
 
32
32
  def initialize(ci_source, environment)
33
33
  self.ci_source = ci_source
34
- self.environment = environment
35
34
 
36
35
  project, slug = ci_source.repo_slug.split("/")
37
36
  @api = BitbucketServerAPI.new(project, slug, ci_source.pull_request_id, environment)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # coding: utf-8
2
3
 
3
4
  require "openssl"
@@ -12,7 +13,7 @@ module Danger
12
13
  @username = environment["DANGER_BITBUCKETSERVER_USERNAME"]
13
14
  @password = environment["DANGER_BITBUCKETSERVER_PASSWORD"]
14
15
  self.host = environment["DANGER_BITBUCKETSERVER_HOST"]
15
- self.verify_ssl = environment["DANGER_BITBUCKETSERVER_VERIFY_SSL"] == "false" ? false : true
16
+ self.verify_ssl = environment["DANGER_BITBUCKETSERVER_VERIFY_SSL"] != "false"
16
17
  if self.host && !(self.host.include? "http://") && !(self.host.include? "https://")
17
18
  self.host = "https://" + self.host
18
19
  end
@@ -24,9 +25,7 @@ module Danger
24
25
  def inspect
25
26
  inspected = super
26
27
 
27
- if @password
28
- inspected = inspected.sub! @password, "********".freeze
29
- end
28
+ inspected.gsub!(@password, "********") if @password
30
29
 
31
30
  inspected
32
31
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # coding: utf-8
2
3
 
3
4
  module Danger
@@ -26,9 +27,7 @@ module Danger
26
27
  def inspect
27
28
  inspected = super
28
29
 
29
- if @password
30
- inspected = inspected.sub! @password, "********".freeze
31
- end
30
+ inspected.gsub!(@password, "********") if @password
32
31
 
33
32
  inspected
34
33
  end
@@ -14,7 +14,7 @@ module Danger
14
14
  class GitHub < RequestSource
15
15
  include Danger::Helpers::CommentsHelper
16
16
 
17
- attr_accessor :pr_json, :issue_json, :support_tokenless_auth, :dismiss_out_of_range_messages
17
+ attr_accessor :pr_json, :issue_json, :use_local_git, :support_tokenless_auth, :dismiss_out_of_range_messages, :host, :api_url, :verify_ssl
18
18
 
19
19
  def self.env_vars
20
20
  ["DANGER_GITHUB_API_TOKEN", "DANGER_GITHUB_BEARER_TOKEN"]
@@ -26,12 +26,22 @@ module Danger
26
26
 
27
27
  def initialize(ci_source, environment)
28
28
  self.ci_source = ci_source
29
- self.environment = environment
29
+ self.use_local_git = environment["DANGER_USE_LOCAL_GIT"]
30
30
  self.support_tokenless_auth = false
31
31
  self.dismiss_out_of_range_messages = false
32
+ self.host = environment.fetch("DANGER_GITHUB_HOST", "github.com")
33
+ # `DANGER_GITHUB_API_HOST` is the old name kept for legacy reasons and
34
+ # backwards compatibility. `DANGER_GITHUB_API_BASE_URL` is the new
35
+ # correctly named variable.
36
+ self.api_url = environment.fetch("DANGER_GITHUB_API_HOST") do
37
+ environment.fetch("DANGER_GITHUB_API_BASE_URL") do
38
+ "https://api.github.com/".freeze
39
+ end
40
+ end
41
+ self.verify_ssl = environment["DANGER_OCTOKIT_VERIFY_SSL"] != "false"
32
42
 
33
- @access_token = @environment["DANGER_GITHUB_API_TOKEN"]
34
- @bearer_token = @environment["DANGER_GITHUB_BEARER_TOKEN"]
43
+ @access_token = environment["DANGER_GITHUB_API_TOKEN"]
44
+ @bearer_token = environment["DANGER_GITHUB_BEARER_TOKEN"]
35
45
  end
36
46
 
37
47
  def get_pr_from_branch(repo_name, branch_name, owner)
@@ -46,32 +56,13 @@ module Danger
46
56
  end
47
57
 
48
58
  def validates_as_api_source?
49
- valid_bearer_token? || valid_access_token? || self.environment["DANGER_USE_LOCAL_GIT"]
59
+ valid_bearer_token? || valid_access_token? || use_local_git
50
60
  end
51
61
 
52
62
  def scm
53
63
  @scm ||= GitRepo.new
54
64
  end
55
65
 
56
- def host
57
- @host = @environment["DANGER_GITHUB_HOST"] || "github.com"
58
- end
59
-
60
- def verify_ssl
61
- @environment["DANGER_OCTOKIT_VERIFY_SSL"] == "false" ? false : true
62
- end
63
-
64
- # `DANGER_GITHUB_API_HOST` is the old name kept for legacy reasons and
65
- # backwards compatibility. `DANGER_GITHUB_API_BASE_URL` is the new
66
- # correctly named variable.
67
- def api_url
68
- @environment.fetch("DANGER_GITHUB_API_HOST") do
69
- @environment.fetch("DANGER_GITHUB_API_BASE_URL") do
70
- "https://api.github.com/".freeze
71
- end
72
- end
73
- end
74
-
75
66
  def client
76
67
  raise "No API token given, please provide one using `DANGER_GITHUB_API_TOKEN` or `DANGER_GITHUB_BEARER_TOKEN`" if !valid_access_token? && !valid_bearer_token? && !support_tokenless_auth
77
68
  @client ||= begin
@@ -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, :dismiss_out_of_range_messages
11
+ attr_accessor :mr_json, :commits_json, :dismiss_out_of_range_messages, :endpoint, :host
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")
@@ -23,20 +23,19 @@ module Danger
23
23
 
24
24
  def initialize(ci_source, environment)
25
25
  self.ci_source = ci_source
26
- self.environment = environment
27
26
  self.dismiss_out_of_range_messages = false
28
-
29
- @token = @environment["DANGER_GITLAB_API_TOKEN"]
27
+ @endpoint = environment["DANGER_GITLAB_API_BASE_URL"] || environment.fetch("CI_API_V4_URL", "https://gitlab.com/api/v4")
28
+ @host = environment.fetch("DANGER_GITLAB_HOST", URI.parse(endpoint).host) || "gitlab.com"
29
+ @token = environment["DANGER_GITLAB_API_TOKEN"]
30
30
  end
31
31
 
32
32
  def client
33
- token = @environment["DANGER_GITLAB_API_TOKEN"]
34
- raise "No API token given, please provide one using `DANGER_GITLAB_API_TOKEN`" unless token
33
+ raise "No API token given, please provide one using `DANGER_GITLAB_API_TOKEN`" unless @token
35
34
 
36
35
  # The require happens inline so that it won't cause exceptions when just using the `danger` gem.
37
36
  require "gitlab"
38
37
 
39
- @client ||= Gitlab.client(endpoint: endpoint, private_token: token)
38
+ @client ||= Gitlab.client(endpoint: endpoint, private_token: @token)
40
39
  rescue LoadError => e
41
40
  if e.path == "gitlab"
42
41
  puts "The GitLab gem was not installed, you will need to change your Gem from `danger` to `danger-gitlab`.".red
@@ -48,7 +47,7 @@ module Danger
48
47
  end
49
48
 
50
49
  def validates_as_ci?
51
- includes_port = self.host.include? ":"
50
+ includes_port = host.include? ":"
52
51
  raise "Port number included in `DANGER_GITLAB_HOST`, this will fail with GitLab CI Runners" if includes_port
53
52
 
54
53
  # We don't call super because in some cases the Git remote doesn't match the GitLab instance host.
@@ -66,14 +65,6 @@ module Danger
66
65
  @scm ||= GitRepo.new
67
66
  end
68
67
 
69
- def endpoint
70
- @endpoint ||= @environment["DANGER_GITLAB_API_BASE_URL"] || @environment["CI_API_V4_URL"] || "https://gitlab.com/api/v4"
71
- end
72
-
73
- def host
74
- @host ||= @environment["DANGER_GITLAB_HOST"] || URI.parse(endpoint).host || "gitlab.com"
75
- end
76
-
77
68
  def base_commit
78
69
  @base_commit ||= self.mr_json.diff_refs.base_sha
79
70
  end
@@ -326,11 +317,10 @@ module Danger
326
317
  # @return [String] A URL to the specific file, ready to be downloaded
327
318
  def file_url(organisation: nil, repository: nil, branch: nil, path: nil)
328
319
  branch ||= 'master'
329
- token = @environment["DANGER_GITLAB_API_TOKEN"]
330
320
  # According to GitLab Repositories API docs path and id(slug) should be encoded.
331
321
  path = URI.encode_www_form_component(path)
332
322
  repository = URI.encode_www_form_component(repository)
333
- "#{endpoint}/projects/#{repository}/repository/files/#{path}/raw?ref=#{branch}&private_token=#{token}"
323
+ "#{endpoint}/projects/#{repository}/repository/files/#{path}/raw?ref=#{branch}&private_token=#{@token}"
334
324
  end
335
325
 
336
326
  def regular_violations_group(warnings: [], errors: [], messages: [], markdowns: [])
@@ -13,9 +13,8 @@ module Danger
13
13
  ["DANGER_LOCAL_ONLY"]
14
14
  end
15
15
 
16
- def initialize(ci_source, environment)
16
+ def initialize(ci_source, _environment)
17
17
  self.ci_source = ci_source
18
- self.environment = environment
19
18
  end
20
19
 
21
20
  def validates_as_ci?
@@ -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 = {
@@ -1,4 +1,4 @@
1
1
  module Danger
2
- VERSION = "8.4.3".freeze
2
+ VERSION = "8.5.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.4.3
4
+ version: 8.5.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: 2022-02-25 00:00:00.000000000 Z
12
+ date: 2022-03-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: claide
@@ -271,6 +271,7 @@ files:
271
271
  - lib/danger/comment_generators/gitlab.md.erb
272
272
  - lib/danger/comment_generators/gitlab_inline.md.erb
273
273
  - lib/danger/comment_generators/vsts.md.erb
274
+ - lib/danger/comment_generators/vsts_inline.md.erb
274
275
  - lib/danger/core_ext/file_list.rb
275
276
  - lib/danger/core_ext/string.rb
276
277
  - lib/danger/danger_core/dangerfile.rb