danger 8.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +94 -0
- data/bin/danger +5 -0
- data/lib/assets/DangerfileTemplate +13 -0
- data/lib/danger.rb +44 -0
- data/lib/danger/ci_source/appcenter.rb +55 -0
- data/lib/danger/ci_source/appveyor.rb +60 -0
- data/lib/danger/ci_source/azure_pipelines.rb +44 -0
- data/lib/danger/ci_source/bamboo.rb +41 -0
- data/lib/danger/ci_source/bitbucket_pipelines.rb +37 -0
- data/lib/danger/ci_source/bitrise.rb +65 -0
- data/lib/danger/ci_source/buddybuild.rb +62 -0
- data/lib/danger/ci_source/buildkite.rb +51 -0
- data/lib/danger/ci_source/ci_source.rb +37 -0
- data/lib/danger/ci_source/circle.rb +94 -0
- data/lib/danger/ci_source/circle_api.rb +51 -0
- data/lib/danger/ci_source/cirrus.rb +31 -0
- data/lib/danger/ci_source/code_build.rb +57 -0
- data/lib/danger/ci_source/codefresh.rb +53 -0
- data/lib/danger/ci_source/codeship.rb +44 -0
- data/lib/danger/ci_source/dotci.rb +52 -0
- data/lib/danger/ci_source/drone.rb +71 -0
- data/lib/danger/ci_source/github_actions.rb +43 -0
- data/lib/danger/ci_source/gitlab_ci.rb +86 -0
- data/lib/danger/ci_source/jenkins.rb +149 -0
- data/lib/danger/ci_source/local_git_repo.rb +119 -0
- data/lib/danger/ci_source/local_only_git_repo.rb +47 -0
- data/lib/danger/ci_source/screwdriver.rb +47 -0
- data/lib/danger/ci_source/semaphore.rb +37 -0
- data/lib/danger/ci_source/support/commits.rb +17 -0
- data/lib/danger/ci_source/support/find_repo_info_from_logs.rb +35 -0
- data/lib/danger/ci_source/support/find_repo_info_from_url.rb +42 -0
- data/lib/danger/ci_source/support/local_pull_request.rb +14 -0
- data/lib/danger/ci_source/support/no_pull_request.rb +7 -0
- data/lib/danger/ci_source/support/no_repo_info.rb +5 -0
- data/lib/danger/ci_source/support/pull_request_finder.rb +179 -0
- data/lib/danger/ci_source/support/remote_pull_request.rb +15 -0
- data/lib/danger/ci_source/support/repo_info.rb +10 -0
- data/lib/danger/ci_source/surf.rb +37 -0
- data/lib/danger/ci_source/teamcity.rb +159 -0
- data/lib/danger/ci_source/travis.rb +51 -0
- data/lib/danger/ci_source/vsts.rb +73 -0
- data/lib/danger/ci_source/xcode_server.rb +48 -0
- data/lib/danger/clients/rubygems_client.rb +14 -0
- data/lib/danger/commands/dangerfile/gem.rb +43 -0
- data/lib/danger/commands/dangerfile/init.rb +30 -0
- data/lib/danger/commands/dry_run.rb +54 -0
- data/lib/danger/commands/init.rb +297 -0
- data/lib/danger/commands/init_helpers/interviewer.rb +92 -0
- data/lib/danger/commands/local.rb +83 -0
- data/lib/danger/commands/local_helpers/http_cache.rb +36 -0
- data/lib/danger/commands/local_helpers/local_setup.rb +46 -0
- data/lib/danger/commands/local_helpers/pry_setup.rb +31 -0
- data/lib/danger/commands/plugins/plugin_json.rb +46 -0
- data/lib/danger/commands/plugins/plugin_lint.rb +54 -0
- data/lib/danger/commands/plugins/plugin_readme.rb +45 -0
- data/lib/danger/commands/pr.rb +92 -0
- data/lib/danger/commands/runner.rb +94 -0
- data/lib/danger/commands/staging.rb +53 -0
- data/lib/danger/commands/systems.rb +43 -0
- data/lib/danger/comment_generators/bitbucket_server.md.erb +20 -0
- data/lib/danger/comment_generators/bitbucket_server_inline.md.erb +15 -0
- data/lib/danger/comment_generators/bitbucket_server_message_group.md.erb +12 -0
- data/lib/danger/comment_generators/github.md.erb +55 -0
- data/lib/danger/comment_generators/github_inline.md.erb +26 -0
- data/lib/danger/comment_generators/gitlab.md.erb +40 -0
- data/lib/danger/comment_generators/gitlab_inline.md.erb +26 -0
- data/lib/danger/comment_generators/vsts.md.erb +20 -0
- data/lib/danger/core_ext/file_list.rb +18 -0
- data/lib/danger/core_ext/string.rb +20 -0
- data/lib/danger/danger_core/dangerfile.rb +341 -0
- data/lib/danger/danger_core/dangerfile_dsl.rb +29 -0
- data/lib/danger/danger_core/dangerfile_generator.rb +11 -0
- data/lib/danger/danger_core/environment_manager.rb +123 -0
- data/lib/danger/danger_core/executor.rb +92 -0
- data/lib/danger/danger_core/message_aggregator.rb +49 -0
- data/lib/danger/danger_core/message_group.rb +68 -0
- data/lib/danger/danger_core/messages/base.rb +56 -0
- data/lib/danger/danger_core/messages/markdown.rb +42 -0
- data/lib/danger/danger_core/messages/violation.rb +54 -0
- data/lib/danger/danger_core/plugins/dangerfile_bitbucket_cloud_plugin.rb +144 -0
- data/lib/danger/danger_core/plugins/dangerfile_bitbucket_server_plugin.rb +211 -0
- data/lib/danger/danger_core/plugins/dangerfile_danger_plugin.rb +248 -0
- data/lib/danger/danger_core/plugins/dangerfile_git_plugin.rb +158 -0
- data/lib/danger/danger_core/plugins/dangerfile_github_plugin.rb +254 -0
- data/lib/danger/danger_core/plugins/dangerfile_gitlab_plugin.rb +240 -0
- data/lib/danger/danger_core/plugins/dangerfile_local_only_plugin.rb +42 -0
- data/lib/danger/danger_core/plugins/dangerfile_messaging_plugin.rb +218 -0
- data/lib/danger/danger_core/plugins/dangerfile_vsts_plugin.rb +191 -0
- data/lib/danger/danger_core/standard_error.rb +143 -0
- data/lib/danger/helpers/array_subclass.rb +61 -0
- data/lib/danger/helpers/comment.rb +32 -0
- data/lib/danger/helpers/comments_helper.rb +178 -0
- data/lib/danger/helpers/comments_parsing_helper.rb +70 -0
- data/lib/danger/helpers/emoji_mapper.rb +41 -0
- data/lib/danger/helpers/find_max_num_violations.rb +31 -0
- data/lib/danger/helpers/message_groups_array_helper.rb +31 -0
- data/lib/danger/plugin_support/gems_resolver.rb +77 -0
- data/lib/danger/plugin_support/plugin.rb +49 -0
- data/lib/danger/plugin_support/plugin_file_resolver.rb +30 -0
- data/lib/danger/plugin_support/plugin_linter.rb +161 -0
- data/lib/danger/plugin_support/plugin_parser.rb +199 -0
- data/lib/danger/plugin_support/templates/readme_table.html.erb +26 -0
- data/lib/danger/request_sources/bitbucket_cloud.rb +171 -0
- data/lib/danger/request_sources/bitbucket_cloud_api.rb +181 -0
- data/lib/danger/request_sources/bitbucket_server.rb +105 -0
- data/lib/danger/request_sources/bitbucket_server_api.rb +117 -0
- data/lib/danger/request_sources/github/github.rb +530 -0
- data/lib/danger/request_sources/github/github_review.rb +126 -0
- data/lib/danger/request_sources/github/github_review_resolver.rb +19 -0
- data/lib/danger/request_sources/github/github_review_unsupported.rb +25 -0
- data/lib/danger/request_sources/gitlab.rb +525 -0
- data/lib/danger/request_sources/local_only.rb +53 -0
- data/lib/danger/request_sources/request_source.rb +85 -0
- data/lib/danger/request_sources/support/get_ignored_violation.rb +17 -0
- data/lib/danger/request_sources/vsts.rb +118 -0
- data/lib/danger/request_sources/vsts_api.rb +138 -0
- data/lib/danger/scm_source/git_repo.rb +181 -0
- data/lib/danger/version.rb +4 -0
- metadata +339 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require "danger/helpers/comments_helper"
|
4
|
+
require "danger/helpers/comment"
|
5
|
+
|
6
|
+
module Danger
|
7
|
+
module RequestSources
|
8
|
+
class LocalOnly < RequestSource
|
9
|
+
include Danger::Helpers::CommentsHelper
|
10
|
+
attr_accessor :mr_json, :commits_json
|
11
|
+
|
12
|
+
def self.env_vars
|
13
|
+
["DANGER_LOCAL_ONLY"]
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(ci_source, environment)
|
17
|
+
self.ci_source = ci_source
|
18
|
+
self.environment = environment
|
19
|
+
end
|
20
|
+
|
21
|
+
def validates_as_ci?
|
22
|
+
true
|
23
|
+
end
|
24
|
+
|
25
|
+
def validates_as_api_source?
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
def scm
|
30
|
+
@scm ||= GitRepo.new
|
31
|
+
end
|
32
|
+
|
33
|
+
def setup_danger_branches
|
34
|
+
# Check that discovered values really exists
|
35
|
+
[ci_source.base_commit, ci_source.head_commit].each do |commit|
|
36
|
+
raise "Specified commit '#{commit}' not found" if scm.exec("rev-parse --quiet --verify #{commit}").empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
self.scm.exec "branch #{EnvironmentManager.danger_base_branch} #{ci_source.base_commit}"
|
40
|
+
self.scm.exec "branch #{EnvironmentManager.danger_head_branch} #{ci_source.head_commit}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def fetch_details; end
|
44
|
+
|
45
|
+
def update_pull_request!(_hash_needed); end
|
46
|
+
|
47
|
+
# @return [String] The organisation name, is nil if it can't be detected
|
48
|
+
def organisation
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Danger
|
2
|
+
module RequestSources
|
3
|
+
class RequestSource
|
4
|
+
DANGER_REPO_NAME = "danger".freeze
|
5
|
+
|
6
|
+
attr_accessor :ci_source, :environment, :scm, :host, :ignored_violations
|
7
|
+
|
8
|
+
def self.env_vars
|
9
|
+
raise "Subclass and overwrite self.env_vars"
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.optional_env_vars
|
13
|
+
[]
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.inherited(child_class)
|
17
|
+
available_request_sources.add child_class
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.available_request_sources
|
22
|
+
@available_request_sources ||= Set.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.source_name
|
26
|
+
to_s.sub("Danger::RequestSources::".freeze, "".freeze)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.available_source_names_and_envs
|
30
|
+
available_request_sources.map do |klass|
|
31
|
+
" - #{klass.source_name}: #{klass.env_vars.join(', '.freeze).yellow}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(_ci_source, _environment)
|
36
|
+
raise "Subclass and overwrite initialize"
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Boolean] whether scm.origins is a valid git repository or not
|
40
|
+
def validates_as_ci?
|
41
|
+
!!self.scm.origins.match(%r{#{Regexp.escape self.host}(:|/)(.+/.+?)(?:\.git)?$})
|
42
|
+
end
|
43
|
+
|
44
|
+
def validates_as_api_source?
|
45
|
+
raise "Subclass and overwrite validates_as_api_source?"
|
46
|
+
end
|
47
|
+
|
48
|
+
def scm
|
49
|
+
@scm ||= nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def host
|
53
|
+
@host ||= nil
|
54
|
+
end
|
55
|
+
|
56
|
+
def ignored_violations
|
57
|
+
@ignored_violations ||= []
|
58
|
+
end
|
59
|
+
|
60
|
+
def update_pull_request!(_warnings: [], _errors: [], _messages: [], _markdowns: [])
|
61
|
+
raise "Subclass and overwrite update_pull_request!"
|
62
|
+
end
|
63
|
+
|
64
|
+
def setup_danger_branches
|
65
|
+
raise "Subclass and overwrite setup_danger_branches"
|
66
|
+
end
|
67
|
+
|
68
|
+
def fetch_details
|
69
|
+
raise "Subclass and overwrite initialize"
|
70
|
+
end
|
71
|
+
|
72
|
+
def organisation
|
73
|
+
raise "Subclass and overwrite organisation"
|
74
|
+
end
|
75
|
+
|
76
|
+
def file_url(_organisation: nil, _repository: nil, _branch: "master", _path: nil)
|
77
|
+
raise "Subclass and overwrite file_url"
|
78
|
+
end
|
79
|
+
|
80
|
+
def update_build_status(status)
|
81
|
+
raise "Subclass and overwrite update_build_status"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class GetIgnoredViolation
|
2
|
+
IGNORE_REGEXP = />*\s*danger\s*:\s*ignore\s*"(?<error>[^"]*)"/i
|
3
|
+
|
4
|
+
def initialize(body)
|
5
|
+
@body = body
|
6
|
+
end
|
7
|
+
|
8
|
+
def call
|
9
|
+
return [] unless body
|
10
|
+
|
11
|
+
body.chomp.scan(IGNORE_REGEXP).flatten
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_reader :body
|
17
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require "danger/helpers/comments_helper"
|
4
|
+
require "danger/request_sources/vsts_api"
|
5
|
+
|
6
|
+
module Danger
|
7
|
+
module RequestSources
|
8
|
+
class VSTS < RequestSource
|
9
|
+
include Danger::Helpers::CommentsHelper
|
10
|
+
attr_accessor :pr_json
|
11
|
+
|
12
|
+
def self.env_vars
|
13
|
+
[
|
14
|
+
"DANGER_VSTS_API_TOKEN",
|
15
|
+
"DANGER_VSTS_HOST"
|
16
|
+
]
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.optional_env_vars
|
20
|
+
[
|
21
|
+
"DANGER_VSTS_API_VERSION"
|
22
|
+
]
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(ci_source, environment)
|
26
|
+
self.ci_source = ci_source
|
27
|
+
self.environment = environment
|
28
|
+
|
29
|
+
@is_vsts_git = environment["BUILD_REPOSITORY_PROVIDER"] == "TfsGit"
|
30
|
+
|
31
|
+
project, slug = ci_source.repo_slug.split("/")
|
32
|
+
@api = VSTSAPI.new(project, slug, ci_source.pull_request_id, environment)
|
33
|
+
end
|
34
|
+
|
35
|
+
def validates_as_ci?
|
36
|
+
@is_vsts_git
|
37
|
+
end
|
38
|
+
|
39
|
+
def validates_as_api_source?
|
40
|
+
@api.credentials_given?
|
41
|
+
end
|
42
|
+
|
43
|
+
def scm
|
44
|
+
@scm ||= GitRepo.new
|
45
|
+
end
|
46
|
+
|
47
|
+
def host
|
48
|
+
@host ||= @api.host
|
49
|
+
end
|
50
|
+
|
51
|
+
def fetch_details
|
52
|
+
self.pr_json = @api.fetch_pr_json
|
53
|
+
end
|
54
|
+
|
55
|
+
def setup_danger_branches
|
56
|
+
base_branch = self.pr_json[:targetRefName].sub("refs/heads/", "")
|
57
|
+
base_commit = self.pr_json[:lastMergeTargetCommit][:commitId]
|
58
|
+
head_branch = self.pr_json[:sourceRefName].sub("refs/heads/", "")
|
59
|
+
head_commit = self.pr_json[:lastMergeSourceCommit][:commitId]
|
60
|
+
|
61
|
+
# Next, we want to ensure that we have a version of the current branch at a known location
|
62
|
+
scm.ensure_commitish_exists_on_branch! base_branch, base_commit
|
63
|
+
self.scm.exec "branch #{EnvironmentManager.danger_base_branch} #{base_commit}"
|
64
|
+
|
65
|
+
# OK, so we want to ensure that we have a known head branch, this will always represent
|
66
|
+
# the head of the PR ( e.g. the most recent commit that will be merged. )
|
67
|
+
scm.ensure_commitish_exists_on_branch! head_branch, head_commit
|
68
|
+
self.scm.exec "branch #{EnvironmentManager.danger_head_branch} #{head_commit}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def organisation
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
|
75
|
+
def update_pull_request!(warnings: [], errors: [], messages: [], markdowns: [], danger_id: "danger", new_comment: false, remove_previous_comments: false)
|
76
|
+
unless @api.supports_comments?
|
77
|
+
return
|
78
|
+
end
|
79
|
+
|
80
|
+
comment = generate_description(warnings: warnings, errors: errors)
|
81
|
+
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")
|
89
|
+
if new_comment || remove_previous_comments
|
90
|
+
post_new_comment(comment)
|
91
|
+
else
|
92
|
+
update_old_comment(comment, danger_id: danger_id)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def post_new_comment(comment)
|
97
|
+
@api.post_comment(comment)
|
98
|
+
end
|
99
|
+
|
100
|
+
def update_old_comment(new_comment, danger_id: "danger")
|
101
|
+
comment_updated = false
|
102
|
+
@api.fetch_last_comments.each do |c|
|
103
|
+
thread_id = c[:id]
|
104
|
+
comment = c[:comments].first
|
105
|
+
comment_id = comment[:id]
|
106
|
+
comment_content = comment[:content].nil? ? "" : comment[:content]
|
107
|
+
# Skip the comment if it wasn't posted by danger
|
108
|
+
next unless comment_content.include?("generated_by_#{danger_id}")
|
109
|
+
# Updated the danger posted comment
|
110
|
+
@api.update_comment(thread_id, comment_id, new_comment)
|
111
|
+
comment_updated = true
|
112
|
+
end
|
113
|
+
# If no comment was updated, post a new one
|
114
|
+
post_new_comment(new_comment) unless comment_updated
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require "base64"
|
4
|
+
require "danger/helpers/comments_helper"
|
5
|
+
|
6
|
+
module Danger
|
7
|
+
module RequestSources
|
8
|
+
class VSTSAPI
|
9
|
+
attr_accessor :host, :pr_api_endpoint, :min_api_version_for_comments
|
10
|
+
|
11
|
+
def initialize(_project, slug, pull_request_id, environment)
|
12
|
+
self.min_api_version_for_comments = "3.0"
|
13
|
+
|
14
|
+
user_name = ""
|
15
|
+
personal_access_token = environment["DANGER_VSTS_API_TOKEN"]
|
16
|
+
|
17
|
+
@token = Base64.strict_encode64("#{user_name}:#{personal_access_token}")
|
18
|
+
@api_version = environment["DANGER_VSTS_API_VERSION"] ||= self.min_api_version_for_comments
|
19
|
+
|
20
|
+
self.host = environment["DANGER_VSTS_HOST"]
|
21
|
+
if self.host && !(self.host.include? "http://") && !(self.host.include? "https://")
|
22
|
+
self.host = "https://" + self.host
|
23
|
+
end
|
24
|
+
|
25
|
+
self.pr_api_endpoint = "#{host}/_apis/git/repositories/#{slug}/pullRequests/#{pull_request_id}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def supports_comments?
|
29
|
+
major_version = @api_version.split(".").first.to_i
|
30
|
+
minimun_version_for_comments = self.min_api_version_for_comments.split(".").first.to_i
|
31
|
+
|
32
|
+
major_version >= minimun_version_for_comments
|
33
|
+
end
|
34
|
+
|
35
|
+
def inspect
|
36
|
+
inspected = super
|
37
|
+
|
38
|
+
if @token
|
39
|
+
inspected = inspected.sub! @token, "********".freeze
|
40
|
+
end
|
41
|
+
|
42
|
+
inspected
|
43
|
+
end
|
44
|
+
|
45
|
+
def credentials_given?
|
46
|
+
@token && !@token.empty?
|
47
|
+
end
|
48
|
+
|
49
|
+
def fetch_pr_json
|
50
|
+
uri = URI("#{pr_api_endpoint}?api-version=#{@api_version}")
|
51
|
+
fetch_json(uri)
|
52
|
+
end
|
53
|
+
|
54
|
+
def fetch_last_comments
|
55
|
+
uri = URI("#{pr_api_endpoint}/threads?api-version=#{@api_version}")
|
56
|
+
fetch_json(uri)[:value]
|
57
|
+
end
|
58
|
+
|
59
|
+
def post_comment(text)
|
60
|
+
uri = URI("#{pr_api_endpoint}/threads?api-version=#{@api_version}")
|
61
|
+
body = {
|
62
|
+
"comments" => [
|
63
|
+
{
|
64
|
+
"parentCommentId" => 0,
|
65
|
+
"content" => text,
|
66
|
+
"commentType" => 1
|
67
|
+
}
|
68
|
+
],
|
69
|
+
"properties" => {
|
70
|
+
"Microsoft.TeamFoundation.Discussion.SupportsMarkdown" => {
|
71
|
+
"type" => "System.Int32",
|
72
|
+
"value" => 1
|
73
|
+
}
|
74
|
+
},
|
75
|
+
"status" => 1
|
76
|
+
}.to_json
|
77
|
+
post(uri, body)
|
78
|
+
end
|
79
|
+
|
80
|
+
def update_comment(thread, id, new_comment)
|
81
|
+
uri = URI("#{pr_api_endpoint}/threads/#{thread}/comments/#{id}?api-version=#{@api_version}")
|
82
|
+
body = {
|
83
|
+
"content" => new_comment
|
84
|
+
}.to_json
|
85
|
+
patch(uri, body)
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def use_ssl
|
91
|
+
return self.pr_api_endpoint.include? "https://"
|
92
|
+
end
|
93
|
+
|
94
|
+
def fetch_json(uri)
|
95
|
+
req = Net::HTTP::Get.new(uri.request_uri, { "Content-Type" => "application/json", "Authorization" => "Basic #{@token}" })
|
96
|
+
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: use_ssl) do |http|
|
97
|
+
http.request(req)
|
98
|
+
end
|
99
|
+
JSON.parse(res.body, symbolize_names: true)
|
100
|
+
end
|
101
|
+
|
102
|
+
def post(uri, body)
|
103
|
+
req = Net::HTTP::Post.new(uri.request_uri, { "Content-Type" => "application/json", "Authorization" => "Basic #{@token}" })
|
104
|
+
req.body = body
|
105
|
+
|
106
|
+
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: use_ssl) do |http|
|
107
|
+
http.request(req)
|
108
|
+
end
|
109
|
+
|
110
|
+
# show error to the user when VSTS returned an error
|
111
|
+
case res
|
112
|
+
when Net::HTTPClientError, Net::HTTPServerError
|
113
|
+
# HTTP 4xx - 5xx
|
114
|
+
abort "\nError posting comment to VSTS: #{res.code} (#{res.message})\n\n"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def patch(uri, body)
|
119
|
+
puts uri
|
120
|
+
puts body
|
121
|
+
|
122
|
+
req = Net::HTTP::Patch.new(uri.request_uri, { "Content-Type" => "application/json", "Authorization" => "Basic #{@token}" })
|
123
|
+
req.body = body
|
124
|
+
|
125
|
+
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: use_ssl) do |http|
|
126
|
+
http.request(req)
|
127
|
+
end
|
128
|
+
|
129
|
+
# show error to the user when VSTS returned an error
|
130
|
+
case res
|
131
|
+
when Net::HTTPClientError, Net::HTTPServerError
|
132
|
+
# HTTP 4xx - 5xx
|
133
|
+
abort "\nError updating comment on VSTS: #{res.code} (#{res.message})\n\n"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
# For more info see: https://github.com/schacon/ruby-git
|
2
|
+
|
3
|
+
require "git"
|
4
|
+
|
5
|
+
module Danger
|
6
|
+
class GitRepo
|
7
|
+
attr_accessor :diff, :log, :folder
|
8
|
+
|
9
|
+
def diff_for_folder(folder, from: "master", to: "HEAD", lookup_top_level: false)
|
10
|
+
self.folder = folder
|
11
|
+
git_top_level = folder
|
12
|
+
if lookup_top_level
|
13
|
+
Dir.chdir(folder) do
|
14
|
+
git_top_level = exec("rev-parse --show-toplevel")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
repo = Git.open git_top_level
|
18
|
+
|
19
|
+
ensure_commitish_exists!(from)
|
20
|
+
ensure_commitish_exists!(to)
|
21
|
+
|
22
|
+
merge_base = find_merge_base(repo, from, to)
|
23
|
+
commits_in_branch_count = commits_in_branch_count(from, to)
|
24
|
+
|
25
|
+
self.diff = repo.diff(merge_base, to)
|
26
|
+
self.log = repo.log(commits_in_branch_count).between(from, to)
|
27
|
+
end
|
28
|
+
|
29
|
+
def renamed_files
|
30
|
+
# Get raw diff with --find-renames --diff-filter
|
31
|
+
# We need to pass --find-renames cause
|
32
|
+
# older versions of git don't use this flag as default
|
33
|
+
diff = exec(
|
34
|
+
"diff #{self.diff.from} #{self.diff.to} --find-renames --diff-filter=R"
|
35
|
+
).lines.map { |line| line.tr("\n", "") }
|
36
|
+
|
37
|
+
before_name_regexp = /^rename from (.*)$/
|
38
|
+
after_name_regexp = /^rename to (.*)$/
|
39
|
+
|
40
|
+
# Extract old and new paths via regexp
|
41
|
+
diff.each_with_index.map do |line, index|
|
42
|
+
before_match = line.match(before_name_regexp)
|
43
|
+
next unless before_match
|
44
|
+
|
45
|
+
after_match = diff.fetch(index + 1, "").match(after_name_regexp)
|
46
|
+
next unless after_match
|
47
|
+
|
48
|
+
{
|
49
|
+
before: before_match.captures.first,
|
50
|
+
after: after_match.captures.first
|
51
|
+
}
|
52
|
+
end.compact
|
53
|
+
end
|
54
|
+
|
55
|
+
def exec(string)
|
56
|
+
require "open3"
|
57
|
+
Dir.chdir(self.folder || ".") do
|
58
|
+
Open3.popen2(default_env, "git #{string}") do |_stdin, stdout, _wait_thr|
|
59
|
+
stdout.read.rstrip
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def head_commit
|
65
|
+
exec("rev-parse HEAD")
|
66
|
+
end
|
67
|
+
|
68
|
+
def tags
|
69
|
+
exec("tag")
|
70
|
+
end
|
71
|
+
|
72
|
+
def origins
|
73
|
+
exec("remote show origin -n").lines.grep(/Fetch URL/)[0].split(": ", 2)[1].chomp
|
74
|
+
end
|
75
|
+
|
76
|
+
def ensure_commitish_exists!(commitish)
|
77
|
+
return ensure_commitish_exists_on_branch!(commitish, commitish) if commit_is_ref?(commitish)
|
78
|
+
return if commit_exists?(commitish)
|
79
|
+
|
80
|
+
git_in_depth_fetch
|
81
|
+
raise_if_we_cannot_find_the_commit(commitish) if commit_not_exists?(commitish)
|
82
|
+
end
|
83
|
+
|
84
|
+
def ensure_commitish_exists_on_branch!(branch, commitish)
|
85
|
+
return if commit_exists?(commitish)
|
86
|
+
|
87
|
+
depth = 0
|
88
|
+
success =
|
89
|
+
(3..6).any? do |factor|
|
90
|
+
depth += Math.exp(factor).to_i
|
91
|
+
|
92
|
+
git_fetch_branch_to_depth(branch, depth)
|
93
|
+
commit_exists?(commitish)
|
94
|
+
end
|
95
|
+
|
96
|
+
return if success
|
97
|
+
|
98
|
+
git_in_depth_fetch
|
99
|
+
raise_if_we_cannot_find_the_commit(commitish) if commit_not_exists?(commitish)
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def git_in_depth_fetch
|
105
|
+
exec("fetch --depth 1000000")
|
106
|
+
end
|
107
|
+
|
108
|
+
def git_fetch_branch_to_depth(branch, depth)
|
109
|
+
exec("fetch --depth=#{depth} --prune origin +refs/heads/#{branch}:refs/remotes/origin/#{branch}")
|
110
|
+
end
|
111
|
+
|
112
|
+
def default_env
|
113
|
+
{ "LANG" => "en_US.UTF-8" }
|
114
|
+
end
|
115
|
+
|
116
|
+
def raise_if_we_cannot_find_the_commit(commitish)
|
117
|
+
raise "Commit #{commitish[0..7]} doesn't exist. Are you running `danger local/pr` against the correct repository? Also this usually happens when you rebase/reset and force-pushed."
|
118
|
+
end
|
119
|
+
|
120
|
+
def commit_exists?(sha1)
|
121
|
+
!commit_not_exists?(sha1)
|
122
|
+
end
|
123
|
+
|
124
|
+
def commit_not_exists?(sha1)
|
125
|
+
exec("rev-parse --quiet --verify #{sha1}^{commit}").empty?
|
126
|
+
end
|
127
|
+
|
128
|
+
def find_merge_base(repo, from, to)
|
129
|
+
possible_merge_base = possible_merge_base(repo, from, to)
|
130
|
+
return possible_merge_base if possible_merge_base
|
131
|
+
|
132
|
+
possible_merge_base = find_merge_base_with_incremental_fetch(repo, from, to)
|
133
|
+
return possible_merge_base if possible_merge_base
|
134
|
+
|
135
|
+
git_in_depth_fetch
|
136
|
+
possible_merge_base = possible_merge_base(repo, from, to)
|
137
|
+
|
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
|
+
|
140
|
+
possible_merge_base
|
141
|
+
end
|
142
|
+
|
143
|
+
def find_merge_base_with_incremental_fetch(repo, from, to)
|
144
|
+
from_is_ref = commit_is_ref?(from)
|
145
|
+
to_is_ref = commit_is_ref?(to)
|
146
|
+
|
147
|
+
return unless from_is_ref || to_is_ref
|
148
|
+
|
149
|
+
depth = 0
|
150
|
+
(3..6).any? do |factor|
|
151
|
+
depth += Math.exp(factor).to_i
|
152
|
+
|
153
|
+
git_fetch_branch_to_depth(from, depth) if from_is_ref
|
154
|
+
git_fetch_branch_to_depth(to, depth) if to_is_ref
|
155
|
+
possible_merge_base(repo, from, to)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def possible_merge_base(repo, from, to)
|
160
|
+
[repo.merge_base(from, to)].find { |base| commit_exists?(base) }
|
161
|
+
end
|
162
|
+
|
163
|
+
def commits_in_branch_count(from, to)
|
164
|
+
exec("rev-list #{from}..#{to} --count").to_i
|
165
|
+
end
|
166
|
+
|
167
|
+
def commit_is_ref?(commit)
|
168
|
+
/[a-f0-9]{5,40}/ !~ commit
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
module Git
|
174
|
+
class Base
|
175
|
+
# Use git-merge-base https://git-scm.com/docs/git-merge-base to
|
176
|
+
# find as good common ancestors as possible for a merge
|
177
|
+
def merge_base(commit1, commit2, *other_commits)
|
178
|
+
Open3.popen2("git", "merge-base", commit1, commit2, *other_commits) { |_stdin, stdout, _wait_thr| stdout.read.rstrip }
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|