danger-additional-logging 0.0.1
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 +93 -0
- data/bin/danger +5 -0
- data/lib/assets/DangerfileTemplate +13 -0
- data/lib/danger/ci_source/appcenter.rb +55 -0
- data/lib/danger/ci_source/appcircle.rb +83 -0
- data/lib/danger/ci_source/appveyor.rb +64 -0
- data/lib/danger/ci_source/azure_pipelines.rb +61 -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 +78 -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 +71 -0
- data/lib/danger/ci_source/codefresh.rb +47 -0
- data/lib/danger/ci_source/codemagic.rb +58 -0
- data/lib/danger/ci_source/codeship.rb +44 -0
- data/lib/danger/ci_source/concourse.rb +60 -0
- data/lib/danger/ci_source/custom_ci_with_github.rb +49 -0
- data/lib/danger/ci_source/dotci.rb +50 -0
- data/lib/danger/ci_source/drone.rb +71 -0
- data/lib/danger/ci_source/github_actions.rb +44 -0
- data/lib/danger/ci_source/gitlab_ci.rb +89 -0
- data/lib/danger/ci_source/jenkins.rb +148 -0
- data/lib/danger/ci_source/local_git_repo.rb +117 -0
- data/lib/danger/ci_source/local_only_git_repo.rb +44 -0
- data/lib/danger/ci_source/screwdriver.rb +48 -0
- data/lib/danger/ci_source/semaphore.rb +37 -0
- data/lib/danger/ci_source/support/commits.rb +19 -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 +43 -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 +190 -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 +163 -0
- data/lib/danger/ci_source/travis.rb +51 -0
- data/lib/danger/ci_source/xcode_cloud.rb +38 -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 +38 -0
- data/lib/danger/commands/local_helpers/local_setup.rb +48 -0
- data/lib/danger/commands/local_helpers/pry_setup.rb +32 -0
- data/lib/danger/commands/plugins/plugin_json.rb +44 -0
- data/lib/danger/commands/plugins/plugin_lint.rb +52 -0
- data/lib/danger/commands/plugins/plugin_readme.rb +42 -0
- data/lib/danger/commands/pr.rb +93 -0
- data/lib/danger/commands/runner.rb +94 -0
- data/lib/danger/commands/staging.rb +53 -0
- data/lib/danger/commands/systems.rb +41 -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 +21 -0
- data/lib/danger/comment_generators/vsts.md.erb +20 -0
- data/lib/danger/comment_generators/vsts_inline.md.erb +17 -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 +348 -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 +126 -0
- data/lib/danger/danger_core/executor.rb +91 -0
- data/lib/danger/danger_core/message_aggregator.rb +50 -0
- data/lib/danger/danger_core/message_group.rb +68 -0
- data/lib/danger/danger_core/messages/base.rb +57 -0
- data/lib/danger/danger_core/messages/markdown.rb +41 -0
- data/lib/danger/danger_core/messages/violation.rb +53 -0
- data/lib/danger/danger_core/plugins/dangerfile_bitbucket_cloud_plugin.rb +142 -0
- data/lib/danger/danger_core/plugins/dangerfile_bitbucket_server_plugin.rb +211 -0
- data/lib/danger/danger_core/plugins/dangerfile_danger_plugin.rb +274 -0
- data/lib/danger/danger_core/plugins/dangerfile_git_plugin.rb +159 -0
- data/lib/danger/danger_core/plugins/dangerfile_github_plugin.rb +264 -0
- data/lib/danger/danger_core/plugins/dangerfile_gitlab_plugin.rb +275 -0
- data/lib/danger/danger_core/plugins/dangerfile_local_only_plugin.rb +43 -0
- data/lib/danger/danger_core/plugins/dangerfile_messaging_plugin.rb +220 -0
- data/lib/danger/danger_core/plugins/dangerfile_vsts_plugin.rb +191 -0
- data/lib/danger/danger_core/standard_error.rb +142 -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 +179 -0
- data/lib/danger/helpers/comments_parsing_helper.rb +71 -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 +52 -0
- data/lib/danger/plugin_support/plugin_file_resolver.rb +30 -0
- data/lib/danger/plugin_support/plugin_linter.rb +162 -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 +169 -0
- data/lib/danger/request_sources/bitbucket_cloud_api.rb +181 -0
- data/lib/danger/request_sources/bitbucket_server.rb +210 -0
- data/lib/danger/request_sources/bitbucket_server_api.rb +129 -0
- data/lib/danger/request_sources/code_insights_api.rb +142 -0
- data/lib/danger/request_sources/github/github.rb +535 -0
- data/lib/danger/request_sources/github/github_review.rb +127 -0
- data/lib/danger/request_sources/github/github_review_resolver.rb +17 -0
- data/lib/danger/request_sources/github/github_review_unsupported.rb +23 -0
- data/lib/danger/request_sources/gitlab.rb +557 -0
- data/lib/danger/request_sources/local_only.rb +50 -0
- data/lib/danger/request_sources/request_source.rb +97 -0
- data/lib/danger/request_sources/support/get_ignored_violation.rb +17 -0
- data/lib/danger/request_sources/vsts.rb +278 -0
- data/lib/danger/request_sources/vsts_api.rb +172 -0
- data/lib/danger/scm_source/git_repo.rb +198 -0
- data/lib/danger/version.rb +4 -0
- data/lib/danger.rb +45 -0
- metadata +351 -0
|
@@ -0,0 +1,274 @@
|
|
|
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
|
+
# @example Import a plugin available over HTTP
|
|
37
|
+
#
|
|
38
|
+
# custom_url = "https://custom.bitbucket.com/project-name/Dangerfile?raw"
|
|
39
|
+
# danger.import_dangerfile(url: custom_url)
|
|
40
|
+
#
|
|
41
|
+
# @see danger/danger
|
|
42
|
+
# @tags core, plugins
|
|
43
|
+
|
|
44
|
+
class DangerfileDangerPlugin < Plugin
|
|
45
|
+
# The instance name used in the Dangerfile
|
|
46
|
+
# @return [String]
|
|
47
|
+
#
|
|
48
|
+
def self.instance_name
|
|
49
|
+
"danger"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @!group Danger
|
|
53
|
+
# Download a local or remote plugin and make it usable inside the Dangerfile.
|
|
54
|
+
#
|
|
55
|
+
# @param [String] path_or_url
|
|
56
|
+
# a local path or a https URL to the Ruby file to import
|
|
57
|
+
# a danger plugin from.
|
|
58
|
+
# @return [void]
|
|
59
|
+
#
|
|
60
|
+
def import_plugin(path_or_url)
|
|
61
|
+
raise "`import_plugin` requires a string" unless path_or_url.kind_of?(String)
|
|
62
|
+
|
|
63
|
+
if path_or_url.start_with?("http")
|
|
64
|
+
import_url(path_or_url)
|
|
65
|
+
else
|
|
66
|
+
import_local(path_or_url)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# @!group Danger
|
|
71
|
+
# Import a Dangerfile.
|
|
72
|
+
#
|
|
73
|
+
# @param [Hash] opts
|
|
74
|
+
# @option opts [String] :github GitHub repo
|
|
75
|
+
# @option opts [String] :gitlab GitLab repo
|
|
76
|
+
# @option opts [String] :gem Gem name
|
|
77
|
+
# @option opts [String] :ref The name of branch, tag or commit
|
|
78
|
+
# @option opts [String] :branch Alias of :ref
|
|
79
|
+
# @option opts [String] :path Path to Dangerfile
|
|
80
|
+
# @return [void]
|
|
81
|
+
def import_dangerfile(opts)
|
|
82
|
+
if opts.kind_of?(String)
|
|
83
|
+
warn "Use `import_dangerfile(github: '#{opts}')` instead of `import_dangerfile '#{opts}'`."
|
|
84
|
+
import_dangerfile_from_github(opts)
|
|
85
|
+
elsif opts.kind_of?(Hash)
|
|
86
|
+
if opts.key?(:github)
|
|
87
|
+
import_dangerfile_from_github(opts[:github], opts[:ref] || opts[:branch], opts[:path])
|
|
88
|
+
elsif opts.key?(:gitlab)
|
|
89
|
+
import_dangerfile_from_gitlab(opts[:gitlab], opts[:ref] || opts[:branch], opts[:path])
|
|
90
|
+
elsif opts.key?(:path)
|
|
91
|
+
import_dangerfile_from_path(opts[:path])
|
|
92
|
+
elsif opts.key?(:gem)
|
|
93
|
+
import_dangerfile_from_gem(opts[:gem])
|
|
94
|
+
elsif opts.key?(:url)
|
|
95
|
+
import_dangerfile_from_url(opts[:url])
|
|
96
|
+
else
|
|
97
|
+
raise "`import` requires a Hash with either :github, :gitlab, :gem, :path or :url"
|
|
98
|
+
end
|
|
99
|
+
else
|
|
100
|
+
raise "`import` requires a Hash"
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# @!group Danger
|
|
105
|
+
# Returns the name of the current SCM Provider being used.
|
|
106
|
+
# @return [Symbol] The name of the SCM Provider used for the active repository.
|
|
107
|
+
def scm_provider
|
|
108
|
+
return :unknown unless env.request_source
|
|
109
|
+
|
|
110
|
+
case env.request_source
|
|
111
|
+
when Danger::RequestSources::GitHub
|
|
112
|
+
:github
|
|
113
|
+
when Danger::RequestSources::GitLab
|
|
114
|
+
:gitlab
|
|
115
|
+
when Danger::RequestSources::BitbucketServer
|
|
116
|
+
:bitbucket_server
|
|
117
|
+
when Danger::RequestSources::BitbucketCloud
|
|
118
|
+
:bitbucket_cloud
|
|
119
|
+
when Danger::RequestSources::VSTS
|
|
120
|
+
:vsts
|
|
121
|
+
else
|
|
122
|
+
:unknown
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
# @!group Danger
|
|
129
|
+
# Read and execute a local Dangerfile.
|
|
130
|
+
#
|
|
131
|
+
# @param [String] path
|
|
132
|
+
# A path to a Dangerfile.
|
|
133
|
+
# @return [void]
|
|
134
|
+
#
|
|
135
|
+
def import_dangerfile_from_path(path)
|
|
136
|
+
raise "`import_dangerfile_from_path` requires a string" unless path.kind_of?(String)
|
|
137
|
+
|
|
138
|
+
local_path = File.file?(path) ? path : File.join(path, "Dangerfile")
|
|
139
|
+
@dangerfile.parse(Pathname.new(local_path))
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# @!group Danger
|
|
143
|
+
# Read and execute a Dangerfile from a gem.
|
|
144
|
+
#
|
|
145
|
+
# @param [String] name
|
|
146
|
+
# The name of the gem that contains a Dangerfile.
|
|
147
|
+
# @return [void]
|
|
148
|
+
#
|
|
149
|
+
def import_dangerfile_from_gem(name)
|
|
150
|
+
raise "`import_dangerfile_from_gem` requires a string" unless name.kind_of?(String)
|
|
151
|
+
|
|
152
|
+
spec = Gem::Specification.find_by_name(name)
|
|
153
|
+
import_dangerfile_from_path(spec.gem_dir)
|
|
154
|
+
rescue Gem::MissingSpecError
|
|
155
|
+
raise "`import_dangerfile_from_gem` tried to load `#{name}` and failed, did you forget to include it in your Gemfile?"
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# @!group Danger
|
|
159
|
+
# Download and execute a remote Dangerfile.
|
|
160
|
+
#
|
|
161
|
+
# @param [String] slug
|
|
162
|
+
# A slug that represents the repo where the Dangerfile is.
|
|
163
|
+
# @param [String] branch
|
|
164
|
+
# A branch from repo where the Dangerfile is.
|
|
165
|
+
# @param [String] path
|
|
166
|
+
# The path at the repo where Dangerfile is.
|
|
167
|
+
# @return [void]
|
|
168
|
+
#
|
|
169
|
+
def import_dangerfile_from_github(slug, branch = nil, path = nil)
|
|
170
|
+
raise "`import_dangerfile_from_github` requires a string" unless slug.kind_of?(String)
|
|
171
|
+
|
|
172
|
+
org, repo = slug.split("/")
|
|
173
|
+
download_url = env.request_source.file_url(organisation: org, repository: repo, branch: branch, path: path || "Dangerfile")
|
|
174
|
+
local_path = download(download_url)
|
|
175
|
+
@dangerfile.parse(Pathname.new(local_path))
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# @!group Danger
|
|
179
|
+
# Download and execute a remote Dangerfile.
|
|
180
|
+
#
|
|
181
|
+
# @param [Int] slug_or_project_id
|
|
182
|
+
# The slug or id of the repo where the Dangerfile is.
|
|
183
|
+
# @param [String] branch
|
|
184
|
+
# A branch from repo where the Dangerfile is.
|
|
185
|
+
# @param [String] path
|
|
186
|
+
# The path at the repo where Dangerfile is.
|
|
187
|
+
# @return [void]
|
|
188
|
+
#
|
|
189
|
+
def import_dangerfile_from_gitlab(slug_or_project_id, branch = nil, path = nil)
|
|
190
|
+
download_url = env.request_source.file_url(repository: slug_or_project_id, branch: branch, path: path || "Dangerfile")
|
|
191
|
+
local_path = download(download_url)
|
|
192
|
+
@dangerfile.parse(Pathname.new(local_path))
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# @!group Danger
|
|
196
|
+
# Download and execute a remote Dangerfile.
|
|
197
|
+
#
|
|
198
|
+
# @param [String] url
|
|
199
|
+
# A https url where the Dangerfile is.
|
|
200
|
+
# @return [void]
|
|
201
|
+
#
|
|
202
|
+
def import_dangerfile_from_url(url)
|
|
203
|
+
raise "`import_dangerfile_from_url` requires a string" unless url.kind_of?(String)
|
|
204
|
+
|
|
205
|
+
local_path = download(url)
|
|
206
|
+
@dangerfile.parse(Pathname.new(local_path))
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# @!group Plugins
|
|
210
|
+
# Download a local or remote plugin or Dangerfile.
|
|
211
|
+
# This method will not import the file for you, use plugin.import instead
|
|
212
|
+
#
|
|
213
|
+
# @param [String] path_or_url
|
|
214
|
+
# a local path or a https URL to the Ruby file to import
|
|
215
|
+
# a danger plugin from.
|
|
216
|
+
# @return [String] The path to the downloaded Ruby file
|
|
217
|
+
#
|
|
218
|
+
def download(path_or_url)
|
|
219
|
+
raise "`download` requires a string" unless path_or_url.kind_of?(String)
|
|
220
|
+
raise "URL is not https, for security reasons `danger` only supports encrypted requests" if URI.parse(path_or_url).scheme != "https"
|
|
221
|
+
|
|
222
|
+
require "tmpdir"
|
|
223
|
+
require "faraday"
|
|
224
|
+
|
|
225
|
+
@http_client ||= Faraday.new do |b|
|
|
226
|
+
b.adapter :net_http
|
|
227
|
+
end
|
|
228
|
+
content = @http_client.get(path_or_url)
|
|
229
|
+
|
|
230
|
+
path = File.join(Dir.mktmpdir, "temporary_danger.rb")
|
|
231
|
+
File.write(path, content.body)
|
|
232
|
+
return path
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# @!group Plugins
|
|
236
|
+
# Download a remote plugin and use it locally.
|
|
237
|
+
#
|
|
238
|
+
# @param [String] url
|
|
239
|
+
# https URL to the Ruby file to use
|
|
240
|
+
# @return [void]
|
|
241
|
+
def import_url(url)
|
|
242
|
+
path = download(url)
|
|
243
|
+
import_local(path)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# @!group Plugins
|
|
247
|
+
# Import one or more local plugins.
|
|
248
|
+
#
|
|
249
|
+
# @param [String] path
|
|
250
|
+
# The path to the file to import
|
|
251
|
+
# Can also be a pattern (./**/*plugin.rb)
|
|
252
|
+
# @return [void]
|
|
253
|
+
def import_local(path)
|
|
254
|
+
Dir[path].each do |file|
|
|
255
|
+
validate_file_contains_plugin!(file) do
|
|
256
|
+
# Without the expand_path it would fail if the path doesn't start with ./
|
|
257
|
+
require File.expand_path(file)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
refresh_plugins
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Raises an error when the given block does not register a plugin.
|
|
265
|
+
def validate_file_contains_plugin!(file)
|
|
266
|
+
plugin_count_was = Danger::Plugin.all_plugins.length
|
|
267
|
+
yield
|
|
268
|
+
|
|
269
|
+
if Danger::Plugin.all_plugins.length == plugin_count_was
|
|
270
|
+
raise("#{file} doesn't contain any valid danger plugins.")
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
require "danger/plugin_support/plugin"
|
|
2
|
+
require "danger/core_ext/file_list"
|
|
3
|
+
|
|
4
|
+
# Danger
|
|
5
|
+
module Danger
|
|
6
|
+
# Handles interacting with git inside a Dangerfile. Providing access to files that have changed, and useful statistics. Also provides
|
|
7
|
+
# access to the commits in the form of [Git::Log](https://github.com/schacon/ruby-git/blob/master/lib/git/log.rb) objects.
|
|
8
|
+
#
|
|
9
|
+
# @example Do something to all new and edited markdown files
|
|
10
|
+
#
|
|
11
|
+
# markdowns = (git.added_files + git.modified_files)
|
|
12
|
+
# do_something markdowns.select{ |file| file.end_with? "md" }
|
|
13
|
+
#
|
|
14
|
+
# @example Don't allow a file to be deleted
|
|
15
|
+
#
|
|
16
|
+
# deleted = git.deleted_files.include? "my/favourite.file"
|
|
17
|
+
# failure "Don't delete my precious" if deleted
|
|
18
|
+
#
|
|
19
|
+
# @example Fail really big diffs
|
|
20
|
+
#
|
|
21
|
+
# failure "We cannot handle the scale of this PR" if git.lines_of_code > 50_000
|
|
22
|
+
#
|
|
23
|
+
# @example Warn when there are merge commits in the diff
|
|
24
|
+
#
|
|
25
|
+
# if git.commits.any? { |c| c.parents.count > 1 }
|
|
26
|
+
# warn 'Please rebase to get rid of the merge commits in this PR'
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# @example Warn when somebody tries to add nokogiri to the project
|
|
30
|
+
#
|
|
31
|
+
# diff = git.diff_for_file("Gemfile.lock")
|
|
32
|
+
# if diff && diff.patch =~ "nokogiri"
|
|
33
|
+
# warn 'Please do not add nokogiri to the project. Thank you.'
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
36
|
+
# @see danger/danger
|
|
37
|
+
# @tags core, git
|
|
38
|
+
|
|
39
|
+
class DangerfileGitPlugin < Plugin
|
|
40
|
+
# The instance name used in the Dangerfile
|
|
41
|
+
# @return [String]
|
|
42
|
+
#
|
|
43
|
+
def self.instance_name
|
|
44
|
+
"git"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def initialize(dangerfile)
|
|
48
|
+
super(dangerfile)
|
|
49
|
+
raise unless dangerfile.env.scm.class == Danger::GitRepo # rubocop:disable Style/ClassEqualityComparison
|
|
50
|
+
|
|
51
|
+
@git = dangerfile.env.scm
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @!group Git Files
|
|
55
|
+
# Paths for files that were added during the diff
|
|
56
|
+
# @return [FileList<String>] an [Array] subclass
|
|
57
|
+
#
|
|
58
|
+
def added_files
|
|
59
|
+
Danger::FileList.new(@git.diff.select { |diff| diff.type == "new" }.map(&:path))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @!group Git Files
|
|
63
|
+
# Paths for files that were removed during the diff
|
|
64
|
+
# @return [FileList<String>] an [Array] subclass
|
|
65
|
+
#
|
|
66
|
+
def deleted_files
|
|
67
|
+
Danger::FileList.new(@git.diff.select { |diff| diff.type == "deleted" }.map(&:path))
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# @!group Git Files
|
|
71
|
+
# Paths for files that changed during the diff
|
|
72
|
+
# @return [FileList<String>] an [Array] subclass
|
|
73
|
+
#
|
|
74
|
+
def modified_files
|
|
75
|
+
Danger::FileList.new(@git.diff.select { |diff| diff.type == "modified" }.map(&:path))
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @!group Git Metadata
|
|
79
|
+
# List of renamed files
|
|
80
|
+
# @return [Array<Hash>] with keys `:before` and `:after`
|
|
81
|
+
#
|
|
82
|
+
def renamed_files
|
|
83
|
+
@git.renamed_files
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @!group Git Metadata
|
|
87
|
+
# Whole diff
|
|
88
|
+
# @return [Git::Diff] from the gem `git`
|
|
89
|
+
#
|
|
90
|
+
def diff
|
|
91
|
+
@git.diff
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# @!group Git Metadata
|
|
95
|
+
# The overall lines of code added/removed in the diff
|
|
96
|
+
# @return [Fixnum]
|
|
97
|
+
#
|
|
98
|
+
def lines_of_code
|
|
99
|
+
@git.diff.lines
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# @!group Git Metadata
|
|
103
|
+
# The overall lines of code removed in the diff
|
|
104
|
+
# @return [Fixnum]
|
|
105
|
+
#
|
|
106
|
+
def deletions
|
|
107
|
+
@git.diff.deletions
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# @!group Git Metadata
|
|
111
|
+
# The overall lines of code added in the diff
|
|
112
|
+
# @return [Fixnum]
|
|
113
|
+
#
|
|
114
|
+
def insertions
|
|
115
|
+
@git.diff.insertions
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# @!group Git Metadata
|
|
119
|
+
# The log of commits inside the diff
|
|
120
|
+
# @return [Git::Log] from the gem `git`
|
|
121
|
+
#
|
|
122
|
+
def commits
|
|
123
|
+
@git.log.to_a
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# @!group Git Metadata
|
|
127
|
+
# Details for a specific file in this diff
|
|
128
|
+
# @return [Git::Diff::DiffFile] from the gem `git`
|
|
129
|
+
#
|
|
130
|
+
def diff_for_file(file)
|
|
131
|
+
(added_files + modified_files + deleted_files).include?(file) ? @git.diff[file] : nil
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# @!group Git Metadata
|
|
135
|
+
# Statistics for a specific file in this diff
|
|
136
|
+
# @return [Hash] with keys `:insertions`, `:deletions` giving line counts, and `:before`, `:after` giving file contents, or nil if the file has no changes or does not exist
|
|
137
|
+
#
|
|
138
|
+
def info_for_file(file)
|
|
139
|
+
return nil unless modified_files.include?(file) || added_files.include?(file) || deleted_files.include?(file)
|
|
140
|
+
|
|
141
|
+
stats = @git.diff.stats[:files][file]
|
|
142
|
+
diff = @git.diff[file]
|
|
143
|
+
{
|
|
144
|
+
insertions: stats[:insertions],
|
|
145
|
+
deletions: stats[:deletions],
|
|
146
|
+
before: added_files.include?(file) || deleted_files.include?(file) ? nil : diff.blob(:src).contents,
|
|
147
|
+
after: added_files.include?(file) || deleted_files.include?(file) ? nil : diff.blob(:dst).contents
|
|
148
|
+
}
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# @!group Git Metadata
|
|
152
|
+
# List of remote tags
|
|
153
|
+
# @return [String]
|
|
154
|
+
#
|
|
155
|
+
def tags
|
|
156
|
+
@git.tags.each_line
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
require "danger/plugin_support/plugin"
|
|
2
|
+
|
|
3
|
+
module Danger
|
|
4
|
+
# Handles interacting with GitHub inside a Dangerfile. Provides a few functions which wrap `pr_json` and also
|
|
5
|
+
# through a few standard functions to simplify your code.
|
|
6
|
+
#
|
|
7
|
+
# @example Warn when a PR is classed as work in progress
|
|
8
|
+
#
|
|
9
|
+
# warn "PR is classed as Work in Progress" if github.pr_title.include? "[WIP]"
|
|
10
|
+
#
|
|
11
|
+
# @example Declare a PR to be simple to avoid specific Danger rules
|
|
12
|
+
#
|
|
13
|
+
# declared_trivial = (github.pr_title + github.pr_body).include?("#trivial")
|
|
14
|
+
#
|
|
15
|
+
# @example Ensure that labels have been used on the PR
|
|
16
|
+
#
|
|
17
|
+
# failure "Please add labels to this PR" if github.pr_labels.empty?
|
|
18
|
+
#
|
|
19
|
+
# @example Check if a user is in a specific GitHub org, and message them if so
|
|
20
|
+
#
|
|
21
|
+
# unless github.api.organization_member?('danger', github.pr_author)
|
|
22
|
+
# message "@#{github.pr_author} is not a contributor yet, would you like to join the Danger org?"
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# @example Ensure there is a summary for a PR
|
|
26
|
+
#
|
|
27
|
+
# failure "Please provide a summary in the Pull Request description" if github.pr_body.length < 5
|
|
28
|
+
#
|
|
29
|
+
# @example Only accept PRs to the develop branch
|
|
30
|
+
#
|
|
31
|
+
# failure "Please re-submit this PR to develop, we may have already fixed your issue." if github.branch_for_base != "develop"
|
|
32
|
+
#
|
|
33
|
+
# @example Note when PRs don't reference a milestone, which goes away when it does
|
|
34
|
+
#
|
|
35
|
+
# has_milestone = github.pr_json["milestone"] != nil
|
|
36
|
+
# warn("This PR does not refer to an existing milestone", sticky: false) unless has_milestone
|
|
37
|
+
#
|
|
38
|
+
# @example Note when a PR cannot be manually merged, which goes away when you can
|
|
39
|
+
#
|
|
40
|
+
# can_merge = github.pr_json["mergeable"]
|
|
41
|
+
# warn("This PR cannot be merged yet.", sticky: false) unless can_merge
|
|
42
|
+
#
|
|
43
|
+
# @example Highlight when a celebrity makes a pull request
|
|
44
|
+
#
|
|
45
|
+
# message "Welcome, Danger." if github.pr_author == "dangermcshane"
|
|
46
|
+
#
|
|
47
|
+
# @example Ensure that all PRs have an assignee
|
|
48
|
+
#
|
|
49
|
+
# warn "This PR does not have any assignees yet." unless github.pr_json["assignee"]
|
|
50
|
+
#
|
|
51
|
+
# @example Send a message with links to a collection of specific files
|
|
52
|
+
#
|
|
53
|
+
# if git.modified_files.include? "config/*.js"
|
|
54
|
+
# config_files = git.modified_files.select { |path| path.include? "config/" }
|
|
55
|
+
# message "This PR changes #{ github.html_link(config_files) }"
|
|
56
|
+
# end
|
|
57
|
+
#
|
|
58
|
+
# @example Highlight with a clickable link if a Package.json is changed
|
|
59
|
+
#
|
|
60
|
+
# warn "#{github.html_link("Package.json")} was edited." if git.modified_files.include? "Package.json"
|
|
61
|
+
#
|
|
62
|
+
# @example Note an issue with a particular line on a file using the #L[num] syntax, e.g. `#L23`
|
|
63
|
+
#
|
|
64
|
+
# linter_json = `my_linter lint "file"`
|
|
65
|
+
# results = JSON.parse linter_json
|
|
66
|
+
# unless results.empty?
|
|
67
|
+
# file, line, warning = result.first
|
|
68
|
+
# warn "#{github.html_link("#{file}#L#{line}")} has linter issue: #{warning}."
|
|
69
|
+
# end
|
|
70
|
+
#
|
|
71
|
+
#
|
|
72
|
+
# @see danger/danger
|
|
73
|
+
# @tags core, github
|
|
74
|
+
#
|
|
75
|
+
class DangerfileGitHubPlugin < Plugin
|
|
76
|
+
# So that this init can fail.
|
|
77
|
+
def self.new(dangerfile)
|
|
78
|
+
return nil if dangerfile.env.request_source.class != Danger::RequestSources::GitHub
|
|
79
|
+
|
|
80
|
+
super
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def initialize(dangerfile)
|
|
84
|
+
super(dangerfile)
|
|
85
|
+
|
|
86
|
+
@github = dangerfile.env.request_source
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# The instance name used in the Dangerfile
|
|
90
|
+
# @return [String]
|
|
91
|
+
#
|
|
92
|
+
def self.instance_name
|
|
93
|
+
"github"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# @!group PR Review
|
|
97
|
+
#
|
|
98
|
+
# In Beta. Provides access to creating a GitHub Review instead of a typical GitHub comment.
|
|
99
|
+
#
|
|
100
|
+
# To use you announce the start of your review, and the end via the `start` and `submit` functions,
|
|
101
|
+
# for example:
|
|
102
|
+
#
|
|
103
|
+
# github.review.start
|
|
104
|
+
# github.review.fail(message)
|
|
105
|
+
# github.review.warn(message)
|
|
106
|
+
# github.review.message(message)
|
|
107
|
+
# github.review.markdown(message)
|
|
108
|
+
# github.review.submit
|
|
109
|
+
#
|
|
110
|
+
# @return [ReviewDSL]
|
|
111
|
+
def review
|
|
112
|
+
@github.review
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# @!group PR Metadata
|
|
116
|
+
# The title of the Pull Request.
|
|
117
|
+
# @return [String]
|
|
118
|
+
#
|
|
119
|
+
def pr_title
|
|
120
|
+
@github.pr_json["title"].to_s
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# @!group PR Metadata
|
|
124
|
+
# The body text of the Pull Request.
|
|
125
|
+
# @return [String]
|
|
126
|
+
#
|
|
127
|
+
def pr_body
|
|
128
|
+
pr_json["body"].to_s
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# @!group PR Metadata
|
|
132
|
+
# The username of the author of the Pull Request.
|
|
133
|
+
# @return [String]
|
|
134
|
+
#
|
|
135
|
+
def pr_author
|
|
136
|
+
pr_json["user"]["login"].to_s
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# @!group PR Metadata
|
|
140
|
+
# The labels assigned to the Pull Request.
|
|
141
|
+
# @return [String]
|
|
142
|
+
#
|
|
143
|
+
def pr_labels
|
|
144
|
+
@github.issue_json["labels"].map { |l| l[:name] }
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# @!group PR Metadata
|
|
148
|
+
# Whether the PR is a Draft.
|
|
149
|
+
# @return [Boolean]
|
|
150
|
+
#
|
|
151
|
+
def pr_draft?
|
|
152
|
+
pr_json["draft"] == true
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# @!group PR Commit Metadata
|
|
156
|
+
# The branch to which the PR is going to be merged into.
|
|
157
|
+
# @return [String]
|
|
158
|
+
#
|
|
159
|
+
def branch_for_base
|
|
160
|
+
pr_json["base"]["ref"]
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# @!group PR Commit Metadata
|
|
164
|
+
# The branch to which the PR is going to be merged from.
|
|
165
|
+
# @return [String]
|
|
166
|
+
#
|
|
167
|
+
def branch_for_head
|
|
168
|
+
pr_json["head"]["ref"]
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# @!group PR Commit Metadata
|
|
172
|
+
# The base commit to which the PR is going to be merged as a parent.
|
|
173
|
+
# @return [String]
|
|
174
|
+
#
|
|
175
|
+
def base_commit
|
|
176
|
+
pr_json["base"]["sha"]
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# @!group PR Commit Metadata
|
|
180
|
+
# The head commit to which the PR is requesting to be merged from.
|
|
181
|
+
# @return [String]
|
|
182
|
+
#
|
|
183
|
+
def head_commit
|
|
184
|
+
pr_json["head"]["sha"]
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# @!group GitHub Misc
|
|
188
|
+
# The hash that represents the PR's JSON. For an example of what this looks like
|
|
189
|
+
# see the [Danger Fixture'd one](https://raw.githubusercontent.com/danger/danger/master/spec/fixtures/github_api/pr_response.json).
|
|
190
|
+
# @return [Hash]
|
|
191
|
+
#
|
|
192
|
+
def pr_json
|
|
193
|
+
@github.pr_json
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# @!group GitHub Misc
|
|
197
|
+
# Provides access to the GitHub API client used inside Danger. Making
|
|
198
|
+
# it easy to use the GitHub API inside a Dangerfile.
|
|
199
|
+
# @return [Octokit::Client]
|
|
200
|
+
def api
|
|
201
|
+
@github.client
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# @!group PR Content
|
|
205
|
+
# The unified diff produced by Github for this PR
|
|
206
|
+
# see [Unified diff](https://en.wikipedia.org/wiki/Diff_utility#Unified_format)
|
|
207
|
+
# @return [String]
|
|
208
|
+
def pr_diff
|
|
209
|
+
@github.pr_diff
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# @!group GitHub Misc
|
|
213
|
+
# Returns a list of HTML anchors for a file, or files in the head repository. An example would be:
|
|
214
|
+
# `<a href='https://github.com/artsy/eigen/blob/561827e46167077b5e53515b4b7349b8ae04610b/file.txt'>file.txt</a>`. It returns a string of multiple anchors if passed an array.
|
|
215
|
+
# @param [String or Array<String>] paths
|
|
216
|
+
# A list of strings to convert to github anchors
|
|
217
|
+
# @param [Bool] full_path
|
|
218
|
+
# Shows the full path as the link's text, defaults to `true`.
|
|
219
|
+
#
|
|
220
|
+
# @return [String]
|
|
221
|
+
def html_link(paths, full_path: true)
|
|
222
|
+
paths = [paths] unless paths.kind_of?(Array)
|
|
223
|
+
commit = head_commit
|
|
224
|
+
repo = pr_json["head"]["repo"]["html_url"]
|
|
225
|
+
|
|
226
|
+
paths = paths.map do |path|
|
|
227
|
+
url_path = path.start_with?("/") ? path : "/#{path}"
|
|
228
|
+
text = full_path ? path : File.basename(path)
|
|
229
|
+
create_link("#{repo}/blob/#{commit}#{url_path}", text)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
return paths.first if paths.count < 2
|
|
233
|
+
|
|
234
|
+
paths.first(paths.count - 1).join(", ") + " & " + paths.last
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# @!group GitHub Misc
|
|
238
|
+
# Use to ignore inline messages which lay outside a diff's range, thereby not posting them in the main comment.
|
|
239
|
+
# You can set hash to change behavior per each kinds. (ex. `{warning: true, error: false}`)
|
|
240
|
+
# @param [Bool] or [Hash<Symbol, Bool>] dismiss
|
|
241
|
+
# Ignore out of range inline messages, defaults to `true`
|
|
242
|
+
#
|
|
243
|
+
# @return [void]
|
|
244
|
+
def dismiss_out_of_range_messages(dismiss = true)
|
|
245
|
+
if dismiss.kind_of?(Hash)
|
|
246
|
+
@github.dismiss_out_of_range_messages = dismiss
|
|
247
|
+
elsif dismiss.kind_of?(TrueClass)
|
|
248
|
+
@github.dismiss_out_of_range_messages = true
|
|
249
|
+
elsif dismiss.kind_of?(FalseClass)
|
|
250
|
+
@github.dismiss_out_of_range_messages = false
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
%i(title body author labels json).each do |suffix|
|
|
255
|
+
alias_method "mr_#{suffix}".to_sym, "pr_#{suffix}".to_sym
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
private
|
|
259
|
+
|
|
260
|
+
def create_link(href, text)
|
|
261
|
+
"<a href='#{href}'>#{text}</a>"
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|