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,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