danger 8.4.4 → 8.6.0
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.
- checksums.yaml +4 -4
- data/lib/danger/ci_source/codefresh.rb +7 -13
- data/lib/danger/ci_source/local_git_repo.rb +29 -37
- data/lib/danger/ci_source/local_only_git_repo.rb +4 -8
- data/lib/danger/ci_source/support/pull_request_finder.rb +33 -37
- data/lib/danger/comment_generators/vsts_inline.md.erb +17 -0
- data/lib/danger/danger_core/plugins/dangerfile_git_plugin.rb +1 -1
- data/lib/danger/helpers/comments_helper.rb +3 -2
- data/lib/danger/request_sources/bitbucket_cloud.rb +0 -1
- data/lib/danger/request_sources/bitbucket_cloud_api.rb +3 -3
- data/lib/danger/request_sources/bitbucket_server.rb +0 -1
- data/lib/danger/request_sources/bitbucket_server_api.rb +3 -4
- data/lib/danger/request_sources/code_insights_api.rb +2 -3
- data/lib/danger/request_sources/github/github.rb +15 -24
- data/lib/danger/request_sources/gitlab.rb +8 -18
- data/lib/danger/request_sources/local_only.rb +1 -2
- data/lib/danger/request_sources/request_source.rb +16 -4
- data/lib/danger/request_sources/vsts.rb +171 -9
- data/lib/danger/request_sources/vsts_api.rb +34 -3
- data/lib/danger/scm_source/git_repo.rb +2 -1
- data/lib/danger/version.rb +1 -1
- metadata +8 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b2a439e378d52312f7b772f1df4ee246d61aaede6b725578e9328e50a6d3a13
|
4
|
+
data.tar.gz: 1728ce2347569a956fe415ae3d9553f3606fd4272054e359947f11aff280ccd5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 81094ad713b156bb548ce7e132ca31e6a01c9a280c8a5ac7faa6a6f79575750a23fde0d0331c9e3a98f049e4dfd32c08cda886faf01ccd09dcfdf68101fa1765
|
7
|
+
data.tar.gz: ac33a566081b19995683e314a47d47513a4b4446634250e3526db681aa5d955f2228d87a989a81c24cb907570ce1c2b8eddf794c7e5cf4f33c641ba04c8db5e0
|
@@ -31,23 +31,17 @@ module Danger
|
|
31
31
|
@supported_request_sources ||= [Danger::RequestSources::GitHub]
|
32
32
|
end
|
33
33
|
|
34
|
-
def
|
35
|
-
return "" if
|
36
|
-
return "" if
|
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
|
-
|
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
|
-
|
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
|
-
@
|
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 :
|
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
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
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"
|
13
|
-
BASE_VAR = "DANGER_LOCAL_BASE"
|
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: ""
|
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
|
-
|
18
|
-
|
19
|
-
|
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
|
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
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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+"
|
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
|
92
|
-
|
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/"
|
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 %>")
|
@@ -128,7 +128,7 @@ module Danger
|
|
128
128
|
# @return [Git::Diff::DiffFile] from the gem `git`
|
129
129
|
#
|
130
130
|
def diff_for_file(file)
|
131
|
-
(added_files + modified_files).include?(file) ? @git.diff[file] : nil
|
131
|
+
(added_files + modified_files + deleted_files).include?(file) ? @git.diff[file] : nil
|
132
132
|
end
|
133
133
|
|
134
134
|
# @!group Git Metadata
|
@@ -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')} "
|
@@ -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
|
-
|
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"]
|
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.
|
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 =
|
34
|
-
@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? ||
|
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
|
-
@
|
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
|
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 =
|
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: [])
|
@@ -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"
|
6
|
+
DANGER_REPO_NAME = "danger"
|
5
7
|
|
6
|
-
attr_accessor :ci_source, :
|
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::"
|
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(', '
|
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
|
-
|
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(
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
-
|
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
|
data/lib/danger/version.rb
CHANGED
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.
|
4
|
+
version: 8.6.0
|
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: 2022-
|
12
|
+
date: 2022-04-16 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
|
@@ -327,7 +328,7 @@ homepage: https://github.com/danger/danger
|
|
327
328
|
licenses:
|
328
329
|
- MIT
|
329
330
|
metadata: {}
|
330
|
-
post_install_message:
|
331
|
+
post_install_message:
|
331
332
|
rdoc_options: []
|
332
333
|
require_paths:
|
333
334
|
- lib
|
@@ -342,8 +343,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
342
343
|
- !ruby/object:Gem::Version
|
343
344
|
version: '0'
|
344
345
|
requirements: []
|
345
|
-
|
346
|
-
|
346
|
+
rubyforge_project:
|
347
|
+
rubygems_version: 2.7.6.2
|
348
|
+
signing_key:
|
347
349
|
specification_version: 4
|
348
350
|
summary: Like Unit Tests, but for your Team Culture.
|
349
351
|
test_files: []
|