danger 8.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +94 -0
  4. data/bin/danger +5 -0
  5. data/lib/assets/DangerfileTemplate +13 -0
  6. data/lib/danger.rb +44 -0
  7. data/lib/danger/ci_source/appcenter.rb +55 -0
  8. data/lib/danger/ci_source/appveyor.rb +60 -0
  9. data/lib/danger/ci_source/azure_pipelines.rb +44 -0
  10. data/lib/danger/ci_source/bamboo.rb +41 -0
  11. data/lib/danger/ci_source/bitbucket_pipelines.rb +37 -0
  12. data/lib/danger/ci_source/bitrise.rb +65 -0
  13. data/lib/danger/ci_source/buddybuild.rb +62 -0
  14. data/lib/danger/ci_source/buildkite.rb +51 -0
  15. data/lib/danger/ci_source/ci_source.rb +37 -0
  16. data/lib/danger/ci_source/circle.rb +94 -0
  17. data/lib/danger/ci_source/circle_api.rb +51 -0
  18. data/lib/danger/ci_source/cirrus.rb +31 -0
  19. data/lib/danger/ci_source/code_build.rb +57 -0
  20. data/lib/danger/ci_source/codefresh.rb +53 -0
  21. data/lib/danger/ci_source/codeship.rb +44 -0
  22. data/lib/danger/ci_source/dotci.rb +52 -0
  23. data/lib/danger/ci_source/drone.rb +71 -0
  24. data/lib/danger/ci_source/github_actions.rb +43 -0
  25. data/lib/danger/ci_source/gitlab_ci.rb +86 -0
  26. data/lib/danger/ci_source/jenkins.rb +149 -0
  27. data/lib/danger/ci_source/local_git_repo.rb +119 -0
  28. data/lib/danger/ci_source/local_only_git_repo.rb +47 -0
  29. data/lib/danger/ci_source/screwdriver.rb +47 -0
  30. data/lib/danger/ci_source/semaphore.rb +37 -0
  31. data/lib/danger/ci_source/support/commits.rb +17 -0
  32. data/lib/danger/ci_source/support/find_repo_info_from_logs.rb +35 -0
  33. data/lib/danger/ci_source/support/find_repo_info_from_url.rb +42 -0
  34. data/lib/danger/ci_source/support/local_pull_request.rb +14 -0
  35. data/lib/danger/ci_source/support/no_pull_request.rb +7 -0
  36. data/lib/danger/ci_source/support/no_repo_info.rb +5 -0
  37. data/lib/danger/ci_source/support/pull_request_finder.rb +179 -0
  38. data/lib/danger/ci_source/support/remote_pull_request.rb +15 -0
  39. data/lib/danger/ci_source/support/repo_info.rb +10 -0
  40. data/lib/danger/ci_source/surf.rb +37 -0
  41. data/lib/danger/ci_source/teamcity.rb +159 -0
  42. data/lib/danger/ci_source/travis.rb +51 -0
  43. data/lib/danger/ci_source/vsts.rb +73 -0
  44. data/lib/danger/ci_source/xcode_server.rb +48 -0
  45. data/lib/danger/clients/rubygems_client.rb +14 -0
  46. data/lib/danger/commands/dangerfile/gem.rb +43 -0
  47. data/lib/danger/commands/dangerfile/init.rb +30 -0
  48. data/lib/danger/commands/dry_run.rb +54 -0
  49. data/lib/danger/commands/init.rb +297 -0
  50. data/lib/danger/commands/init_helpers/interviewer.rb +92 -0
  51. data/lib/danger/commands/local.rb +83 -0
  52. data/lib/danger/commands/local_helpers/http_cache.rb +36 -0
  53. data/lib/danger/commands/local_helpers/local_setup.rb +46 -0
  54. data/lib/danger/commands/local_helpers/pry_setup.rb +31 -0
  55. data/lib/danger/commands/plugins/plugin_json.rb +46 -0
  56. data/lib/danger/commands/plugins/plugin_lint.rb +54 -0
  57. data/lib/danger/commands/plugins/plugin_readme.rb +45 -0
  58. data/lib/danger/commands/pr.rb +92 -0
  59. data/lib/danger/commands/runner.rb +94 -0
  60. data/lib/danger/commands/staging.rb +53 -0
  61. data/lib/danger/commands/systems.rb +43 -0
  62. data/lib/danger/comment_generators/bitbucket_server.md.erb +20 -0
  63. data/lib/danger/comment_generators/bitbucket_server_inline.md.erb +15 -0
  64. data/lib/danger/comment_generators/bitbucket_server_message_group.md.erb +12 -0
  65. data/lib/danger/comment_generators/github.md.erb +55 -0
  66. data/lib/danger/comment_generators/github_inline.md.erb +26 -0
  67. data/lib/danger/comment_generators/gitlab.md.erb +40 -0
  68. data/lib/danger/comment_generators/gitlab_inline.md.erb +26 -0
  69. data/lib/danger/comment_generators/vsts.md.erb +20 -0
  70. data/lib/danger/core_ext/file_list.rb +18 -0
  71. data/lib/danger/core_ext/string.rb +20 -0
  72. data/lib/danger/danger_core/dangerfile.rb +341 -0
  73. data/lib/danger/danger_core/dangerfile_dsl.rb +29 -0
  74. data/lib/danger/danger_core/dangerfile_generator.rb +11 -0
  75. data/lib/danger/danger_core/environment_manager.rb +123 -0
  76. data/lib/danger/danger_core/executor.rb +92 -0
  77. data/lib/danger/danger_core/message_aggregator.rb +49 -0
  78. data/lib/danger/danger_core/message_group.rb +68 -0
  79. data/lib/danger/danger_core/messages/base.rb +56 -0
  80. data/lib/danger/danger_core/messages/markdown.rb +42 -0
  81. data/lib/danger/danger_core/messages/violation.rb +54 -0
  82. data/lib/danger/danger_core/plugins/dangerfile_bitbucket_cloud_plugin.rb +144 -0
  83. data/lib/danger/danger_core/plugins/dangerfile_bitbucket_server_plugin.rb +211 -0
  84. data/lib/danger/danger_core/plugins/dangerfile_danger_plugin.rb +248 -0
  85. data/lib/danger/danger_core/plugins/dangerfile_git_plugin.rb +158 -0
  86. data/lib/danger/danger_core/plugins/dangerfile_github_plugin.rb +254 -0
  87. data/lib/danger/danger_core/plugins/dangerfile_gitlab_plugin.rb +240 -0
  88. data/lib/danger/danger_core/plugins/dangerfile_local_only_plugin.rb +42 -0
  89. data/lib/danger/danger_core/plugins/dangerfile_messaging_plugin.rb +218 -0
  90. data/lib/danger/danger_core/plugins/dangerfile_vsts_plugin.rb +191 -0
  91. data/lib/danger/danger_core/standard_error.rb +143 -0
  92. data/lib/danger/helpers/array_subclass.rb +61 -0
  93. data/lib/danger/helpers/comment.rb +32 -0
  94. data/lib/danger/helpers/comments_helper.rb +178 -0
  95. data/lib/danger/helpers/comments_parsing_helper.rb +70 -0
  96. data/lib/danger/helpers/emoji_mapper.rb +41 -0
  97. data/lib/danger/helpers/find_max_num_violations.rb +31 -0
  98. data/lib/danger/helpers/message_groups_array_helper.rb +31 -0
  99. data/lib/danger/plugin_support/gems_resolver.rb +77 -0
  100. data/lib/danger/plugin_support/plugin.rb +49 -0
  101. data/lib/danger/plugin_support/plugin_file_resolver.rb +30 -0
  102. data/lib/danger/plugin_support/plugin_linter.rb +161 -0
  103. data/lib/danger/plugin_support/plugin_parser.rb +199 -0
  104. data/lib/danger/plugin_support/templates/readme_table.html.erb +26 -0
  105. data/lib/danger/request_sources/bitbucket_cloud.rb +171 -0
  106. data/lib/danger/request_sources/bitbucket_cloud_api.rb +181 -0
  107. data/lib/danger/request_sources/bitbucket_server.rb +105 -0
  108. data/lib/danger/request_sources/bitbucket_server_api.rb +117 -0
  109. data/lib/danger/request_sources/github/github.rb +530 -0
  110. data/lib/danger/request_sources/github/github_review.rb +126 -0
  111. data/lib/danger/request_sources/github/github_review_resolver.rb +19 -0
  112. data/lib/danger/request_sources/github/github_review_unsupported.rb +25 -0
  113. data/lib/danger/request_sources/gitlab.rb +525 -0
  114. data/lib/danger/request_sources/local_only.rb +53 -0
  115. data/lib/danger/request_sources/request_source.rb +85 -0
  116. data/lib/danger/request_sources/support/get_ignored_violation.rb +17 -0
  117. data/lib/danger/request_sources/vsts.rb +118 -0
  118. data/lib/danger/request_sources/vsts_api.rb +138 -0
  119. data/lib/danger/scm_source/git_repo.rb +181 -0
  120. data/lib/danger/version.rb +4 -0
  121. metadata +339 -0
