danger 8.0.4
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 +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 +161 -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
|