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,17 @@
|
|
|
1
|
+
<%- @tables.each do |table| -%>
|
|
2
|
+
<%- if table[:content].any? || table[:resolved].any? -%>
|
|
3
|
+
| | |
|
|
4
|
+
|---|---|
|
|
5
|
+
<%- table[:content].each do |violation| -%>
|
|
6
|
+
| <%= @emoji_mapper.map(table[:emoji]) %> | <%= "~~" if table[:resolved] %><%= violation.message %><%= "~~" if table[:resolved] %> |
|
|
7
|
+
<%- end -%>
|
|
8
|
+
|
|
9
|
+
<%- end -%>
|
|
10
|
+
<%- end -%>
|
|
11
|
+
|
|
12
|
+
<%- @markdowns.each do |current| -%>
|
|
13
|
+
<%= current %>
|
|
14
|
+
<%# the previous line has to be aligned far to the left, otherwise markdown can break easily %>
|
|
15
|
+
<%- end -%>
|
|
16
|
+
|
|
17
|
+
Generated by :no_entry_sign: [Danger](https://danger.systems/ "generated_by_<%= @danger_id %>")
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require "danger/helpers/array_subclass"
|
|
2
|
+
|
|
3
|
+
module Danger
|
|
4
|
+
class FileList
|
|
5
|
+
include Helpers::ArraySubclass
|
|
6
|
+
|
|
7
|
+
# Information about pattern: http://ruby-doc.org/core-2.2.0/File.html#method-c-fnmatch
|
|
8
|
+
# e.g. "**/something.*" for any file called something with any extension
|
|
9
|
+
def include?(pattern)
|
|
10
|
+
self.each do |current|
|
|
11
|
+
if !current.nil? && (File.fnmatch(pattern, current, File::FNM_EXTGLOB) || pattern == current)
|
|
12
|
+
return true
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
return false
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class String
|
|
2
|
+
# @return [String] the plural form of self determined by count
|
|
3
|
+
def danger_pluralize(count)
|
|
4
|
+
"#{count} #{self}#{'s' unless count == 1}"
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
# @return [String] converts to underscored, lowercase form
|
|
8
|
+
def danger_underscore
|
|
9
|
+
self.gsub(/::/, "/".freeze).
|
|
10
|
+
gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2'.freeze).
|
|
11
|
+
gsub(/([a-z\d])([A-Z])/, '\1_\2'.freeze).
|
|
12
|
+
tr("-".freeze, "_".freeze).
|
|
13
|
+
downcase
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @return [String] truncates string with ellipsis when exceeding the limit
|
|
17
|
+
def danger_truncate(limit)
|
|
18
|
+
length > limit ? "#{self[0...limit]}..." : self
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
# So much was ripped direct from CocoaPods-Core - thanks!
|
|
2
|
+
|
|
3
|
+
require "danger/danger_core/dangerfile_dsl"
|
|
4
|
+
require "danger/danger_core/standard_error"
|
|
5
|
+
require "danger/danger_core/message_aggregator"
|
|
6
|
+
|
|
7
|
+
require "danger/danger_core/plugins/dangerfile_messaging_plugin"
|
|
8
|
+
require "danger/danger_core/plugins/dangerfile_danger_plugin"
|
|
9
|
+
require "danger/danger_core/plugins/dangerfile_git_plugin"
|
|
10
|
+
require "danger/danger_core/plugins/dangerfile_github_plugin"
|
|
11
|
+
require "danger/danger_core/plugins/dangerfile_gitlab_plugin"
|
|
12
|
+
require "danger/danger_core/plugins/dangerfile_bitbucket_server_plugin"
|
|
13
|
+
require "danger/danger_core/plugins/dangerfile_bitbucket_cloud_plugin"
|
|
14
|
+
require "danger/danger_core/plugins/dangerfile_vsts_plugin"
|
|
15
|
+
require "danger/danger_core/plugins/dangerfile_local_only_plugin"
|
|
16
|
+
|
|
17
|
+
module Danger
|
|
18
|
+
class Dangerfile
|
|
19
|
+
include Danger::Dangerfile::DSL
|
|
20
|
+
|
|
21
|
+
attr_accessor :env, :verbose, :plugins, :ui
|
|
22
|
+
|
|
23
|
+
# @return [Pathname] the path where the Dangerfile was loaded from. It is nil
|
|
24
|
+
# if the Dangerfile was generated programmatically.
|
|
25
|
+
#
|
|
26
|
+
attr_accessor :defined_in_file
|
|
27
|
+
|
|
28
|
+
# @return [String] a string useful to represent the Dangerfile in a message
|
|
29
|
+
# presented to the user.
|
|
30
|
+
#
|
|
31
|
+
def to_s
|
|
32
|
+
"Dangerfile"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# These are the classes that are allowed to also use method_missing
|
|
36
|
+
# in order to provide broader plugin support
|
|
37
|
+
def self.core_plugin_classes
|
|
38
|
+
[DangerfileMessagingPlugin]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# The ones that everything would break without
|
|
42
|
+
def self.essential_plugin_classes
|
|
43
|
+
[DangerfileMessagingPlugin, DangerfileGitPlugin, DangerfileDangerPlugin, DangerfileGitHubPlugin, DangerfileGitLabPlugin, DangerfileBitbucketServerPlugin, DangerfileBitbucketCloudPlugin, DangerfileVSTSPlugin, DangerfileLocalOnlyPlugin]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Both of these methods exist on all objects
|
|
47
|
+
# http://ruby-doc.org/core-2.2.3/Kernel.html#method-i-warn
|
|
48
|
+
# http://ruby-doc.org/core-2.2.3/Kernel.html#method-i-fail
|
|
49
|
+
# However, as we're using using them in the DSL, they won't
|
|
50
|
+
# get method_missing called correctly without overriding them.
|
|
51
|
+
|
|
52
|
+
def warn(*args, **kargs, &blk)
|
|
53
|
+
method_missing(:warn, *args, **kargs, &blk)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def fail(*args, **kargs, &blk)
|
|
57
|
+
method_missing(:fail, *args, **kargs, &blk)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# When an undefined method is called, we check to see if it's something
|
|
61
|
+
# that the core DSLs have, then starts looking at plugins support.
|
|
62
|
+
|
|
63
|
+
def method_missing(method_sym, *arguments, **keyword_arguments, &_block)
|
|
64
|
+
@core_plugins.each do |plugin|
|
|
65
|
+
if plugin.public_methods(false).include?(method_sym)
|
|
66
|
+
if keyword_arguments.empty?
|
|
67
|
+
return plugin.send(method_sym, *arguments)
|
|
68
|
+
else
|
|
69
|
+
return plugin.send(method_sym, *arguments, **keyword_arguments)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
super
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# cork_board not being set comes from plugins #585
|
|
77
|
+
def initialize(env_manager, cork_board = nil)
|
|
78
|
+
@plugins = {}
|
|
79
|
+
@core_plugins = []
|
|
80
|
+
@ui = cork_board || Cork::Board.new(silent: false, verbose: false)
|
|
81
|
+
|
|
82
|
+
# Triggers the core plugins
|
|
83
|
+
@env = env_manager
|
|
84
|
+
|
|
85
|
+
# Triggers local plugins from the root of a project
|
|
86
|
+
Dir["./danger_plugins/*.rb"].each do |file|
|
|
87
|
+
require File.expand_path(file)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
refresh_plugins if env_manager.pr?
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Iterate through available plugin classes and initialize them with
|
|
94
|
+
# a reference to this Dangerfile
|
|
95
|
+
def refresh_plugins
|
|
96
|
+
plugins = Plugin.all_plugins
|
|
97
|
+
plugins.each do |klass|
|
|
98
|
+
next if klass.respond_to?(:singleton_class?) && klass.singleton_class?
|
|
99
|
+
|
|
100
|
+
plugin = klass.new(self)
|
|
101
|
+
next if plugin.nil? || @plugins[klass]
|
|
102
|
+
|
|
103
|
+
name = plugin.class.instance_name
|
|
104
|
+
self.class.send(:attr_reader, name)
|
|
105
|
+
instance_variable_set("@#{name}", plugin)
|
|
106
|
+
|
|
107
|
+
@plugins[klass] = plugin
|
|
108
|
+
@core_plugins << plugin if self.class.core_plugin_classes.include? klass
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
alias init_plugins refresh_plugins
|
|
112
|
+
|
|
113
|
+
def core_dsl_attributes
|
|
114
|
+
@core_plugins.map { |plugin| { plugin: plugin, methods: plugin.public_methods(false) } }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def external_dsl_attributes
|
|
118
|
+
plugins.values.reject { |plugin| @core_plugins.include? plugin }.map { |plugin| { plugin: plugin, methods: plugin.public_methods(false) } }
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def method_values_for_plugin_hashes(plugin_hashes)
|
|
122
|
+
plugin_hashes.flat_map do |plugin_hash|
|
|
123
|
+
plugin = plugin_hash[:plugin]
|
|
124
|
+
methods = plugin_hash[:methods].select { |name| plugin.method(name).parameters.empty? }
|
|
125
|
+
|
|
126
|
+
methods.map do |method|
|
|
127
|
+
case method
|
|
128
|
+
when :api
|
|
129
|
+
value = "Octokit::Client"
|
|
130
|
+
|
|
131
|
+
when :pr_json, :mr_json
|
|
132
|
+
value = "[Skipped JSON]"
|
|
133
|
+
|
|
134
|
+
when :pr_diff, :mr_diff
|
|
135
|
+
value = "[Skipped Diff]"
|
|
136
|
+
|
|
137
|
+
else
|
|
138
|
+
value = plugin.send(method)
|
|
139
|
+
value = wrap_text(value.encode("utf-8")) if value.kind_of?(String)
|
|
140
|
+
# So that we either have one value per row
|
|
141
|
+
# or we have [] for an empty array
|
|
142
|
+
value = value.join("\n") if value.kind_of?(Array) && value.count > 0
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
[method.to_s, value]
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Iterates through the DSL's attributes, and table's the output
|
|
151
|
+
def print_known_info
|
|
152
|
+
rows = []
|
|
153
|
+
rows += method_values_for_plugin_hashes(core_dsl_attributes)
|
|
154
|
+
rows << ["---", "---"]
|
|
155
|
+
rows += method_values_for_plugin_hashes(external_dsl_attributes)
|
|
156
|
+
rows << ["---", "---"]
|
|
157
|
+
rows << ["SCM", env.scm.class]
|
|
158
|
+
rows << ["Source", env.ci_source.class]
|
|
159
|
+
rows << ["Requests", env.request_source.class]
|
|
160
|
+
rows << ["Base Commit", env.meta_info_for_base]
|
|
161
|
+
rows << ["Head Commit", env.meta_info_for_head]
|
|
162
|
+
|
|
163
|
+
params = {}
|
|
164
|
+
params[:rows] = rows.each { |current| current[0] = current[0].yellow }
|
|
165
|
+
params[:title] = "Danger v#{Danger::VERSION}\nDSL Attributes".green
|
|
166
|
+
|
|
167
|
+
ui.section("Info:") do
|
|
168
|
+
ui.puts
|
|
169
|
+
table = Terminal::Table.new(params)
|
|
170
|
+
table.align_column(0, :right)
|
|
171
|
+
ui.puts table
|
|
172
|
+
ui.puts
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Parses the file at a path, optionally takes the content of the file for DI
|
|
177
|
+
#
|
|
178
|
+
def parse(path, contents = nil)
|
|
179
|
+
print_known_info if verbose
|
|
180
|
+
|
|
181
|
+
contents ||= File.open(path, "r:utf-8", &:read)
|
|
182
|
+
|
|
183
|
+
# Work around for Rubinius incomplete encoding in 1.9 mode
|
|
184
|
+
if contents.respond_to?(:encoding) && contents.encoding.name != "UTF-8"
|
|
185
|
+
contents.encode!("UTF-8")
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
if contents.tr!("“”‘’‛", %(""'''))
|
|
189
|
+
# Changes have been made
|
|
190
|
+
ui.puts "Your #{path.basename} has had smart quotes sanitised. " \
|
|
191
|
+
"To avoid issues in the future, you should not use " \
|
|
192
|
+
"TextEdit for editing it. If you are not using TextEdit, " \
|
|
193
|
+
"you should turn off smart quotes in your editor of choice.".red
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
if contents.include?("puts")
|
|
197
|
+
ui.puts "You used `puts` in your Dangerfile. To print out text to GitHub use `message` instead"
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
self.defined_in_file = path
|
|
201
|
+
instance_eval do
|
|
202
|
+
# rubocop:disable Lint/RescueException
|
|
203
|
+
|
|
204
|
+
eval_file(contents, path)
|
|
205
|
+
rescue Exception => e
|
|
206
|
+
message = "Invalid `#{path.basename}` file: #{e.message}"
|
|
207
|
+
raise DSLError.new(message, path, e.backtrace, contents)
|
|
208
|
+
|
|
209
|
+
# rubocop:enable Lint/RescueException
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def print_results
|
|
214
|
+
status = status_report
|
|
215
|
+
violations = violation_report
|
|
216
|
+
return if (violations[:errors] + violations[:warnings] + violations[:messages] + status[:markdowns]).count.zero?
|
|
217
|
+
|
|
218
|
+
ui.section("Results:") do
|
|
219
|
+
%i(errors warnings messages).each do |key|
|
|
220
|
+
formatted = key.to_s.capitalize + ":"
|
|
221
|
+
title = case key
|
|
222
|
+
when :errors
|
|
223
|
+
formatted.red
|
|
224
|
+
when :warnings
|
|
225
|
+
formatted.yellow
|
|
226
|
+
else
|
|
227
|
+
formatted
|
|
228
|
+
end
|
|
229
|
+
rows = violations[key].uniq
|
|
230
|
+
print_list(title, rows)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
if status[:markdowns].count > 0
|
|
234
|
+
ui.title("Markdown:") do
|
|
235
|
+
status[:markdowns].each do |current_markdown|
|
|
236
|
+
ui.puts "#{current_markdown.file}\#L#{current_markdown.line}" if current_markdown.file && current_markdown.line
|
|
237
|
+
ui.puts current_markdown.message
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def failed?
|
|
245
|
+
violation_report[:errors].count > 0
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def post_results(danger_id, new_comment, remove_previous_comments)
|
|
249
|
+
violations = violation_report
|
|
250
|
+
report = {
|
|
251
|
+
warnings: violations[:warnings].uniq,
|
|
252
|
+
errors: violations[:errors].uniq,
|
|
253
|
+
messages: violations[:messages].uniq,
|
|
254
|
+
markdowns: status_report[:markdowns].uniq,
|
|
255
|
+
danger_id: danger_id
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if env.request_source.respond_to?(:update_pr_by_line!) && ENV["DANGER_MESSAGE_AGGREGATION"]
|
|
259
|
+
env.request_source.update_pr_by_line!(message_groups: MessageAggregator.aggregate(**report),
|
|
260
|
+
new_comment: new_comment,
|
|
261
|
+
remove_previous_comments: remove_previous_comments,
|
|
262
|
+
danger_id: report[:danger_id])
|
|
263
|
+
else
|
|
264
|
+
env.request_source.update_pull_request!(
|
|
265
|
+
**report,
|
|
266
|
+
new_comment: new_comment,
|
|
267
|
+
remove_previous_comments: remove_previous_comments
|
|
268
|
+
)
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def setup_for_running(base_branch, head_branch)
|
|
273
|
+
env.ensure_danger_branches_are_setup
|
|
274
|
+
env.scm.diff_for_folder(".".freeze, from: base_branch, to: head_branch, lookup_top_level: true)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def run(base_branch, head_branch, dangerfile_path, danger_id, new_comment, remove_previous_comments, report_results = true)
|
|
278
|
+
# Setup internal state
|
|
279
|
+
init_plugins
|
|
280
|
+
env.fill_environment_vars
|
|
281
|
+
|
|
282
|
+
begin
|
|
283
|
+
# Sets up the git environment
|
|
284
|
+
setup_for_running(base_branch, head_branch)
|
|
285
|
+
|
|
286
|
+
# Parse the local Dangerfile
|
|
287
|
+
parse(Pathname.new(dangerfile_path))
|
|
288
|
+
|
|
289
|
+
# Push results to the API
|
|
290
|
+
# Pass along the details of the run to the request source
|
|
291
|
+
# to send back to the code review site.
|
|
292
|
+
post_results(danger_id, new_comment, remove_previous_comments) if report_results
|
|
293
|
+
|
|
294
|
+
# Print results in the terminal
|
|
295
|
+
print_results
|
|
296
|
+
rescue DSLError => e
|
|
297
|
+
# Push exception to the API and re-raise
|
|
298
|
+
post_exception(e, danger_id, new_comment) unless danger_id.nil?
|
|
299
|
+
raise
|
|
300
|
+
ensure
|
|
301
|
+
# Makes sure that Danger specific git branches are cleaned
|
|
302
|
+
env.clean_up
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
failed?
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
private
|
|
309
|
+
|
|
310
|
+
def eval_file(contents, path)
|
|
311
|
+
eval(contents, nil, path.to_s) # rubocop:disable Security/Eval
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def print_list(title, rows)
|
|
315
|
+
unless rows.empty?
|
|
316
|
+
ui.title(title) do
|
|
317
|
+
rows.each do |row|
|
|
318
|
+
if row.file && row.line
|
|
319
|
+
path = "#{row.file}\#L#{row.line}: "
|
|
320
|
+
else
|
|
321
|
+
path = ""
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
ui.puts("- [ ] #{path}#{row.message}")
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def wrap_text(text, width = 80)
|
|
331
|
+
text.gsub(/.{,#{width}}/) do |line|
|
|
332
|
+
line.strip!
|
|
333
|
+
"#{line}\n"
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def post_exception(ex, danger_id, new_comment)
|
|
338
|
+
return if ENV["DANGER_DO_NOT_POST_INVALID_DANGERFILE_ERROR"]
|
|
339
|
+
return if danger_id.nil?
|
|
340
|
+
|
|
341
|
+
env.request_source.update_pull_request!(
|
|
342
|
+
danger_id: danger_id,
|
|
343
|
+
new_comment: new_comment,
|
|
344
|
+
markdowns: [ex.to_markdown]
|
|
345
|
+
)
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Danger
|
|
2
|
+
class Dangerfile
|
|
3
|
+
# Anything inside this module is considered public API, and in the future
|
|
4
|
+
# documentation will be generated from it via rdoc.
|
|
5
|
+
|
|
6
|
+
module DSL
|
|
7
|
+
# @!group Danger Zone
|
|
8
|
+
# Provides access to the raw Travis/Circle/Buildkite/GitHub objects, which
|
|
9
|
+
# you can use to pull out extra bits of information. _Warning_
|
|
10
|
+
# the interfaces of these objects is **not** considered a part of the Dangerfile public
|
|
11
|
+
# API, and is viable to change occasionally on the whims of developers.
|
|
12
|
+
# @return [EnvironmentManager]
|
|
13
|
+
|
|
14
|
+
attr_reader :env
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def initialize
|
|
19
|
+
load_default_plugins
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def load_default_plugins
|
|
23
|
+
Dir["./danger_plugins/*.rb"].each do |file|
|
|
24
|
+
require File.expand_path(file)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module Danger
|
|
2
|
+
class DangerfileGenerator
|
|
3
|
+
# returns the string for a Dangerfile based on a folder's contents'
|
|
4
|
+
def self.create_dangerfile(_path, _ui)
|
|
5
|
+
# Use this template for now, but this is a really ripe place to
|
|
6
|
+
# improve now!
|
|
7
|
+
dir = Danger.gem_path
|
|
8
|
+
File.read(File.join(dir, "lib", "assets", "DangerfileTemplate"))
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
require "danger/ci_source/ci_source"
|
|
2
|
+
require "danger/request_sources/request_source"
|
|
3
|
+
|
|
4
|
+
module Danger
|
|
5
|
+
class EnvironmentManager
|
|
6
|
+
attr_accessor :ci_source, :request_source, :scm, :ui, :danger_id
|
|
7
|
+
|
|
8
|
+
# Finds a Danger::CI class based on the ENV
|
|
9
|
+
def self.local_ci_source(env)
|
|
10
|
+
CI.available_ci_sources.find { |ci| ci.validates_as_ci? env }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Uses the current Danger::CI subclass, and sees if it is a PR
|
|
14
|
+
def self.pr?(env)
|
|
15
|
+
local_ci_source(env).validates_as_pr?(env)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @return [String] danger's default head branch
|
|
19
|
+
def self.danger_head_branch
|
|
20
|
+
"danger_head".freeze
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [String] danger's default base branch
|
|
24
|
+
def self.danger_base_branch
|
|
25
|
+
"danger_base".freeze
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def initialize(env, ui = nil, danger_id = "danger")
|
|
29
|
+
ci_klass = self.class.local_ci_source(env)
|
|
30
|
+
self.ci_source = ci_klass.new(env)
|
|
31
|
+
self.ui = ui || Cork::Board.new(silent: false, verbose: false)
|
|
32
|
+
self.danger_id = danger_id
|
|
33
|
+
|
|
34
|
+
RequestSources::RequestSource.available_request_sources.each do |klass|
|
|
35
|
+
next unless self.ci_source.supports?(klass)
|
|
36
|
+
|
|
37
|
+
request_source = klass.new(self.ci_source, env)
|
|
38
|
+
next unless request_source.validates_as_ci?
|
|
39
|
+
next unless request_source.validates_as_api_source?
|
|
40
|
+
|
|
41
|
+
self.request_source = request_source
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
raise_error_for_no_request_source(env, self.ui) unless self.request_source
|
|
45
|
+
self.scm = self.request_source.scm
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def pr?
|
|
49
|
+
self.ci_source != nil
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def fill_environment_vars
|
|
53
|
+
request_source.fetch_details
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def ensure_danger_branches_are_setup
|
|
57
|
+
clean_up
|
|
58
|
+
|
|
59
|
+
self.request_source.setup_danger_branches
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def clean_up
|
|
63
|
+
[EnvironmentManager.danger_base_branch, EnvironmentManager.danger_head_branch].each do |branch|
|
|
64
|
+
scm.exec("branch -D #{branch}") unless scm.exec("rev-parse --quiet --verify #{branch}").empty?
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def meta_info_for_head
|
|
69
|
+
scm.exec("--no-pager log #{EnvironmentManager.danger_head_branch} -n1")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def meta_info_for_base
|
|
73
|
+
scm.exec("--no-pager log #{EnvironmentManager.danger_base_branch} -n1")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def raise_error_for_no_request_source(env, ui)
|
|
77
|
+
title, subtitle = extract_title_and_subtitle_from_source(ci_source.repo_url)
|
|
78
|
+
subtitle += travis_note if env["TRAVIS_SECURE_ENV_VARS"] == "true"
|
|
79
|
+
|
|
80
|
+
ui_display_no_request_source_error_message(ui, env, title, subtitle)
|
|
81
|
+
|
|
82
|
+
exit(1)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def get_repo_source(repo_url)
|
|
88
|
+
if repo_url =~ /github/i
|
|
89
|
+
RequestSources::GitHub
|
|
90
|
+
elsif repo_url =~ /gitlab/i
|
|
91
|
+
RequestSources::GitLab
|
|
92
|
+
elsif repo_url =~ /bitbucket\.(org|com)/i
|
|
93
|
+
RequestSources::BitbucketCloud
|
|
94
|
+
elsif repo_url =~ /\.visualstudio\.com/i || repo_url =~ /dev\.azure\.com/i
|
|
95
|
+
RequestSources::VSTS
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def extract_title_and_subtitle_from_source(repo_url)
|
|
100
|
+
source = get_repo_source(repo_url)
|
|
101
|
+
|
|
102
|
+
if source
|
|
103
|
+
title = "For your #{source.source_name} repo, you need to expose: " + source.env_vars.join(", ").yellow
|
|
104
|
+
subtitle = "You may also need: #{source.optional_env_vars.join(', ')}" if source.optional_env_vars.any?
|
|
105
|
+
else
|
|
106
|
+
title = "For Danger to run on this project, you need to expose a set of following the ENV vars:\n#{RequestSources::RequestSource.available_source_names_and_envs.join("\n")}"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
[title, (subtitle || "")]
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def ui_display_no_request_source_error_message(ui, env, title, subtitle)
|
|
113
|
+
ui.title "Could not set up API to Code Review site for Danger\n".freeze
|
|
114
|
+
ui.puts title
|
|
115
|
+
ui.puts subtitle
|
|
116
|
+
ui.puts "\nFound these keys in your ENV: #{env.keys.join(', '.freeze)}."
|
|
117
|
+
ui.puts "\nFailing the build, Danger cannot run without API access.".freeze
|
|
118
|
+
ui.puts "You can see more information at https://danger.systems/guides/getting_started.html".freeze
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def travis_note
|
|
122
|
+
"\nTravis note: If you have an open source project, you should ensure 'Display value in build log' enabled for these flags, so that PRs from forks work." \
|
|
123
|
+
"\nThis also means that people can see this token, so this account should have no write access to repos."
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
module Danger
|
|
2
|
+
class Executor
|
|
3
|
+
def initialize(system_env)
|
|
4
|
+
@system_env = system_env
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def run(env: nil,
|
|
8
|
+
dm: nil,
|
|
9
|
+
cork: nil,
|
|
10
|
+
base: nil,
|
|
11
|
+
head: nil,
|
|
12
|
+
dangerfile_path: nil,
|
|
13
|
+
danger_id: nil,
|
|
14
|
+
new_comment: nil,
|
|
15
|
+
fail_on_errors: nil,
|
|
16
|
+
fail_if_no_pr: nil,
|
|
17
|
+
remove_previous_comments: nil)
|
|
18
|
+
# Create a silent Cork instance if cork is nil, as it's likely a test
|
|
19
|
+
cork ||= Cork::Board.new(silent: false, verbose: false)
|
|
20
|
+
|
|
21
|
+
# Run some validations
|
|
22
|
+
validate!(cork, fail_if_no_pr: fail_if_no_pr)
|
|
23
|
+
|
|
24
|
+
# OK, we now know that Danger can run in this environment
|
|
25
|
+
env ||= EnvironmentManager.new(system_env, cork, danger_id)
|
|
26
|
+
dm ||= Dangerfile.new(env, cork)
|
|
27
|
+
|
|
28
|
+
ran_status = dm.run(
|
|
29
|
+
base_branch(base),
|
|
30
|
+
head_branch(head),
|
|
31
|
+
dangerfile_path,
|
|
32
|
+
danger_id,
|
|
33
|
+
new_comment,
|
|
34
|
+
remove_previous_comments
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# By default Danger will use the status API to fail a build,
|
|
38
|
+
# allowing execution to continue, this behavior isn't always
|
|
39
|
+
# optimal for everyone.
|
|
40
|
+
exit(1) if fail_on_errors && ran_status
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def validate!(cork, fail_if_no_pr: false)
|
|
44
|
+
validate_ci!
|
|
45
|
+
validate_pr!(cork, fail_if_no_pr)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
attr_reader :system_env
|
|
51
|
+
|
|
52
|
+
# Could we find a CI source at all?
|
|
53
|
+
def validate_ci!
|
|
54
|
+
unless EnvironmentManager.local_ci_source(system_env)
|
|
55
|
+
abort("Could not find the type of CI for Danger to run on.".red)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Could we determine that the CI source is inside a PR?
|
|
60
|
+
def validate_pr!(cork, fail_if_no_pr)
|
|
61
|
+
unless EnvironmentManager.pr?(system_env)
|
|
62
|
+
ci_name = EnvironmentManager.local_ci_source(system_env).name.split("::").last
|
|
63
|
+
|
|
64
|
+
msg = "Not a #{ci_name} #{commit_request(ci_name)} - skipping `danger` run. "
|
|
65
|
+
# circle won't run danger properly if the commit is pushed and build runs before the PR exists
|
|
66
|
+
# https://danger.systems/guides/troubleshooting.html#circle-ci-doesnt-run-my-build-consistently
|
|
67
|
+
# the best solution is to enable `fail_if_no_pr`, and then re-run the job once the PR is up
|
|
68
|
+
if ci_name == "CircleCI"
|
|
69
|
+
msg << "If you only created the PR recently, try re-running your workflow."
|
|
70
|
+
end
|
|
71
|
+
cork.puts msg.strip.yellow
|
|
72
|
+
|
|
73
|
+
exit(fail_if_no_pr ? 1 : 0)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def base_branch(user_specified_base_branch)
|
|
78
|
+
user_specified_base_branch || EnvironmentManager.danger_base_branch
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def head_branch(user_specified_head_branch)
|
|
82
|
+
user_specified_head_branch || EnvironmentManager.danger_head_branch
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def commit_request(ci_name)
|
|
86
|
+
return "Merge Request" if ci_name == "GitLabCI"
|
|
87
|
+
|
|
88
|
+
return "Pull Request"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|