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.
Files changed (127) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +93 -0
  4. data/bin/danger +5 -0
  5. data/lib/assets/DangerfileTemplate +13 -0
  6. data/lib/danger/ci_source/appcenter.rb +55 -0
  7. data/lib/danger/ci_source/appcircle.rb +83 -0
  8. data/lib/danger/ci_source/appveyor.rb +64 -0
  9. data/lib/danger/ci_source/azure_pipelines.rb +61 -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 +78 -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 +71 -0
  20. data/lib/danger/ci_source/codefresh.rb +47 -0
  21. data/lib/danger/ci_source/codemagic.rb +58 -0
  22. data/lib/danger/ci_source/codeship.rb +44 -0
  23. data/lib/danger/ci_source/concourse.rb +60 -0
  24. data/lib/danger/ci_source/custom_ci_with_github.rb +49 -0
  25. data/lib/danger/ci_source/dotci.rb +50 -0
  26. data/lib/danger/ci_source/drone.rb +71 -0
  27. data/lib/danger/ci_source/github_actions.rb +44 -0
  28. data/lib/danger/ci_source/gitlab_ci.rb +89 -0
  29. data/lib/danger/ci_source/jenkins.rb +148 -0
  30. data/lib/danger/ci_source/local_git_repo.rb +117 -0
  31. data/lib/danger/ci_source/local_only_git_repo.rb +44 -0
  32. data/lib/danger/ci_source/screwdriver.rb +48 -0
  33. data/lib/danger/ci_source/semaphore.rb +37 -0
  34. data/lib/danger/ci_source/support/commits.rb +19 -0
  35. data/lib/danger/ci_source/support/find_repo_info_from_logs.rb +35 -0
  36. data/lib/danger/ci_source/support/find_repo_info_from_url.rb +43 -0
  37. data/lib/danger/ci_source/support/local_pull_request.rb +14 -0
  38. data/lib/danger/ci_source/support/no_pull_request.rb +7 -0
  39. data/lib/danger/ci_source/support/no_repo_info.rb +5 -0
  40. data/lib/danger/ci_source/support/pull_request_finder.rb +190 -0
  41. data/lib/danger/ci_source/support/remote_pull_request.rb +15 -0
  42. data/lib/danger/ci_source/support/repo_info.rb +10 -0
  43. data/lib/danger/ci_source/surf.rb +37 -0
  44. data/lib/danger/ci_source/teamcity.rb +163 -0
  45. data/lib/danger/ci_source/travis.rb +51 -0
  46. data/lib/danger/ci_source/xcode_cloud.rb +38 -0
  47. data/lib/danger/ci_source/xcode_server.rb +48 -0
  48. data/lib/danger/clients/rubygems_client.rb +14 -0
  49. data/lib/danger/commands/dangerfile/gem.rb +43 -0
  50. data/lib/danger/commands/dangerfile/init.rb +30 -0
  51. data/lib/danger/commands/dry_run.rb +54 -0
  52. data/lib/danger/commands/init.rb +297 -0
  53. data/lib/danger/commands/init_helpers/interviewer.rb +92 -0
  54. data/lib/danger/commands/local.rb +83 -0
  55. data/lib/danger/commands/local_helpers/http_cache.rb +38 -0
  56. data/lib/danger/commands/local_helpers/local_setup.rb +48 -0
  57. data/lib/danger/commands/local_helpers/pry_setup.rb +32 -0
  58. data/lib/danger/commands/plugins/plugin_json.rb +44 -0
  59. data/lib/danger/commands/plugins/plugin_lint.rb +52 -0
  60. data/lib/danger/commands/plugins/plugin_readme.rb +42 -0
  61. data/lib/danger/commands/pr.rb +93 -0
  62. data/lib/danger/commands/runner.rb +94 -0
  63. data/lib/danger/commands/staging.rb +53 -0
  64. data/lib/danger/commands/systems.rb +41 -0
  65. data/lib/danger/comment_generators/bitbucket_server.md.erb +20 -0
  66. data/lib/danger/comment_generators/bitbucket_server_inline.md.erb +15 -0
  67. data/lib/danger/comment_generators/bitbucket_server_message_group.md.erb +12 -0
  68. data/lib/danger/comment_generators/github.md.erb +55 -0
  69. data/lib/danger/comment_generators/github_inline.md.erb +26 -0
  70. data/lib/danger/comment_generators/gitlab.md.erb +40 -0
  71. data/lib/danger/comment_generators/gitlab_inline.md.erb +21 -0
  72. data/lib/danger/comment_generators/vsts.md.erb +20 -0
  73. data/lib/danger/comment_generators/vsts_inline.md.erb +17 -0
  74. data/lib/danger/core_ext/file_list.rb +18 -0
  75. data/lib/danger/core_ext/string.rb +20 -0
  76. data/lib/danger/danger_core/dangerfile.rb +348 -0
  77. data/lib/danger/danger_core/dangerfile_dsl.rb +29 -0
  78. data/lib/danger/danger_core/dangerfile_generator.rb +11 -0
  79. data/lib/danger/danger_core/environment_manager.rb +126 -0
  80. data/lib/danger/danger_core/executor.rb +91 -0
  81. data/lib/danger/danger_core/message_aggregator.rb +50 -0
  82. data/lib/danger/danger_core/message_group.rb +68 -0
  83. data/lib/danger/danger_core/messages/base.rb +57 -0
  84. data/lib/danger/danger_core/messages/markdown.rb +41 -0
  85. data/lib/danger/danger_core/messages/violation.rb +53 -0
  86. data/lib/danger/danger_core/plugins/dangerfile_bitbucket_cloud_plugin.rb +142 -0
  87. data/lib/danger/danger_core/plugins/dangerfile_bitbucket_server_plugin.rb +211 -0
  88. data/lib/danger/danger_core/plugins/dangerfile_danger_plugin.rb +274 -0
  89. data/lib/danger/danger_core/plugins/dangerfile_git_plugin.rb +159 -0
  90. data/lib/danger/danger_core/plugins/dangerfile_github_plugin.rb +264 -0
  91. data/lib/danger/danger_core/plugins/dangerfile_gitlab_plugin.rb +275 -0
  92. data/lib/danger/danger_core/plugins/dangerfile_local_only_plugin.rb +43 -0
  93. data/lib/danger/danger_core/plugins/dangerfile_messaging_plugin.rb +220 -0
  94. data/lib/danger/danger_core/plugins/dangerfile_vsts_plugin.rb +191 -0
  95. data/lib/danger/danger_core/standard_error.rb +142 -0
  96. data/lib/danger/helpers/array_subclass.rb +61 -0
  97. data/lib/danger/helpers/comment.rb +32 -0
  98. data/lib/danger/helpers/comments_helper.rb +179 -0
  99. data/lib/danger/helpers/comments_parsing_helper.rb +71 -0
  100. data/lib/danger/helpers/emoji_mapper.rb +41 -0
  101. data/lib/danger/helpers/find_max_num_violations.rb +31 -0
  102. data/lib/danger/helpers/message_groups_array_helper.rb +31 -0
  103. data/lib/danger/plugin_support/gems_resolver.rb +77 -0
  104. data/lib/danger/plugin_support/plugin.rb +52 -0
  105. data/lib/danger/plugin_support/plugin_file_resolver.rb +30 -0
  106. data/lib/danger/plugin_support/plugin_linter.rb +162 -0
  107. data/lib/danger/plugin_support/plugin_parser.rb +199 -0
  108. data/lib/danger/plugin_support/templates/readme_table.html.erb +26 -0
  109. data/lib/danger/request_sources/bitbucket_cloud.rb +169 -0
  110. data/lib/danger/request_sources/bitbucket_cloud_api.rb +181 -0
  111. data/lib/danger/request_sources/bitbucket_server.rb +210 -0
  112. data/lib/danger/request_sources/bitbucket_server_api.rb +129 -0
  113. data/lib/danger/request_sources/code_insights_api.rb +142 -0
  114. data/lib/danger/request_sources/github/github.rb +535 -0
  115. data/lib/danger/request_sources/github/github_review.rb +127 -0
  116. data/lib/danger/request_sources/github/github_review_resolver.rb +17 -0
  117. data/lib/danger/request_sources/github/github_review_unsupported.rb +23 -0
  118. data/lib/danger/request_sources/gitlab.rb +557 -0
  119. data/lib/danger/request_sources/local_only.rb +50 -0
  120. data/lib/danger/request_sources/request_source.rb +97 -0
  121. data/lib/danger/request_sources/support/get_ignored_violation.rb +17 -0
  122. data/lib/danger/request_sources/vsts.rb +278 -0
  123. data/lib/danger/request_sources/vsts_api.rb +172 -0
  124. data/lib/danger/scm_source/git_repo.rb +198 -0
  125. data/lib/danger/version.rb +4 -0
  126. data/lib/danger.rb +45 -0
  127. 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