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,143 @@
1
+ require "claide"
2
+ require "claide/informative_error"
3
+
4
+ module Danger
5
+ # From below here - this entire file was taken verbatim for CocoaPods-Core.
6
+
7
+ #-------------------------------------------------------------------------#
8
+
9
+ # Wraps an exception raised by a DSL file in order to show to the user the
10
+ # contents of the line that raised the exception.
11
+ #
12
+ class DSLError < StandardError
13
+ # @return [String] the description that should be presented to the user.
14
+ #
15
+ attr_reader :description
16
+
17
+ # @return [String] the path of the dsl file that raised the exception.
18
+ #
19
+ attr_reader :dsl_path
20
+
21
+ # @return [Exception] the backtrace of the exception raised by the
22
+ # evaluation of the dsl file.
23
+ #
24
+ attr_reader :backtrace
25
+
26
+ # @param [Exception] backtrace @see backtrace
27
+ # @param [String] dsl_path @see dsl_path
28
+ #
29
+ def initialize(description, dsl_path, backtrace, contents = nil)
30
+ @description = description
31
+ @dsl_path = dsl_path
32
+ @backtrace = backtrace
33
+ @contents = contents
34
+ end
35
+
36
+ # @return [String] the contents of the DSL that cause the exception to
37
+ # be raised.
38
+ #
39
+ def contents
40
+ @contents ||= begin
41
+ dsl_path && File.exist?(dsl_path) && File.read(dsl_path)
42
+ end
43
+ end
44
+
45
+ # The message of the exception reports the content of podspec for the
46
+ # line that generated the original exception.
47
+ #
48
+ # @example Output
49
+ #
50
+ # Invalid podspec at `RestKit.podspec` - undefined method
51
+ # `exclude_header_search_paths=' for #<Pod::Specification for
52
+ # `RestKit/Network (0.9.3)`>
53
+ #
54
+ # from spec-repos/master/RestKit/0.9.3/RestKit.podspec:36
55
+ # -------------------------------------------
56
+ # # because it would break: #import <CoreData/CoreData.h>
57
+ # > ns.exclude_header_search_paths = 'Code/RestKit.h'
58
+ # end
59
+ # -------------------------------------------
60
+ #
61
+ # @return [String] the message of the exception.
62
+ #
63
+ def message
64
+ @message ||= begin
65
+ description, stacktrace = parse.values_at(:description, :stacktrace)
66
+
67
+ msg = description
68
+ msg = msg.red if msg.respond_to?(:red)
69
+ msg << stacktrace if stacktrace
70
+ msg
71
+ end
72
+ end
73
+
74
+ def to_markdown
75
+ @markdown ||= begin
76
+ description, stacktrace = parse.values_at(:description, :stacktrace)
77
+
78
+ # Highlight failed method in markdown
79
+ description = description.tr("'", "`")
80
+
81
+ # Escape markdown brackets
82
+ description = description.gsub(/<|>/) { |bracket| "\\#{bracket}" }
83
+
84
+ md = "## Danger has errored"
85
+ md << "#{description}\n"
86
+ md << "```#{stacktrace}```" if stacktrace
87
+
88
+ Markdown.new(md, nil, nil)
89
+ end
90
+ end
91
+
92
+ private
93
+
94
+ def parse
95
+ result = {}
96
+
97
+ trace_line, description = parse_line_number_from_description
98
+ latest_version = Danger.danger_outdated?
99
+
100
+ result[:description] = "\n[!] #{description}"
101
+ result[:description] << upgrade_message(latest_version) if latest_version
102
+
103
+ return result unless backtrace && dsl_path && contents
104
+
105
+ trace_line = backtrace.find { |l| l.include?(dsl_path.to_s) } || trace_line
106
+ return result unless trace_line
107
+ line_numer = trace_line.split(":")[1].to_i - 1
108
+ return result unless line_numer
109
+
110
+ lines = contents.lines
111
+ indent = " # "
112
+ indicator = indent.tr("#", ">")
113
+ first_line = line_numer.zero?
114
+ last_line = (line_numer == (lines.count - 1))
115
+
116
+ result[:stacktrace] = "\n"
117
+ result[:stacktrace] << "#{indent}from #{trace_line.gsub(/:in.*$/, '')}\n"
118
+ result[:stacktrace] << "#{indent}-------------------------------------------\n"
119
+ result[:stacktrace] << "#{indent}#{lines[line_numer - 1]}" unless first_line
120
+ result[:stacktrace] << "#{indicator}#{lines[line_numer]}"
121
+ result[:stacktrace] << "#{indent}#{lines[line_numer + 1]}" unless last_line
122
+ result[:stacktrace] << "\n" unless result[:stacktrace].end_with?("\n")
123
+ result[:stacktrace] << "#{indent}-------------------------------------------\n"
124
+
125
+ result
126
+ end
127
+
128
+ def parse_line_number_from_description
129
+ description = self.description
130
+ if dsl_path && description =~ /((#{Regexp.quote File.expand_path(dsl_path)}|#{Regexp.quote dsl_path.to_s}):\d+)/
131
+ trace_line = Regexp.last_match[1]
132
+ description = description.sub(/#{Regexp.quote trace_line}:\s*/, "")
133
+ end
134
+ [trace_line, description]
135
+ end
136
+
137
+ def upgrade_message(latest_version)
138
+ ". Updating the Danger gem might fix the issue. "\
139
+ "Your Danger version: #{Danger::VERSION}, "\
140
+ "latest Danger version: #{latest_version}\n"
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,61 @@
1
+ module Danger
2
+ module Helpers
3
+ module ArraySubclass
4
+ include Comparable
5
+
6
+ def initialize(array)
7
+ @__array__ = array
8
+ end
9
+
10
+ def kind_of?(compare_class)
11
+ return true if compare_class == self.class
12
+
13
+ dummy.kind_of?(compare_class)
14
+ end
15
+
16
+ def method_missing(name, *args, &block)
17
+ super unless __array__.respond_to?(name)
18
+
19
+ respond_to_method(name, *args, &block)
20
+ end
21
+
22
+ def respond_to_missing?(name)
23
+ __array__.respond_to?(name) || super
24
+ end
25
+
26
+ def to_a
27
+ __array__
28
+ end
29
+
30
+ def to_ary
31
+ __array__
32
+ end
33
+
34
+ def <=>(other)
35
+ return unless other.kind_of?(self.class)
36
+
37
+ __array__ <=> other.instance_variable_get(:@__array__)
38
+ end
39
+
40
+ private
41
+
42
+ attr_accessor :__array__
43
+
44
+ def dummy
45
+ Class.new(Array).new
46
+ end
47
+
48
+ def respond_to_method(name, *args, &block)
49
+ result = __array__.send(name, *args, &block)
50
+ return result unless result.kind_of?(Array)
51
+
52
+ if name =~ /!/
53
+ __array__ = result
54
+ self
55
+ else
56
+ self.class.new(result)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,32 @@
1
+ module Danger
2
+ class Comment
3
+ attr_reader :id, :body
4
+
5
+ def initialize(id, body, inline = nil)
6
+ @id = id
7
+ @body = body
8
+ @inline = inline
9
+ end
10
+
11
+ def self.from_github(comment)
12
+ self.new(comment["id"], comment["body"])
13
+ end
14
+
15
+ def self.from_gitlab(comment)
16
+ if comment.respond_to?(:id) && comment.respond_to?(:body)
17
+ type = comment.respond_to?(:type) ? comment.type : nil
18
+ self.new(comment.id, comment.body, type == "DiffNote")
19
+ else
20
+ self.new(comment["id"], comment["body"], comment["type"] == "DiffNote")
21
+ end
22
+ end
23
+
24
+ def generated_by_danger?(danger_id)
25
+ body.include?("\"generated_by_#{danger_id}\"")
26
+ end
27
+
28
+ def inline?
29
+ @inline.nil? ? body.include?("") : @inline
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,178 @@
1
+ require "kramdown"
2
+ require "danger/helpers/comments_parsing_helper"
3
+ require "danger/helpers/emoji_mapper"
4
+ require "danger/helpers/find_max_num_violations"
5
+
6
+ module Danger
7
+ module Helpers
8
+ module CommentsHelper
9
+ # This might be a bit weird, but table_kind_from_title is a shared dependency for
10
+ # parsing and generating. And rubocop was adamant about file size so...
11
+ include Danger::Helpers::CommentsParsingHelper
12
+
13
+ def markdown_parser(text)
14
+ Kramdown::Document.new(text, input: "GFM")
15
+ end
16
+
17
+ # !@group Extension points
18
+ # Produces a markdown link to the file the message points to
19
+ #
20
+ # request_source implementations are invited to override this method with their
21
+ # vendor specific link.
22
+ #
23
+ # @param [Violation or Markdown] message
24
+ # @param [Bool] Should hide any generated link created
25
+ #
26
+ # @return [String] The Markdown compatible link
27
+ def markdown_link_to_message(message, _)
28
+ "#{message.file}#L#{message.line}"
29
+ end
30
+
31
+ # !@group Extension points
32
+ # Determine whether two messages are equivalent
33
+ #
34
+ # request_source implementations are invited to override this method.
35
+ # This is mostly here to enable sources to detect when inlines change only in their
36
+ # commit hash and not in content per-se. since the link is implementation dependant
37
+ # so should be the comparison.
38
+ #
39
+ # @param [Violation or Markdown] m1
40
+ # @param [Violation or Markdown] m2
41
+ #
42
+ # @return [Boolean] whether they represent the same message
43
+ def messages_are_equivalent(m1, m2)
44
+ m1 == m2
45
+ end
46
+
47
+ def process_markdown(violation, hide_link = false)
48
+ message = violation.message
49
+ message = "#{markdown_link_to_message(violation, hide_link)}#{message}" if violation.file && violation.line
50
+
51
+ html = markdown_parser(message).to_html
52
+ # Remove the outer `<p>` and `</p>`.
53
+ html = html.strip.sub(%r{\A<p>(.*)</p>\z}m, '\1')
54
+ Violation.new(html, violation.sticky, violation.file, violation.line)
55
+ end
56
+
57
+ def table(name, emoji, violations, all_previous_violations, template: "github")
58
+ content = violations
59
+ content = content.map { |v| process_markdown(v) } unless ["bitbucket_server", "vsts"].include?(template)
60
+
61
+ kind = table_kind_from_title(name)
62
+ previous_violations = all_previous_violations[kind] || []
63
+ resolved_violations = previous_violations.reject do |pv|
64
+ content.count { |v| messages_are_equivalent(v, pv) } > 0
65
+ end
66
+
67
+ resolved_messages = resolved_violations.map(&:message).uniq
68
+ count = content.count
69
+
70
+ {
71
+ name: name,
72
+ emoji: emoji,
73
+ content: content,
74
+ resolved: resolved_messages,
75
+ count: count
76
+ }
77
+ end
78
+
79
+ def apply_template(tables: [], markdowns: [], danger_id: "danger", template: "github", request_source: template)
80
+ require "erb"
81
+
82
+ md_template = File.join(Danger.gem_path, "lib/danger/comment_generators/#{template}.md.erb")
83
+
84
+ # erb: http://www.rrn.dk/rubys-erb-templating-system
85
+ # for the extra args: http://stackoverflow.com/questions/4632879/erb-template-removing-the-trailing-line
86
+ @tables = tables
87
+ @markdowns = markdowns.map(&:message)
88
+ @danger_id = danger_id
89
+ @emoji_mapper = EmojiMapper.new(request_source.sub("_inline",""))
90
+
91
+ return ERB.new(File.read(md_template), 0, "-").result(binding)
92
+ end
93
+
94
+ def generate_comment(warnings: [], errors: [], messages: [], markdowns: [], previous_violations: {}, danger_id: "danger", template: "github")
95
+ apply_template(
96
+ tables: [
97
+ table("Error", "no_entry_sign", errors, previous_violations, template: template),
98
+ table("Warning", "warning", warnings, previous_violations, template: template),
99
+ table("Message", "book", messages, previous_violations, template: template)
100
+ ],
101
+ markdowns: markdowns,
102
+ danger_id: danger_id,
103
+ template: template
104
+ )
105
+ end
106
+
107
+ # resolved is essentially reserved for future use - eventually we might
108
+ # have some nice generic resolved-thing going :)
109
+ def generate_message_group_comment(message_group:,
110
+ danger_id: "danger",
111
+ resolved: [],
112
+ template: "github")
113
+ # cheating a bit - I don't want to alter the apply_template API
114
+ # so just sneak around behind its back setting some instance variables
115
+ # to get them to show up in the template
116
+ @message_group = message_group
117
+ @resolved = resolved
118
+ request_source_name = template.sub("_message_group", "")
119
+
120
+
121
+ apply_template(danger_id: danger_id,
122
+ markdowns: message_group.markdowns,
123
+ template: template,
124
+ request_source: request_source_name)
125
+ .sub(/\A\n*/, "")
126
+ end
127
+
128
+ def generate_inline_comment_body(emoji,
129
+ message,
130
+ danger_id: "danger",
131
+ resolved: false,
132
+ template: "github")
133
+ apply_template(
134
+ tables: [{ content: [message], resolved: resolved, emoji: emoji }],
135
+ danger_id: danger_id,
136
+ template: "#{template}_inline"
137
+ )
138
+ end
139
+
140
+ def generate_inline_markdown_body(markdown, danger_id: "danger", template: "github")
141
+ apply_template(
142
+ markdowns: [markdown],
143
+ danger_id: danger_id,
144
+ template: "#{template}_inline"
145
+ )
146
+ end
147
+
148
+ def generate_description(warnings: nil, errors: nil, template: "github")
149
+ emoji_mapper = EmojiMapper.new(template)
150
+ if errors.empty? && warnings.empty?
151
+ return "All green. #{random_compliment}"
152
+ else
153
+ message = "#{emoji_mapper.map('warning')} "
154
+ message += "#{'Error'.danger_pluralize(errors.count)}. " unless errors.empty?
155
+ message += "#{'Warning'.danger_pluralize(warnings.count)}. " unless warnings.empty?
156
+ message += "Don't worry, everything is fixable."
157
+ return message
158
+ end
159
+ end
160
+
161
+ def random_compliment
162
+ ["Well done.", "Congrats.", "Woo!",
163
+ "Yay.", "Jolly good show.", "Good on 'ya.", "Nice work."].sample
164
+ end
165
+
166
+ private
167
+
168
+ def pluralize(string, count)
169
+ string.danger_pluralize(count)
170
+ end
171
+
172
+ def truncate(string)
173
+ max_message_length = 30
174
+ string.danger_truncate(max_message_length)
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,70 @@
1
+ module Danger
2
+ module Helpers
3
+ module CommentsParsingHelper
4
+ # !@group Extension points
5
+ # Produces a message-like from a row in a comment table
6
+ #
7
+ # @param [String] row
8
+ # The content of the row in the table
9
+ #
10
+ # @return [Violation or Markdown] the extracted message
11
+ def parse_message_from_row(row)
12
+ Violation.new(row, true)
13
+ end
14
+
15
+ def parse_tables_from_comment(comment)
16
+ comment.split("</table>")
17
+ end
18
+
19
+ def violations_from_table(table)
20
+ row_regex = %r{<td data-sticky="true">(?:<del>)?(.*?)(?:</del>)?\s*</td>}im
21
+ table.scan(row_regex).flatten.map do |row|
22
+ parse_message_from_row(row.strip)
23
+ end
24
+ end
25
+
26
+ def parse_comment(comment)
27
+ tables = parse_tables_from_comment(comment)
28
+ violations = {}
29
+ tables.each do |table|
30
+ match = danger_table?(table)
31
+ next unless match
32
+ title = match[1]
33
+ kind = table_kind_from_title(title)
34
+ next unless kind
35
+
36
+ violations[kind] = violations_from_table(table)
37
+ end
38
+
39
+ violations.reject { |_, v| v.empty? }
40
+ end
41
+
42
+ def table_kind_from_title(title)
43
+ if title =~ /error/i
44
+ :error
45
+ elsif title =~ /warning/i
46
+ :warning
47
+ elsif title =~ /message/i
48
+ :message
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ GITHUB_OLD_REGEX = %r{<th width="100%"(.*?)</th>}im
55
+ NEW_REGEX = %r{<th.*data-danger-table="true"(.*?)</th>}im
56
+
57
+ def danger_table?(table)
58
+ # The old GitHub specific method relied on
59
+ # the width of a `th` element to find the table
60
+ # title and determine if it was a danger table.
61
+ # The new method uses a more robust data-danger-table
62
+ # tag instead.
63
+ match = GITHUB_OLD_REGEX.match(table)
64
+ return match if match
65
+
66
+ return NEW_REGEX.match(table)
67
+ end
68
+ end
69
+ end
70
+ end