danger 8.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +94 -0
  4. data/bin/danger +5 -0
  5. data/lib/assets/DangerfileTemplate +13 -0
  6. data/lib/danger.rb +44 -0
  7. data/lib/danger/ci_source/appcenter.rb +55 -0
  8. data/lib/danger/ci_source/appveyor.rb +60 -0
  9. data/lib/danger/ci_source/azure_pipelines.rb +44 -0
  10. data/lib/danger/ci_source/bamboo.rb +41 -0
  11. data/lib/danger/ci_source/bitbucket_pipelines.rb +37 -0
  12. data/lib/danger/ci_source/bitrise.rb +65 -0
  13. data/lib/danger/ci_source/buddybuild.rb +62 -0
  14. data/lib/danger/ci_source/buildkite.rb +51 -0
  15. data/lib/danger/ci_source/ci_source.rb +37 -0
  16. data/lib/danger/ci_source/circle.rb +94 -0
  17. data/lib/danger/ci_source/circle_api.rb +51 -0
  18. data/lib/danger/ci_source/cirrus.rb +31 -0
  19. data/lib/danger/ci_source/code_build.rb +57 -0
  20. data/lib/danger/ci_source/codefresh.rb +53 -0
  21. data/lib/danger/ci_source/codeship.rb +44 -0
  22. data/lib/danger/ci_source/dotci.rb +52 -0
  23. data/lib/danger/ci_source/drone.rb +71 -0
  24. data/lib/danger/ci_source/github_actions.rb +43 -0
  25. data/lib/danger/ci_source/gitlab_ci.rb +86 -0
  26. data/lib/danger/ci_source/jenkins.rb +149 -0
  27. data/lib/danger/ci_source/local_git_repo.rb +119 -0
  28. data/lib/danger/ci_source/local_only_git_repo.rb +47 -0
  29. data/lib/danger/ci_source/screwdriver.rb +47 -0
  30. data/lib/danger/ci_source/semaphore.rb +37 -0
  31. data/lib/danger/ci_source/support/commits.rb +17 -0
  32. data/lib/danger/ci_source/support/find_repo_info_from_logs.rb +35 -0
  33. data/lib/danger/ci_source/support/find_repo_info_from_url.rb +42 -0
  34. data/lib/danger/ci_source/support/local_pull_request.rb +14 -0
  35. data/lib/danger/ci_source/support/no_pull_request.rb +7 -0
  36. data/lib/danger/ci_source/support/no_repo_info.rb +5 -0
  37. data/lib/danger/ci_source/support/pull_request_finder.rb +179 -0
  38. data/lib/danger/ci_source/support/remote_pull_request.rb +15 -0
  39. data/lib/danger/ci_source/support/repo_info.rb +10 -0
  40. data/lib/danger/ci_source/surf.rb +37 -0
  41. data/lib/danger/ci_source/teamcity.rb +161 -0
  42. data/lib/danger/ci_source/travis.rb +51 -0
  43. data/lib/danger/ci_source/vsts.rb +73 -0
  44. data/lib/danger/ci_source/xcode_server.rb +48 -0
  45. data/lib/danger/clients/rubygems_client.rb +14 -0
  46. data/lib/danger/commands/dangerfile/gem.rb +43 -0
  47. data/lib/danger/commands/dangerfile/init.rb +30 -0
  48. data/lib/danger/commands/dry_run.rb +54 -0
  49. data/lib/danger/commands/init.rb +297 -0
  50. data/lib/danger/commands/init_helpers/interviewer.rb +92 -0
  51. data/lib/danger/commands/local.rb +83 -0
  52. data/lib/danger/commands/local_helpers/http_cache.rb +36 -0
  53. data/lib/danger/commands/local_helpers/local_setup.rb +46 -0
  54. data/lib/danger/commands/local_helpers/pry_setup.rb +31 -0
  55. data/lib/danger/commands/plugins/plugin_json.rb +46 -0
  56. data/lib/danger/commands/plugins/plugin_lint.rb +54 -0
  57. data/lib/danger/commands/plugins/plugin_readme.rb +45 -0
  58. data/lib/danger/commands/pr.rb +92 -0
  59. data/lib/danger/commands/runner.rb +94 -0
  60. data/lib/danger/commands/staging.rb +53 -0
  61. data/lib/danger/commands/systems.rb +43 -0
  62. data/lib/danger/comment_generators/bitbucket_server.md.erb +20 -0
  63. data/lib/danger/comment_generators/bitbucket_server_inline.md.erb +15 -0
  64. data/lib/danger/comment_generators/bitbucket_server_message_group.md.erb +12 -0
  65. data/lib/danger/comment_generators/github.md.erb +55 -0
  66. data/lib/danger/comment_generators/github_inline.md.erb +26 -0
  67. data/lib/danger/comment_generators/gitlab.md.erb +40 -0
  68. data/lib/danger/comment_generators/gitlab_inline.md.erb +26 -0
  69. data/lib/danger/comment_generators/vsts.md.erb +20 -0
  70. data/lib/danger/core_ext/file_list.rb +18 -0
  71. data/lib/danger/core_ext/string.rb +20 -0
  72. data/lib/danger/danger_core/dangerfile.rb +341 -0
  73. data/lib/danger/danger_core/dangerfile_dsl.rb +29 -0
  74. data/lib/danger/danger_core/dangerfile_generator.rb +11 -0
  75. data/lib/danger/danger_core/environment_manager.rb +123 -0
  76. data/lib/danger/danger_core/executor.rb +92 -0
  77. data/lib/danger/danger_core/message_aggregator.rb +49 -0
  78. data/lib/danger/danger_core/message_group.rb +68 -0
  79. data/lib/danger/danger_core/messages/base.rb +56 -0
  80. data/lib/danger/danger_core/messages/markdown.rb +42 -0
  81. data/lib/danger/danger_core/messages/violation.rb +54 -0
  82. data/lib/danger/danger_core/plugins/dangerfile_bitbucket_cloud_plugin.rb +144 -0
  83. data/lib/danger/danger_core/plugins/dangerfile_bitbucket_server_plugin.rb +211 -0
  84. data/lib/danger/danger_core/plugins/dangerfile_danger_plugin.rb +248 -0
  85. data/lib/danger/danger_core/plugins/dangerfile_git_plugin.rb +158 -0
  86. data/lib/danger/danger_core/plugins/dangerfile_github_plugin.rb +254 -0
  87. data/lib/danger/danger_core/plugins/dangerfile_gitlab_plugin.rb +240 -0
  88. data/lib/danger/danger_core/plugins/dangerfile_local_only_plugin.rb +42 -0
  89. data/lib/danger/danger_core/plugins/dangerfile_messaging_plugin.rb +218 -0
  90. data/lib/danger/danger_core/plugins/dangerfile_vsts_plugin.rb +191 -0
  91. data/lib/danger/danger_core/standard_error.rb +143 -0
  92. data/lib/danger/helpers/array_subclass.rb +61 -0
  93. data/lib/danger/helpers/comment.rb +32 -0
  94. data/lib/danger/helpers/comments_helper.rb +178 -0
  95. data/lib/danger/helpers/comments_parsing_helper.rb +70 -0
  96. data/lib/danger/helpers/emoji_mapper.rb +41 -0
  97. data/lib/danger/helpers/find_max_num_violations.rb +31 -0
  98. data/lib/danger/helpers/message_groups_array_helper.rb +31 -0
  99. data/lib/danger/plugin_support/gems_resolver.rb +77 -0
  100. data/lib/danger/plugin_support/plugin.rb +49 -0
  101. data/lib/danger/plugin_support/plugin_file_resolver.rb +30 -0
  102. data/lib/danger/plugin_support/plugin_linter.rb +161 -0
  103. data/lib/danger/plugin_support/plugin_parser.rb +199 -0
  104. data/lib/danger/plugin_support/templates/readme_table.html.erb +26 -0
  105. data/lib/danger/request_sources/bitbucket_cloud.rb +171 -0
  106. data/lib/danger/request_sources/bitbucket_cloud_api.rb +181 -0
  107. data/lib/danger/request_sources/bitbucket_server.rb +105 -0
  108. data/lib/danger/request_sources/bitbucket_server_api.rb +117 -0
  109. data/lib/danger/request_sources/github/github.rb +530 -0
  110. data/lib/danger/request_sources/github/github_review.rb +126 -0
  111. data/lib/danger/request_sources/github/github_review_resolver.rb +19 -0
  112. data/lib/danger/request_sources/github/github_review_unsupported.rb +25 -0
  113. data/lib/danger/request_sources/gitlab.rb +525 -0
  114. data/lib/danger/request_sources/local_only.rb +53 -0
  115. data/lib/danger/request_sources/request_source.rb +85 -0
  116. data/lib/danger/request_sources/support/get_ignored_violation.rb +17 -0
  117. data/lib/danger/request_sources/vsts.rb +118 -0
  118. data/lib/danger/request_sources/vsts_api.rb +138 -0
  119. data/lib/danger/scm_source/git_repo.rb +181 -0
  120. data/lib/danger/version.rb +4 -0
  121. metadata +339 -0
@@ -0,0 +1,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