@@ -0,0 +1,53 @@
1
+ require "danger/commands/local_helpers/pry_setup"
2
+ require "fileutils"
3
+ require "tmpdir"
4
+
5
+ module Danger
6
+ class Staging < Runner
7
+ self.summary = "Run the Dangerfile locally against local master"
8
+ self.command = "staging"
9
+
10
+ def self.options
11
+ [
12
+ ["--pry", "Drop into a Pry shell after evaluating the Dangerfile."]
13
+ ]
14
+ end
15
+
16
+ def initialize(argv)
17
+ show_help = true if argv.arguments == ["-h"]
18
+
19
+ # Currently CLAide doesn't support short option like -h https://github.com/CocoaPods/CLAide/pull/60
20
+ # when user pass in -h, mimic the behavior of passing in --help.
21
+ argv = CLAide::ARGV.new ["--help"] if show_help
22
+
23
+ super
24
+
25
+ if argv.flag?("pry", false)
26
+ @dangerfile_path = PrySetup.new(cork).setup_pry(@dangerfile_path)
27
+ end
28
+ end
29
+
30
+ def validate!
31
+ super
32
+ unless @dangerfile_path
33
+ help! "Could not find a Dangerfile."
34
+ end
35
+ end
36
+
37
+ def run
38
+ ENV["DANGER_USE_LOCAL_ONLY_GIT"] = "YES"
39
+
40
+ env = EnvironmentManager.new(ENV, cork)
41
+ dm = Dangerfile.new(env, cork)
42
+
43
+ dm.run(
44
+ Danger::EnvironmentManager.danger_base_branch,
45
+ Danger::EnvironmentManager.danger_head_branch,
46
+ @dangerfile_path,
47
+ nil,
48
+ nil,
49
+ nil
50
+ )
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,43 @@
1
+ module Danger
2
+ class Systems < Runner
3
+ self.abstract_command = true
4
+ self.description = "For commands related to passing information from Danger to Danger.Systems."
5
+ self.summary = "Create data for Danger.Systems."
6
+ end
7
+
8
+ class CIDocs < Systems
9
+ self.command = "ci_docs"
10
+ self.summary = "Outputs the up-to-date CI documentation for Danger."
11
+
12
+ def run
13
+ here = File.dirname(__FILE__)
14
+ ci_source_paths = Dir.glob(here + "/../ci_source/*.rb")
15
+
16
+ require "yard"
17
+ # Pull out all the Danger::CI subclasses docs
18
+ registry = YARD::Registry.load(ci_source_paths, true)
19
+ ci_sources = begin
20
+ registry.all(:class)
21
+ .select { |klass| klass.inheritance_tree.map(&:name).include? :CI }
22
+ .reject { |source| source.name == :CI }
23
+ .reject { |source| source.name == :LocalGitRepo }
24
+ end
25
+
26
+ # Fail if anything is added and not documented
27
+ cis_without_docs = ci_sources.select { |source| source.base_docstring.empty? }
28
+ unless cis_without_docs.empty?
29
+ cork.puts "Please add docs to: #{cis_without_docs.map(&:name).join(', ')}"
30
+ abort("Failed.".red)
31
+ end
32
+
33
+ # Output a JSON array of name and details
34
+ require "json"
35
+ cork.puts ci_sources.map { |ci|
36
+ {
37
+ name: ci.name,
38
+ docs: ci.docstring
39
+ }
40
+ }.to_json
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,20 @@
1
+ <%- @tables.each do |table| -%>
2
+ <%- if table[:content].any? || table[:resolved].any? -%>
3
+ | | <%= table[:count] %> <%= table[:name] %><%= "s" unless table[:count] == 1 %> |
4
+ |---|---|
5
+ <%- table[:content].each do |violation| -%>
6
+ | <%= @emoji_mapper.map(table[:emoji]) %> | <%= violation.message %> |
7
+ <%- end -%>
8
+ <%- table[:resolved].each do |message| -%>
9
+ | @emoji_mapper.map("white_check_mark") | <%= message %> |
10
+ <%- end -%>
11
+
12
+ <%- end -%>
13
+ <%- end -%>
14
+
15
+ <%- @markdowns.each do |current| -%>
16
+ <%= current %>
17
+ <%# the previous line has to be aligned far to the left, otherwise markdown can break easily %>
18
+ <%- end -%>
19
+
20
+ Generated by :no_entry_sign: [Danger](https://danger.systems/ "generated_by_<%= @danger_id %>")
@@ -0,0 +1,15 @@
1
+ <%- @tables.each do |table| -%>
2
+ <%- table[:content].each do |violation| -%>
3
+ <%= @emoji_mapper.map(table[:emoji]) if table[:emoji] %> <%= violation.message %>
4
+ <%- end -%>
5
+ <%- table[:resolved] && table[:resolved].each do |message| -%>
6
+ <%= @emoji_mapper.map("white_check_mark") %> <%= message %>
7
+ <%- end -%>
8
+ <%- end -%>
9
+
10
+ <%- @markdowns.each do |current| -%>
11
+ <%= current %>
12
+ <%# the previous line has to be aligned far to the left, otherwise markdown can break easily %>
13
+ <%- end -%>
14
+
15
+ Generated by :no_entry_sign: [Danger](https://danger.systems/ "generated_by_<%= @danger_id %>")
@@ -0,0 +1,12 @@
1
+ <%- @message_group.messages.each do |message| -%>
2
+ <%- next if message.type == :markdown -%>
3
+ <%= @emoji_mapper.from_type(message.type) -%> <%= message.message %>
4
+
5
+ <%- end -%>
6
+ <%- @resolved.each do |message| -%>
7
+ <%= @emoji_mapper.map("white_check_mark") %> <%= message %>
8
+ <%- end -%>
9
+
10
+ <%= @message_group.markdowns.map(&:message).join("\n\n") %>
11
+
12
+ Generated by :no_entry_sign: [Danger](https://danger.systems/ "generated_by_<%= @danger_id %>")
@@ -0,0 +1,55 @@
1
+ <!--
2
+ <%- @tables.each do |table| -%>
3
+ <%= pluralize(table[:name], table[:count]) %><%= ": #{truncate(table[:content].first.message)}" if table[:count] > 0 %>
4
+ <%- end -%>
5
+ <%= pluralize("Markdown", @markdowns.size) %>
6
+ -->
7
+ <%- @tables.each do |table| -%>
8
+ <%- if table[:content].any? || table[:resolved].any? -%>
9
+ <table>
10
+ <thead>
11
+ <tr>
12
+ <th width="50"></th>
13
+ <th width="100%" data-danger-table="true" data-kind="<%= table[:name] %>">
14
+ <%- if table[:count] > 0 -%>
15
+ <%= pluralize(table[:name], table[:count]) %>
16
+ <%- else -%>
17
+ :white_check_mark: <%= random_compliment %>
18
+ <%- end -%>
19
+ </th>
20
+ </tr>
21
+ </thead>
22
+ <tbody>
23
+ <%- max_num_violations = FindMaxNumViolations.new(table[:content]).call -%>
24
+ <%- table[:content].take(max_num_violations).each do |violation| -%>
25
+ <tr>
26
+ <td>:<%= table[:emoji] %>:</td>
27
+ <td data-sticky="<%= violation.sticky %>"><%= violation.message %></td>
28
+ </tr>
29
+ <%- end -%>
30
+ <%- if table[:content].length > max_num_violations -%>
31
+ <tr>
32
+ <td>:warning:</td>
33
+ <td>
34
+ Danger found <%= table[:content].length %> violations with this PR. Due to GitHub's max issue comment size, the number shown has been truncated to <%= max_num_violations %>.
35
+ </td>
36
+ </tr>
37
+ <%- end -%>
38
+ <%- table[:resolved].each do |message| -%>
39
+ <tr>
40
+ <td>:white_check_mark:</td>
41
+ <td data-sticky="true"><del><%= message %></del></td>
42
+ </tr>
43
+ <%- end -%>
44
+ </tbody>
45
+ </table>
46
+ <%- end -%>
47
+ <%- end -%>
48
+
49
+ <%- @markdowns.each do |current| -%>
50
+ <%= current %>
51
+ <%# the previous line has to be aligned far to the left, otherwise markdown can break easily %>
52
+ <%- end -%>
53
+ <p align="right" data-meta="generated_by_<%= @danger_id %>">
54
+ Generated by :no_entry_sign: <a href="https://danger.systems/">Danger</a>
55
+ </p>
@@ -0,0 +1,26 @@
1
+ <%- @tables.each do |table| -%>
2
+ <%- if table[:content].any? -%>
3
+ <table data-meta="generated_by_<%= @danger_id %>">
4
+ <tbody>
5
+ <%- table[:content].each do |violation| -%>
6
+ <tr>
7
+ <td>:<%= table[:emoji] %>:</td>
8
+ <td width="100%" data-sticky="<%= violation.sticky %>"><%= "<del>" if table[:resolved] %><%= violation.message %><%= "</del>" if table[:resolved] %></td>
9
+ </tr>
10
+ <%- end -%>
11
+ </tbody>
12
+ </table>
13
+ <%- end -%>
14
+ <%- end -%>
15
+
16
+ <%- @markdowns.each do |current| -%>
17
+ <%= current %>
18
+ <%# the previous line has to be aligned far to the left, otherwise markdown can break easily %>
19
+ <%- end -%>
20
+ <%# We need to add the generated_by_ to identify comments from danger. But with inlines %>
21
+ <%# it might be a little annoying, so we set on the table, but if we have markdown we add the footer anyway %>
22
+ <%- if @markdowns.count > 0 -%>
23
+ <p align="right" data-meta="generated_by_<%= @danger_id %>">
24
+ Generated by :no_entry_sign: <a href="http://danger.systems/">Danger</a>
25
+ </p>
26
+ <%- end -%>
@@ -0,0 +1,40 @@
1
+ <%- @tables.each do |table| -%>
2
+ <%- if table[:content].any? || table[:resolved].any? -%>
3
+ <table>
4
+ <thead>
5
+ <tr>
6
+ <th width="5%"></th>
7
+ <th width="95%" data-danger-table="true" data-kind="<%= table[:name] %>">
8
+ <%- if table[:count] > 0 -%>
9
+ <%= table[:count] %> <%= table[:name] %><%= "s" unless table[:count] == 1 %>
10
+ <%- else -%>
11
+ :white_check_mark: <%= random_compliment %>
12
+ <%- end -%>
13
+ </th>
14
+ </tr>
15
+ </thead>
16
+ <tbody>
17
+ <%- table[:content].each do |violation| -%>
18
+ <tr>
19
+ <td>:<%= table[:emoji] %>:</td>
20
+ <td data-sticky="<%= violation.sticky %>"><%= violation.message %></td>
21
+ </tr>
22
+ <%- end -%>
23
+ <%- table[:resolved].each do |message| -%>
24
+ <tr>
25
+ <td>:white_check_mark:</td>
26
+ <td data-sticky="true"><del><%= message %></del></td>
27
+ </tr>
28
+ <%- end -%>
29
+ </tbody>
30
+ </table>
31
+ <%- end -%>
32
+ <%- end -%>
33
+
34
+ <%- @markdowns.each do |current| -%>
35
+ <%= current %>
36
+ <%# the previous line has to be aligned far to the left, otherwise markdown can break easily %>
37
+ <%- end -%>
38
+ <p align="right" data-meta="generated_by_<%= @danger_id %>">
39
+ Generated by :no_entry_sign: <a href="https://github.com/danger/danger/">Danger</a>
40
+ </p>
@@ -0,0 +1,26 @@
1
+ <%- @tables.each do |table| -%>
2
+ <%- if table[:content].any? -%>
3
+ <table data-meta="generated_by_<%= @danger_id %>">
4
+ <tbody>
5
+ <%- table[:content].each do |violation| -%>
6
+ <tr>
7
+ <td>:<%= table[:emoji] %>:</td>
8
+ <td width="100%" data-sticky="<%= violation.sticky %>"><%= "<del>" if table[:resolved] %><%= violation.message %><%= "</del>" if table[:resolved] %></td>
9
+ </tr>
10
+ <%- end -%>
11
+ </tbody>
12
+ </table>
13
+ <%- end -%>
14
+ <%- end -%>
15
+
16
+ <%- @markdowns.each do |current| -%>
17
+ <%= current %>
18
+ <%# the previous line has to be aligned far to the left, otherwise markdown can break easily %>
19
+ <%- end -%>
20
+ <%# We need to add the generated_by_ to identify comments from danger. But with inlines %>
21
+ <%# it might be a little annoying, so we set on the table, but if we have markdown we add the footer anyway %>
22
+ <%- if @markdowns.count > 0 -%>
23
+ <p align="right" data-meta="generated_by_<%= @danger_id %>">
24
+ Generated by :no_entry_sign: <a href="http://danger.systems/">Danger</a>
25
+ </p>
26
+ <%- end -%>
@@ -0,0 +1,20 @@
1
+ <%- @tables.each do |table| -%>
2
+ <%- if table[:content].any? || table[:resolved].any? -%>
3
+ | | <%= table[:count] %> <%= table[:name] %><%= "s" unless table[:count] == 1 %> |
4
+ |---|---|
5
+ <%- table[:content].each do |violation| -%>
6
+ | <%= @emoji_mapper.map(table[:emoji]) %> | <%= violation.message %> |
7
+ <%- end -%>
8
+ <%- table[:resolved].each do |message| -%>
9
+ | @emoji_mapper.map("white_check_mark") | <%= message %> |
10
+ <%- end -%>
11
+
12
+ <%- end -%>
13
+ <%- end -%>
14
+
15
+ <%- @markdowns.each do |current| -%>
16
+ <%= current %>
17
+ <%# the previous line has to be aligned far to the left, otherwise markdown can break easily %>
18
+ <%- end -%>
19
+
20
+ 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
+ unless current.nil?
12
+ return true if File.fnmatch(pattern, current) || pattern == current
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,341 @@
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, &blk)
53
+ method_missing(:warn, *args, &blk)
54
+ end
55
+
56
+ def fail(*args, &blk)
57
+ method_missing(:fail, *args, &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
+ # rubocop:disable Style/MethodMissing
64
+ def method_missing(method_sym, *arguments, &_block)
65
+ @core_plugins.each do |plugin|
66
+ if plugin.public_methods(false).include?(method_sym)
67
+ return plugin.send(method_sym, *arguments)
68
+ end
69
+ end
70
+ super
71
+ end
72
+
73
+ # cork_board not being set comes from plugins #585
74
+ def initialize(env_manager, cork_board = nil)
75
+ @plugins = {}
76
+ @core_plugins = []
77
+ @ui = cork_board || Cork::Board.new(silent: false, verbose: false)
78
+
79
+ # Triggers the core plugins
80
+ @env = env_manager
81
+
82
+ # Triggers local plugins from the root of a project
83
+ Dir["./danger_plugins/*.rb"].each do |file|
84
+ require File.expand_path(file)
85
+ end
86
+
87
+ refresh_plugins if env_manager.pr?
88
+ end
89
+
90
+ # Iterate through available plugin classes and initialize them with
91
+ # a reference to this Dangerfile
92
+ def refresh_plugins
93
+ plugins = Plugin.all_plugins
94
+ plugins.each do |klass|
95
+ next if klass.respond_to?(:singleton_class?) && klass.singleton_class?
96
+ plugin = klass.new(self)
97
+ next if plugin.nil? || @plugins[klass]
98
+
99
+ name = plugin.class.instance_name
100
+ self.class.send(:attr_reader, name)
101
+ instance_variable_set("@#{name}", plugin)
102
+
103
+ @plugins[klass] = plugin
104
+ @core_plugins << plugin if self.class.core_plugin_classes.include? klass
105
+ end
106
+ end
107
+ alias init_plugins refresh_plugins
108
+
109
+ def core_dsl_attributes
110
+ @core_plugins.map { |plugin| { plugin: plugin, methods: plugin.public_methods(false) } }
111
+ end
112
+
113
+ def external_dsl_attributes
114
+ plugins.values.reject { |plugin| @core_plugins.include? plugin } .map { |plugin| { plugin: plugin, methods: plugin.public_methods(false) } }
115
+ end
116
+
117
+ def method_values_for_plugin_hashes(plugin_hashes)
118
+ plugin_hashes.flat_map do |plugin_hash|
119
+ plugin = plugin_hash[:plugin]
120
+ methods = plugin_hash[:methods].select { |name| plugin.method(name).parameters.empty? }
121
+
122
+ methods.map do |method|
123
+ case method
124
+ when :api
125
+ value = "Octokit::Client"
126
+
127
+ when :pr_json, :mr_json
128
+ value = "[Skipped JSON]"
129
+
130
+ when :pr_diff, :mr_diff
131
+ value = "[Skipped Diff]"
132
+
133
+ else
134
+ value = plugin.send(method)
135
+ value = wrap_text(value.encode("utf-8")) if value.kind_of?(String)
136
+ # So that we either have one value per row
137
+ # or we have [] for an empty array
138
+ value = value.join("\n") if value.kind_of?(Array) && value.count > 0
139
+ end
140
+
141
+ [method.to_s, value]
142
+ end
143
+ end
144
+ end
145
+
146
+ # Iterates through the DSL's attributes, and table's the output
147
+ def print_known_info
148
+ rows = []
149
+ rows += method_values_for_plugin_hashes(core_dsl_attributes)
150
+ rows << ["---", "---"]
151
+ rows += method_values_for_plugin_hashes(external_dsl_attributes)
152
+ rows << ["---", "---"]
153
+ rows << ["SCM", env.scm.class]
154
+ rows << ["Source", env.ci_source.class]
155
+ rows << ["Requests", env.request_source.class]
156
+ rows << ["Base Commit", env.meta_info_for_base]
157
+ rows << ["Head Commit", env.meta_info_for_head]
158
+
159
+ params = {}
160
+ params[:rows] = rows.each { |current| current[0] = current[0].yellow }
161
+ params[:title] = "Danger v#{Danger::VERSION}\nDSL Attributes".green
162
+
163
+ ui.section("Info:") do
164
+ ui.puts
165
+ table = Terminal::Table.new(params)
166
+ table.align_column(0, :right)
167
+ ui.puts table
168
+ ui.puts
169
+ end
170
+ end
171
+
172
+ # Parses the file at a path, optionally takes the content of the file for DI
173
+ #
174
+ def parse(path, contents = nil)
175
+ print_known_info if verbose
176
+
177
+ contents ||= File.open(path, "r:utf-8", &:read)
178
+
179
+ # Work around for Rubinius incomplete encoding in 1.9 mode
180
+ if contents.respond_to?(:encoding) && contents.encoding.name != "UTF-8"
181
+ contents.encode!("UTF-8")
182
+ end
183
+
184
+ if contents.tr!("“”‘’‛", %(""'''))
185
+ # Changes have been made
186
+ ui.puts "Your #{path.basename} has had smart quotes sanitised. " \
187
+ "To avoid issues in the future, you should not use " \
188
+ "TextEdit for editing it. If you are not using TextEdit, " \
189
+ "you should turn off smart quotes in your editor of choice.".red
190
+ end
191
+
192
+ if contents.include?("puts")
193
+ ui.puts "You used `puts` in your Dangerfile. To print out text to GitHub use `message` instead"
194
+ end
195
+
196
+ self.defined_in_file = path
197
+ instance_eval do
198
+ # rubocop:disable Lint/RescueException
199
+ begin
200
+ eval_file(contents, path)
201
+ rescue Exception => e
202
+ message = "Invalid `#{path.basename}` file: #{e.message}"
203
+ raise DSLError.new(message, path, e.backtrace, contents)
204
+ end
205
+ # rubocop:enable Lint/RescueException
206
+ end
207
+ end
208
+
209
+ def print_results
210
+ status = status_report
211
+ violations = violation_report
212
+ return if (violations[:errors] + violations[:warnings] + violations[:messages] + status[:markdowns]).count.zero?
213
+
214
+ ui.section("Results:") do
215
+ %i(errors warnings messages).each do |key|
216
+ formatted = key.to_s.capitalize + ":"
217
+ title = case key
218
+ when :errors
219
+ formatted.red
220
+ when :warnings
221
+ formatted.yellow
222
+ else
223
+ formatted
224
+ end
225
+ rows = violations[key].uniq
226
+ print_list(title, rows)
227
+ end
228
+
229
+ if status[:markdowns].count > 0
230
+ ui.title("Markdown:") do
231
+ status[:markdowns].each do |current_markdown|
232
+ ui.puts "#{current_markdown.file}\#L#{current_markdown.line}" if current_markdown.file && current_markdown.line
233
+ ui.puts current_markdown.message
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end
239
+
240
+ def failed?
241
+ violation_report[:errors].count > 0
242
+ end
243
+
244
+ def post_results(danger_id, new_comment, remove_previous_comments)
245
+ violations = violation_report
246
+ report = {
247
+ warnings: violations[:warnings].uniq,
248
+ errors: violations[:errors].uniq,
249
+ messages: violations[:messages].uniq,
250
+ markdowns: status_report[:markdowns].uniq,
251
+ danger_id: danger_id
252
+ }
253
+
254
+ if env.request_source.respond_to?(:update_pr_by_line!) && ENV["DANGER_MESSAGE_AGGREGATION"]
255
+ env.request_source.update_pr_by_line!(message_groups: MessageAggregator.aggregate(**report),
256
+ new_comment: new_comment,
257
+ remove_previous_comments: remove_previous_comments,
258
+ danger_id: report[:danger_id])
259
+ else
260
+ env.request_source.update_pull_request!(
261
+ **report,
262
+ new_comment: new_comment,
263
+ remove_previous_comments: remove_previous_comments
264
+ )
265
+ end
266
+ end
267
+
268
+ def setup_for_running(base_branch, head_branch)
269
+ env.ensure_danger_branches_are_setup
270
+ env.scm.diff_for_folder(".".freeze, from: base_branch, to: head_branch, lookup_top_level: true)
271
+ end
272
+
273
+ def run(base_branch, head_branch, dangerfile_path, danger_id, new_comment, remove_previous_comments)
274
+ # Setup internal state
275
+ init_plugins
276
+ env.fill_environment_vars
277
+
278
+ begin
279
+ # Sets up the git environment
280
+ setup_for_running(base_branch, head_branch)
281
+
282
+ # Parse the local Dangerfile
283
+ parse(Pathname.new(dangerfile_path))
284
+
285
+ # Push results to the API
286
+ # Pass along the details of the run to the request source
287
+ # to send back to the code review site.
288
+ post_results(danger_id, new_comment, remove_previous_comments) unless danger_id.nil?
289
+
290
+ # Print results in the terminal
291
+ print_results
292
+ rescue DSLError => ex
293
+ # Push exception to the API and re-raise
294
+ post_exception(ex, danger_id, new_comment) unless danger_id.nil?
295
+ raise
296
+ ensure
297
+ # Makes sure that Danger specific git branches are cleaned
298
+ env.clean_up
299
+ end
300
+
301
+ failed?
302
+ end
303
+
304
+ private
305
+
306
+ def eval_file(contents, path)
307
+ eval(contents, nil, path.to_s) # rubocop:disable Eval
308
+ end
309
+
310
+ def print_list(title, rows)
311
+ unless rows.empty?
312
+ ui.title(title) do
313
+ rows.each do |row|
314
+ if row.file && row.line
315
+ path = "#{row.file}\#L#{row.line}: "
316
+ else
317
+ path = ""
318
+ end
319
+
320
+ ui.puts("- [ ] #{path}#{row.message}")
321
+ end
322
+ end
323
+ end
324
+ end
325
+
326
+ def wrap_text(text, width = 80)
327
+ text.gsub(/.{,#{width}}/) do |line|
328
+ line.strip!
329
+ "#{line}\n"
330
+ end
331
+ end
332
+
333
+ def post_exception(ex, danger_id, new_comment)
334
+ env.request_source.update_pull_request!(
335
+ danger_id: danger_id,
336
+ new_comment: new_comment,
337
+ markdowns: [ex.to_markdown]
338
+ )
339
+ end
340
+ end
341
+ end