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.
Files changed (121) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +94 -0
  4. data/bin/danger +5 -0
  5. data/lib/assets/DangerfileTemplate +13 -0
  6. data/lib/danger.rb +44 -0
  7. data/lib/danger/ci_source/appcenter.rb +55 -0
  8. data/lib/danger/ci_source/appveyor.rb +60 -0
  9. data/lib/danger/ci_source/azure_pipelines.rb +44 -0
  10. data/lib/danger/ci_source/bamboo.rb +41 -0
  11. data/lib/danger/ci_source/bitbucket_pipelines.rb +37 -0
  12. data/lib/danger/ci_source/bitrise.rb +65 -0
  13. data/lib/danger/ci_source/buddybuild.rb +62 -0
  14. data/lib/danger/ci_source/buildkite.rb +51 -0
  15. data/lib/danger/ci_source/ci_source.rb +37 -0
  16. data/lib/danger/ci_source/circle.rb +94 -0
  17. data/lib/danger/ci_source/circle_api.rb +51 -0
  18. data/lib/danger/ci_source/cirrus.rb +31 -0
  19. data/lib/danger/ci_source/code_build.rb +57 -0
  20. data/lib/danger/ci_source/codefresh.rb +53 -0
  21. data/lib/danger/ci_source/codeship.rb +44 -0
  22. data/lib/danger/ci_source/dotci.rb +52 -0
  23. data/lib/danger/ci_source/drone.rb +71 -0
  24. data/lib/danger/ci_source/github_actions.rb +43 -0
  25. data/lib/danger/ci_source/gitlab_ci.rb +86 -0
  26. data/lib/danger/ci_source/jenkins.rb +149 -0
  27. data/lib/danger/ci_source/local_git_repo.rb +119 -0
  28. data/lib/danger/ci_source/local_only_git_repo.rb +47 -0
  29. data/lib/danger/ci_source/screwdriver.rb +47 -0
  30. data/lib/danger/ci_source/semaphore.rb +37 -0
  31. data/lib/danger/ci_source/support/commits.rb +17 -0
  32. data/lib/danger/ci_source/support/find_repo_info_from_logs.rb +35 -0
  33. data/lib/danger/ci_source/support/find_repo_info_from_url.rb +42 -0
  34. data/lib/danger/ci_source/support/local_pull_request.rb +14 -0
  35. data/lib/danger/ci_source/support/no_pull_request.rb +7 -0
  36. data/lib/danger/ci_source/support/no_repo_info.rb +5 -0
  37. data/lib/danger/ci_source/support/pull_request_finder.rb +179 -0
  38. data/lib/danger/ci_source/support/remote_pull_request.rb +15 -0
  39. data/lib/danger/ci_source/support/repo_info.rb +10 -0
  40. data/lib/danger/ci_source/surf.rb +37 -0
  41. data/lib/danger/ci_source/teamcity.rb +161 -0
  42. data/lib/danger/ci_source/travis.rb +51 -0
  43. data/lib/danger/ci_source/vsts.rb +73 -0
  44. data/lib/danger/ci_source/xcode_server.rb +48 -0
  45. data/lib/danger/clients/rubygems_client.rb +14 -0
  46. data/lib/danger/commands/dangerfile/gem.rb +43 -0
  47. data/lib/danger/commands/dangerfile/init.rb +30 -0
  48. data/lib/danger/commands/dry_run.rb +54 -0
  49. data/lib/danger/commands/init.rb +297 -0
  50. data/lib/danger/commands/init_helpers/interviewer.rb +92 -0
  51. data/lib/danger/commands/local.rb +83 -0
  52. data/lib/danger/commands/local_helpers/http_cache.rb +36 -0
  53. data/lib/danger/commands/local_helpers/local_setup.rb +46 -0
  54. data/lib/danger/commands/local_helpers/pry_setup.rb +31 -0
  55. data/lib/danger/commands/plugins/plugin_json.rb +46 -0
  56. data/lib/danger/commands/plugins/plugin_lint.rb +54 -0
  57. data/lib/danger/commands/plugins/plugin_readme.rb +45 -0
  58. data/lib/danger/commands/pr.rb +92 -0
  59. data/lib/danger/commands/runner.rb +94 -0
  60. data/lib/danger/commands/staging.rb +53 -0
  61. data/lib/danger/commands/systems.rb +43 -0
  62. data/lib/danger/comment_generators/bitbucket_server.md.erb +20 -0
  63. data/lib/danger/comment_generators/bitbucket_server_inline.md.erb +15 -0
  64. data/lib/danger/comment_generators/bitbucket_server_message_group.md.erb +12 -0
  65. data/lib/danger/comment_generators/github.md.erb +55 -0
  66. data/lib/danger/comment_generators/github_inline.md.erb +26 -0
  67. data/lib/danger/comment_generators/gitlab.md.erb +40 -0
  68. data/lib/danger/comment_generators/gitlab_inline.md.erb +26 -0
  69. data/lib/danger/comment_generators/vsts.md.erb +20 -0
  70. data/lib/danger/core_ext/file_list.rb +18 -0
  71. data/lib/danger/core_ext/string.rb +20 -0
  72. data/lib/danger/danger_core/dangerfile.rb +341 -0
  73. data/lib/danger/danger_core/dangerfile_dsl.rb +29 -0
  74. data/lib/danger/danger_core/dangerfile_generator.rb +11 -0
  75. data/lib/danger/danger_core/environment_manager.rb +123 -0
  76. data/lib/danger/danger_core/executor.rb +92 -0
  77. data/lib/danger/danger_core/message_aggregator.rb +49 -0
  78. data/lib/danger/danger_core/message_group.rb +68 -0
  79. data/lib/danger/danger_core/messages/base.rb +56 -0
  80. data/lib/danger/danger_core/messages/markdown.rb +42 -0
  81. data/lib/danger/danger_core/messages/violation.rb +54 -0
  82. data/lib/danger/danger_core/plugins/dangerfile_bitbucket_cloud_plugin.rb +144 -0
  83. data/lib/danger/danger_core/plugins/dangerfile_bitbucket_server_plugin.rb +211 -0
  84. data/lib/danger/danger_core/plugins/dangerfile_danger_plugin.rb +248 -0
  85. data/lib/danger/danger_core/plugins/dangerfile_git_plugin.rb +158 -0
  86. data/lib/danger/danger_core/plugins/dangerfile_github_plugin.rb +254 -0
  87. data/lib/danger/danger_core/plugins/dangerfile_gitlab_plugin.rb +240 -0
  88. data/lib/danger/danger_core/plugins/dangerfile_local_only_plugin.rb +42 -0
  89. data/lib/danger/danger_core/plugins/dangerfile_messaging_plugin.rb +218 -0
  90. data/lib/danger/danger_core/plugins/dangerfile_vsts_plugin.rb +191 -0
  91. data/lib/danger/danger_core/standard_error.rb +143 -0
  92. data/lib/danger/helpers/array_subclass.rb +61 -0
  93. data/lib/danger/helpers/comment.rb +32 -0
  94. data/lib/danger/helpers/comments_helper.rb +178 -0
  95. data/lib/danger/helpers/comments_parsing_helper.rb +70 -0
  96. data/lib/danger/helpers/emoji_mapper.rb +41 -0
  97. data/lib/danger/helpers/find_max_num_violations.rb +31 -0
  98. data/lib/danger/helpers/message_groups_array_helper.rb +31 -0
  99. data/lib/danger/plugin_support/gems_resolver.rb +77 -0
  100. data/lib/danger/plugin_support/plugin.rb +49 -0
  101. data/lib/danger/plugin_support/plugin_file_resolver.rb +30 -0
  102. data/lib/danger/plugin_support/plugin_linter.rb +161 -0
  103. data/lib/danger/plugin_support/plugin_parser.rb +199 -0
  104. data/lib/danger/plugin_support/templates/readme_table.html.erb +26 -0
  105. data/lib/danger/request_sources/bitbucket_cloud.rb +171 -0
  106. data/lib/danger/request_sources/bitbucket_cloud_api.rb +181 -0
  107. data/lib/danger/request_sources/bitbucket_server.rb +105 -0
  108. data/lib/danger/request_sources/bitbucket_server_api.rb +117 -0
  109. data/lib/danger/request_sources/github/github.rb +530 -0
  110. data/lib/danger/request_sources/github/github_review.rb +126 -0
  111. data/lib/danger/request_sources/github/github_review_resolver.rb +19 -0
  112. data/lib/danger/request_sources/github/github_review_unsupported.rb +25 -0
  113. data/lib/danger/request_sources/gitlab.rb +525 -0
  114. data/lib/danger/request_sources/local_only.rb +53 -0
  115. data/lib/danger/request_sources/request_source.rb +85 -0
  116. data/lib/danger/request_sources/support/get_ignored_violation.rb +17 -0
  117. data/lib/danger/request_sources/vsts.rb +118 -0
  118. data/lib/danger/request_sources/vsts_api.rb +138 -0
  119. data/lib/danger/scm_source/git_repo.rb +181 -0
  120. data/lib/danger/version.rb +4 -0
  121. metadata +339 -0
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "danger/danger_core/messages/base"
4
+
5
+ module Danger
6
+ class Violation < BaseMessage
7
+ VALID_TYPES = %I[error warning message].freeze
8
+ attr_accessor :sticky
9
+
10
+ def initialize(message, sticky, file = nil, line = nil, type: :warning)
11
+ raise ArgumentError unless VALID_TYPES.include?(type)
12
+
13
+ super(type: type, message: message, file: file, line: line)
14
+ self.sticky = sticky
15
+ end
16
+
17
+ def ==(other)
18
+ return false if other.nil?
19
+ return false unless other.kind_of? self.class
20
+
21
+ other.message == message &&
22
+ other.sticky == sticky &&
23
+ other.file == file &&
24
+ other.line == line
25
+ end
26
+
27
+ def hash
28
+ h = 1
29
+ h = h * 31 + message.hash
30
+ h = h * 13 + sticky.hash
31
+ h = h * 17 + file.hash
32
+ h = h * 17 + line.hash
33
+ h
34
+ end
35
+
36
+ def <=>(other)
37
+ types = VALID_TYPES + [:markdown]
38
+ order = types.index(type) <=> types.index(other.type)
39
+ return order unless order.zero?
40
+
41
+ compare_by_file_and_line(other)
42
+ end
43
+
44
+ def to_s
45
+ extra = []
46
+ extra << "sticky: #{sticky}"
47
+ extra << "file: #{file}" if file
48
+ extra << "line: #{line}" if line
49
+ extra << "type: #{type}"
50
+
51
+ "Violation #{message} { #{extra.join ', '} }"
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,144 @@
1
+ # coding: utf-8
2
+
3
+ require "danger/plugin_support/plugin"
4
+
5
+ module Danger
6
+ # Handles interacting with Bitbucket Cloud inside a Dangerfile. Provides a few functions which wrap `pr_json` and also
7
+ # through a few standard functions to simplify your code.
8
+ #
9
+ # @example Warn when a PR is classed as work in progress
10
+ #
11
+ # warn "PR is classed as Work in Progress" if bitbucket_cloud.pr_title.include? "[WIP]"
12
+ #
13
+ # @example Declare a PR to be simple to avoid specific Danger rules
14
+ #
15
+ # declared_trivial = (bitbucket_cloud.pr_title + bitbucket_cloud.pr_body).include?("#trivial")
16
+ #
17
+ # @example Ensure that labels have been used on the PR
18
+ #
19
+ # failure "Please add labels to this PR" if bitbucket_cloud.pr_labels.empty?
20
+ #
21
+ # @example Ensure there is a summary for a PR
22
+ #
23
+ # failure "Please provide a summary in the Pull Request description" if bitbucket_cloud.pr_body.length < 5
24
+ #
25
+ # @example Only accept PRs to the develop branch
26
+ #
27
+ # failure "Please re-submit this PR to develop, we may have already fixed your issue." if bitbucket_cloud.branch_for_base != "develop"
28
+ #
29
+ # @example Highlight when a celebrity makes a pull request
30
+ #
31
+ # message "Welcome, Danger." if bitbucket_cloud.pr_author == "dangermcshane"
32
+ #
33
+ # @example Ensure that all PRs have an assignee
34
+ #
35
+ # warn "This PR does not have any assignees yet." if bitbucket_cloud.pr_json[:reviewers].length == 0
36
+ #
37
+ # @example Send a message with links to a collection of specific files
38
+ #
39
+ # if git.modified_files.include? "config/*.js"
40
+ # config_files = git.modified_files.select { |path| path.include? "config/" }
41
+ # message "This PR changes #{ bitbucket_cloud.html_link(config_files) }"
42
+ # end
43
+ #
44
+ # @example Highlight with a clickable link if a Package.json is changed
45
+ #
46
+ # warn "#{bitbucket_cloud.html_link("Package.json")} was edited." if git.modified_files.include? "Package.json"
47
+ #
48
+ # @see danger/danger
49
+ # @tags core, bitbucket_cloud
50
+ #
51
+ class DangerfileBitbucketCloudPlugin < Plugin
52
+ # So that this init can fail.
53
+ def self.new(dangerfile)
54
+ return nil if dangerfile.env.request_source.class != Danger::RequestSources::BitbucketCloud
55
+ super
56
+ end
57
+
58
+ # The instance name used in the Dangerfile
59
+ # @return [String]
60
+ #
61
+ def self.instance_name
62
+ "bitbucket_cloud"
63
+ end
64
+
65
+ def initialize(dangerfile)
66
+ super(dangerfile)
67
+ @bs = dangerfile.env.request_source
68
+ end
69
+
70
+ # @!group Bitbucket Cloud Misc
71
+ # The hash that represents the PR's JSON. For an example of what this looks like
72
+ # see the [Danger Fixture'd one](https://raw.githubusercontent.com/danger/danger/master/spec/fixtures/bitbucket_cloud_api/pr_response.json).
73
+ # @return [Hash]
74
+ def pr_json
75
+ @bs.pr_json
76
+ end
77
+
78
+ # @!group PR Metadata
79
+ # The title of the Pull Request.
80
+ # @return [String]
81
+ #
82
+ def pr_title
83
+ @bs.pr_json[:title].to_s
84
+ end
85
+
86
+ # @!group PR Metadata
87
+ # The body text of the Pull Request.
88
+ # @return [String]
89
+ #
90
+ def pr_description
91
+ @bs.pr_json[:description].to_s
92
+ end
93
+ alias pr_body pr_description
94
+
95
+ # @!group PR Metadata
96
+ # The username of the author of the Pull Request.
97
+ # @return [String]
98
+ #
99
+ def pr_author
100
+ @bs.pr_json[:author][:username]
101
+ end
102
+
103
+ # @!group PR Commit Metadata
104
+ # The branch to which the PR is going to be merged into.
105
+ # @return [String]
106
+ #
107
+ def branch_for_base
108
+ @bs.pr_json[:destination][:branch][:name]
109
+ end
110
+
111
+ # @!group PR Commit Metadata
112
+ # A href that represents the current PR
113
+ # @return [String]
114
+ #
115
+ def pr_link
116
+ @bs.pr_json[:links][:self][:href]
117
+ end
118
+
119
+ # @!group PR Commit Metadata
120
+ # The branch to which the PR is going to be merged from.
121
+ # @return [String]
122
+ #
123
+ def branch_for_head
124
+ @bs.pr_json[:source][:branch][:name]
125
+ end
126
+
127
+ # @!group PR Commit Metadata
128
+ # The base commit to which the PR is going to be merged as a parent.
129
+ # @return [String]
130
+ #
131
+ def base_commit
132
+ @bs.pr_json[:destination][:commit][:hash]
133
+ end
134
+
135
+ # @!group PR Commit Metadata
136
+ # The head commit to which the PR is requesting to be merged from.
137
+ # @return [String]
138
+ #
139
+ def head_commit
140
+ @bs.pr_json[:source][:commit][:hash]
141
+ end
142
+
143
+ end
144
+ end
@@ -0,0 +1,211 @@
1
+ # coding: utf-8
2
+
3
+ require "danger/plugin_support/plugin"
4
+
5
+ module Danger
6
+ # Handles interacting with Bitbucket Server inside a Dangerfile. Provides a few functions which wrap `pr_json` and also
7
+ # through a few standard functions to simplify your code.
8
+ #
9
+ # @example Warn when a PR is classed as work in progress
10
+ #
11
+ # warn "PR is classed as Work in Progress" if bitbucket_server.pr_title.include? "[WIP]"
12
+ #
13
+ # @example Declare a PR to be simple to avoid specific Danger rules
14
+ #
15
+ # declared_trivial = (bitbucket_server.pr_title + bitbucket_server.pr_body).include?("#trivial")
16
+ #
17
+ # @example Ensure that labels have been used on the PR
18
+ #
19
+ # failure "Please add labels to this PR" if bitbucket_server.pr_labels.empty?
20
+ #
21
+ # @example Ensure there is a summary for a PR
22
+ #
23
+ # failure "Please provide a summary in the Pull Request description" if bitbucket_server.pr_body.length < 5
24
+ #
25
+ # @example Only accept PRs to the develop branch
26
+ #
27
+ # failure "Please re-submit this PR to develop, we may have already fixed your issue." if bitbucket_server.branch_for_base != "develop"
28
+ #
29
+ # @example Highlight when a celebrity makes a pull request
30
+ #
31
+ # message "Welcome, Danger." if bitbucket_server.pr_author == "dangermcshane"
32
+ #
33
+ # @example Ensure that all PRs have an assignee
34
+ #
35
+ # warn "This PR does not have any assignees yet." if bitbucket_server.pr_json[:reviewers].length == 0
36
+ #
37
+ # @example Send a message with links to a collection of specific files
38
+ #
39
+ # if git.modified_files.include? "config/*.js"
40
+ # config_files = git.modified_files.select { |path| path.include? "config/" }
41
+ # message "This PR changes #{ bitbucket_server.html_link(config_files) }"
42
+ # end
43
+ #
44
+ # @example Highlight with a clickable link if a Package.json is changed
45
+ #
46
+ # warn "#{bitbucket_server.html_link("Package.json")} was edited." if git.modified_files.include? "Package.json"
47
+ #
48
+ # @see danger/danger
49
+ # @tags core, bitbucket_server
50
+ #
51
+ class DangerfileBitbucketServerPlugin < Plugin
52
+ # So that this init can fail.
53
+ def self.new(dangerfile)
54
+ return nil if dangerfile.env.request_source.class != Danger::RequestSources::BitbucketServer
55
+ super
56
+ end
57
+
58
+ # The instance name used in the Dangerfile
59
+ # @return [String]
60
+ #
61
+ def self.instance_name
62
+ "bitbucket_server"
63
+ end
64
+
65
+ def initialize(dangerfile)
66
+ super(dangerfile)
67
+ @bs = dangerfile.env.request_source
68
+ end
69
+
70
+ # @!group Bitbucket Server Misc
71
+ # The hash that represents the PR's JSON. For an example of what this looks like
72
+ # see the [Danger Fixture'd one](https://raw.githubusercontent.com/danger/danger/master/spec/fixtures/bitbucket_server_api/pr_response.json).
73
+ # @return [Hash]
74
+ def pr_json
75
+ @bs.pr_json
76
+ end
77
+
78
+ # @!group PR Metadata
79
+ # The title of the Pull Request.
80
+ # @return [String]
81
+ #
82
+ def pr_title
83
+ @bs.pr_json[:title].to_s
84
+ end
85
+
86
+ # @!group PR Metadata
87
+ # The body text of the Pull Request.
88
+ # @return [String]
89
+ #
90
+ def pr_description
91
+ @bs.pr_json[:description].to_s
92
+ end
93
+ alias pr_body pr_description
94
+
95
+ # @!group PR Metadata
96
+ # The username of the author of the Pull Request.
97
+ # @return [String]
98
+ #
99
+ def pr_author
100
+ @bs.pr_json[:author][:user][:slug].to_s
101
+ end
102
+
103
+ # @!group PR Commit Metadata
104
+ # The branch to which the PR is going to be merged into.
105
+ # @return [String]
106
+ #
107
+ def branch_for_base
108
+ @bs.pr_json[:toRef][:displayId].to_s
109
+ end
110
+
111
+ # @!group PR Commit Metadata
112
+ # A href that represents the current PR
113
+ # @return [String]
114
+ #
115
+ def pr_link
116
+ @bs.pr_json[:links][:self].flat_map { |l| l[:href] }.first.to_s
117
+ end
118
+
119
+ # @!group PR Commit Metadata
120
+ # The branch to which the PR is going to be merged from.
121
+ # @return [String]
122
+ #
123
+ def branch_for_head
124
+ @bs.pr_json[:fromRef][:displayId].to_s
125
+ end
126
+
127
+ # @!group PR Commit Metadata
128
+ # The base commit to which the PR is going to be merged as a parent.
129
+ # @return [String]
130
+ #
131
+ def base_commit
132
+ @bs.pr_json[:toRef][:latestCommit].to_s
133
+ end
134
+
135
+ # @!group PR Commit Metadata
136
+ # The head commit to which the PR is requesting to be merged from.
137
+ # @return [String]
138
+ #
139
+ def head_commit
140
+ @bs.pr_json[:fromRef][:latestCommit].to_s
141
+ end
142
+
143
+ # @!group Bitbucket Server Misc
144
+ # Returns a list of Markdown links for a file, or files in the head repository.
145
+ # It returns a string of multiple anchors if passed an array.
146
+ # @note Atlassian [disabled inline HTML support](https://jira.atlassian.com/browse/BSERV-7147).
147
+ # This method method left for backward compatibility.
148
+ # @param [String or Array<String>] paths
149
+ # A list of strings to convert to github anchors
150
+ # @param [Bool] full_path
151
+ # Shows the full path as the link's text, defaults to `true`.
152
+ #
153
+ # @return [String]
154
+ #
155
+ def html_link(paths, full_path: true)
156
+ markdown_link(paths, full_path: full_path)
157
+ end
158
+
159
+ # @!group Bitbucket Server Misc
160
+ # Returns a list of Markdown links for a file, or files in the head repository.
161
+ # It returns a string of multiple links if passed an array.
162
+ # @param [String or Array<String>] paths
163
+ # A list of strings to convert to Markdown links
164
+ # @param [Bool] full_path
165
+ # Shows the full path as the link's text, defaults to `true`.
166
+ #
167
+ # @return [String]
168
+ #
169
+ def markdown_link(paths, full_path: true)
170
+ create_link(paths, full_path) { |href, text| create_markdown_link(href, text) }
171
+ end
172
+
173
+ # @!group Bitbucket Server Misc
174
+ # Updates the PR with build status and build server job link.
175
+ # @param [String] status
176
+ # SUCCESSFUL, FAILED and INPROGRESS
177
+ # @param [String] build_job_link
178
+ # Build server job link
179
+ # @param [String] description
180
+ # Build status description
181
+ # @return [String]
182
+ #
183
+ def update_pr_build_status(status, build_job_link, description)
184
+ @bs.update_pr_build_status(status, build_job_link, description)
185
+ end
186
+
187
+ private
188
+
189
+ def create_link(paths, full_path)
190
+ paths = [paths] unless paths.kind_of?(Array)
191
+ commit = head_commit
192
+ repo = pr_json[:fromRef][:repository][:links][:self].flat_map { |l| l[:href] }.first
193
+
194
+ paths = paths.map do |path|
195
+ path, line = path.split("#")
196
+ url_path = path.start_with?("/") ? path : "/#{path}"
197
+ text = full_path ? path : File.basename(path)
198
+ url_path.gsub!(" ", "%20")
199
+ line_ref = line ? "##{line}" : ""
200
+ yield("#{repo}#{url_path}?at=#{commit}#{line_ref}", text)
201
+ end
202
+
203
+ return paths.first if paths.count < 2
204
+ paths.first(paths.count - 1).join(", ") + " & " + paths.last
205
+ end
206
+
207
+ def create_markdown_link(href, text)
208
+ "[#{text}](#{href})"
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,248 @@
1
+ require "danger/plugin_support/plugin"
2
+
3
+ module Danger
4
+ # A way to interact with Danger herself. Offering APIs to import plugins,
5
+ # and Dangerfiles from multiple sources.
6
+ #
7
+ # @example Import a plugin available over HTTP
8
+ #
9
+ # device_grid = "https://raw.githubusercontent.com/fastlane/fastlane/master/danger-device_grid/lib/device_grid/plugin.rb"
10
+ # danger.import_plugin(device_grid)
11
+ #
12
+ # @example Import from a local file reference
13
+ #
14
+ # danger.import_plugin("danger/plugins/watch_plugin.rb")
15
+ #
16
+ # @example Import all files inside a folder
17
+ #
18
+ # danger.import_plugin("danger/plugins/*.rb")
19
+ #
20
+ # @example Run a Dangerfile from inside a sub-folder
21
+ #
22
+ # danger.import_dangerfile(path: "path/to/Dangerfile")
23
+ #
24
+ # @example Run a Dangerfile from inside a gem
25
+ #
26
+ # danger.import_dangerfile(gem: "ruby-grape-danger")
27
+ #
28
+ # @example Run a Dangerfile from inside a repo
29
+ #
30
+ # danger.import_dangerfile(gitlab_project_id: 1345)
31
+ #
32
+ # @example Run a Dangerfile from inside a repo branch and path
33
+ #
34
+ # danger.import_dangerfile(github: "ruby-grape/danger", branch: "custom", path: "path/to/Dangerfile")
35
+ #
36
+ # @see danger/danger
37
+ # @tags core, plugins
38
+
39
+ class DangerfileDangerPlugin < Plugin
40
+ # The instance name used in the Dangerfile
41
+ # @return [String]
42
+ #
43
+ def self.instance_name
44
+ "danger"
45
+ end
46
+
47
+ # @!group Danger
48
+ # Download a local or remote plugin and make it usable inside the Dangerfile.
49
+ #
50
+ # @param [String] path_or_url
51
+ # a local path or a https URL to the Ruby file to import
52
+ # a danger plugin from.
53
+ # @return [void]
54
+ #
55
+ def import_plugin(path_or_url)
56
+ raise "`import_plugin` requires a string" unless path_or_url.kind_of?(String)
57
+
58
+ if path_or_url.start_with?("http")
59
+ import_url(path_or_url)
60
+ else
61
+ import_local(path_or_url)
62
+ end
63
+ end
64
+
65
+ # @!group Danger
66
+ # Import a Dangerfile.
67
+ #
68
+ # @param [Hash] opts
69
+ # @option opts [String] :github GitHub repo
70
+ # @option opts [String] :gitlab GitLab repo
71
+ # @option opts [String] :gem Gem name
72
+ # @option opts [String] :path Path to Dangerfile
73
+ # @return [void]
74
+ def import_dangerfile(opts)
75
+ if opts.kind_of?(String)
76
+ warn "Use `import_dangerfile(github: '#{opts}')` instead of `import_dangerfile '#{opts}'`."
77
+ import_dangerfile_from_github(opts)
78
+ elsif opts.kind_of?(Hash)
79
+ if opts.key?(:github)
80
+ import_dangerfile_from_github(opts[:github], opts[:branch], opts[:path])
81
+ elsif opts.key?(:gitlab)
82
+ import_dangerfile_from_gitlab(opts[:gitlab], opts[:branch], opts[:path])
83
+ elsif opts.key?(:path)
84
+ import_dangerfile_from_path(opts[:path])
85
+ elsif opts.key?(:gem)
86
+ import_dangerfile_from_gem(opts[:gem])
87
+ else
88
+ raise "`import` requires a Hash with either :github, :gitlab, :gem, or :path"
89
+ end
90
+ else
91
+ raise "`import` requires a Hash"
92
+ end
93
+ end
94
+
95
+ # @!group Danger
96
+ # Returns the name of the current SCM Provider being used.
97
+ # @return [Symbol] The name of the SCM Provider used for the active repository.
98
+ def scm_provider
99
+ return :unknown unless env.request_source
100
+
101
+ case env.request_source
102
+ when Danger::RequestSources::GitHub
103
+ :github
104
+ when Danger::RequestSources::GitLab
105
+ :gitlab
106
+ when Danger::RequestSources::BitbucketServer
107
+ :bitbucket_server
108
+ when Danger::RequestSources::BitbucketCloud
109
+ :bitbucket_cloud
110
+ when Danger::RequestSources::VSTS
111
+ :vsts
112
+ else
113
+ :unknown
114
+ end
115
+ end
116
+
117
+ private
118
+
119
+ # @!group Danger
120
+ # Read and execute a local Dangerfile.
121
+ #
122
+ # @param [String] path
123
+ # A path to a Dangerfile.
124
+ # @return [void]
125
+ #
126
+ def import_dangerfile_from_path(path)
127
+ raise "`import_dangerfile_from_path` requires a string" unless path.kind_of?(String)
128
+ local_path = File.join(path, "Dangerfile")
129
+ @dangerfile.parse(Pathname.new(local_path))
130
+ end
131
+
132
+ # @!group Danger
133
+ # Read and execute a Dangerfile from a gem.
134
+ #
135
+ # @param [String] name
136
+ # The name of the gem that contains a Dangerfile.
137
+ # @return [void]
138
+ #
139
+ def import_dangerfile_from_gem(name)
140
+ raise "`import_dangerfile_from_gem` requires a string" unless name.kind_of?(String)
141
+ spec = Gem::Specification.find_by_name(name)
142
+ import_dangerfile_from_path(spec.gem_dir)
143
+ rescue Gem::MissingSpecError
144
+ raise "`import_dangerfile_from_gem` tried to load `#{name}` and failed, did you forget to include it in your Gemfile?"
145
+ end
146
+
147
+ # @!group Danger
148
+ # Download and execute a remote Dangerfile.
149
+ #
150
+ # @param [String] slug
151
+ # A slug that represents the repo where the Dangerfile is.
152
+ # @param [String] branch
153
+ # A branch from repo where the Dangerfile is.
154
+ # @param [String] path
155
+ # The path at the repo where Dangerfile is.
156
+ # @return [void]
157
+ #
158
+ def import_dangerfile_from_github(slug, branch = nil, path = nil)
159
+ raise "`import_dangerfile_from_github` requires a string" unless slug.kind_of?(String)
160
+ org, repo = slug.split("/")
161
+ download_url = env.request_source.file_url(organisation: org, repository: repo, branch: branch, path: path || "Dangerfile")
162
+ local_path = download(download_url)
163
+ @dangerfile.parse(Pathname.new(local_path))
164
+ end
165
+
166
+ # @!group Danger
167
+ # Download and execute a remote Dangerfile.
168
+ #
169
+ # @param [Int] slug_or_project_id
170
+ # The slug or id of the repo where the Dangerfile is.
171
+ # @param [String] branch
172
+ # A branch from repo where the Dangerfile is.
173
+ # @param [String] path
174
+ # The path at the repo where Dangerfile is.
175
+ # @return [void]
176
+ #
177
+ def import_dangerfile_from_gitlab(slug_or_project_id, branch = nil, path = nil)
178
+ download_url = env.request_source.file_url(repository: slug_or_project_id, branch: branch, path: path || "Dangerfile")
179
+ local_path = download(download_url)
180
+ @dangerfile.parse(Pathname.new(local_path))
181
+ end
182
+
183
+ # @!group Plugins
184
+ # Download a local or remote plugin or Dangerfile.
185
+ # This method will not import the file for you, use plugin.import instead
186
+ #
187
+ # @param [String] path_or_url
188
+ # a local path or a https URL to the Ruby file to import
189
+ # a danger plugin from.
190
+ # @return [String] The path to the downloaded Ruby file
191
+ #
192
+ def download(path_or_url)
193
+ raise "`download` requires a string" unless path_or_url.kind_of?(String)
194
+ raise "URL is not https, for security reasons `danger` only supports encrypted requests" if URI.parse(path_or_url).scheme != "https"
195
+
196
+ require "tmpdir"
197
+ require "faraday"
198
+
199
+ @http_client ||= Faraday.new do |b|
200
+ b.adapter :net_http
201
+ end
202
+ content = @http_client.get(path_or_url)
203
+
204
+ path = File.join(Dir.mktmpdir, "temporary_danger.rb")
205
+ File.write(path, content.body)
206
+ return path
207
+ end
208
+
209
+ # @!group Plugins
210
+ # Download a remote plugin and use it locally.
211
+ #
212
+ # @param [String] url
213
+ # https URL to the Ruby file to use
214
+ # @return [void]
215
+ def import_url(url)
216
+ path = download(url)
217
+ import_local(path)
218
+ end
219
+
220
+ # @!group Plugins
221
+ # Import one or more local plugins.
222
+ #
223
+ # @param [String] path
224
+ # The path to the file to import
225
+ # Can also be a pattern (./**/*plugin.rb)
226
+ # @return [void]
227
+ def import_local(path)
228
+ Dir[path].each do |file|
229
+ validate_file_contains_plugin!(file) do
230
+ # Without the expand_path it would fail if the path doesn't start with ./
231
+ require File.expand_path(file)
232
+ end
233
+
234
+ refresh_plugins
235
+ end
236
+ end
237
+
238
+ # Raises an error when the given block does not register a plugin.
239
+ def validate_file_contains_plugin!(file)
240
+ plugin_count_was = Danger::Plugin.all_plugins.length
241
+ yield
242
+
243
+ if Danger::Plugin.all_plugins.length == plugin_count_was
244
+ raise("#{file} doesn't contain any valid danger plugins.")
245
+ end
246
+ end
247
+ end
248
+ end