pmdtester 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.ci/build.sh +91 -0
  3. data/.ci/files/env.gpg +1 -0
  4. data/.github/workflows/build.yml +39 -0
  5. data/.gitignore +9 -0
  6. data/.hoerc +1 -1
  7. data/.rubocop.yml +6 -2
  8. data/.ruby-version +1 -0
  9. data/History.md +40 -0
  10. data/Manifest.txt +24 -9
  11. data/README.rdoc +45 -30
  12. data/Rakefile +5 -3
  13. data/config/all-java.xml +1 -1
  14. data/config/design.xml +1 -1
  15. data/config/projectlist_1_0_0.xsd +2 -1
  16. data/config/projectlist_1_1_0.xsd +31 -0
  17. data/lib/pmdtester.rb +8 -7
  18. data/lib/pmdtester/builders/liquid_renderer.rb +73 -0
  19. data/lib/pmdtester/builders/pmd_report_builder.rb +102 -78
  20. data/lib/pmdtester/builders/project_builder.rb +100 -0
  21. data/lib/pmdtester/builders/project_hasher.rb +126 -0
  22. data/lib/pmdtester/builders/rule_set_builder.rb +92 -47
  23. data/lib/pmdtester/builders/simple_progress_logger.rb +4 -4
  24. data/lib/pmdtester/builders/summary_report_builder.rb +62 -131
  25. data/lib/pmdtester/collection_by_file.rb +55 -0
  26. data/lib/pmdtester/parsers/options.rb +19 -0
  27. data/lib/pmdtester/parsers/pmd_report_document.rb +74 -29
  28. data/lib/pmdtester/parsers/projects_parser.rb +2 -4
  29. data/lib/pmdtester/pmd_branch_detail.rb +29 -19
  30. data/lib/pmdtester/pmd_configerror.rb +23 -24
  31. data/lib/pmdtester/pmd_error.rb +34 -34
  32. data/lib/pmdtester/pmd_report_detail.rb +9 -12
  33. data/lib/pmdtester/pmd_tester_utils.rb +55 -0
  34. data/lib/pmdtester/pmd_violation.rb +66 -28
  35. data/lib/pmdtester/project.rb +21 -48
  36. data/lib/pmdtester/report_diff.rb +179 -111
  37. data/lib/pmdtester/resource_locator.rb +4 -0
  38. data/lib/pmdtester/runner.rb +66 -64
  39. data/pmdtester.gemspec +27 -36
  40. data/resources/_includes/diff_pill_row.html +6 -0
  41. data/resources/css/bootstrap.min.css +7 -0
  42. data/resources/css/datatables.min.css +36 -0
  43. data/resources/css/pmd-tester.css +131 -0
  44. data/resources/js/bootstrap.min.js +7 -0
  45. data/resources/js/code-snippets.js +66 -0
  46. data/resources/js/datatables.min.js +726 -0
  47. data/resources/js/jquery-3.2.1.slim.min.js +4 -0
  48. data/resources/js/jquery.min.js +2 -0
  49. data/resources/js/popper.min.js +5 -0
  50. data/resources/js/project-report.js +136 -0
  51. data/resources/project_diff_report.html +205 -0
  52. data/resources/project_index.html +102 -0
  53. metadata +64 -20
  54. data/.travis.yml +0 -40
  55. data/lib/pmdtester/builders/diff_builder.rb +0 -31
  56. data/lib/pmdtester/builders/diff_report/configerrors.rb +0 -50
  57. data/lib/pmdtester/builders/diff_report/errors.rb +0 -71
  58. data/lib/pmdtester/builders/diff_report/violations.rb +0 -77
  59. data/lib/pmdtester/builders/diff_report_builder.rb +0 -99
  60. data/lib/pmdtester/builders/html_report_builder.rb +0 -56
  61. data/resources/css/maven-base.css +0 -155
  62. data/resources/css/maven-theme.css +0 -171
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'differ'
4
+
5
+ module PmdTester
6
+ # Turn a project report into a hash that can be rendered somewhere else
7
+ module ProjectHasher
8
+ include PmdTester
9
+
10
+ def report_diff_to_h(rdiff)
11
+ {
12
+ 'violation_counts' => rdiff.violation_counts.to_h,
13
+ 'error_counts' => rdiff.error_counts.to_h,
14
+ 'configerror_counts' => rdiff.configerror_counts.to_h,
15
+
16
+ 'base_execution_time' => PmdReportDetail.convert_seconds(rdiff.base_report.exec_time),
17
+ 'patch_execution_time' => PmdReportDetail.convert_seconds(rdiff.patch_report.exec_time),
18
+ 'diff_execution_time' => PmdReportDetail.convert_seconds(rdiff.patch_report.exec_time -
19
+ rdiff.base_report.exec_time),
20
+
21
+ 'base_timestamp' => rdiff.base_report.timestamp,
22
+ 'patch_timestamp' => rdiff.patch_report.timestamp,
23
+
24
+ 'rule_diffs' => rdiff.rule_summaries
25
+ }
26
+ end
27
+
28
+ def errors_to_h(project)
29
+ errors = project.report_diff.error_diffs_by_file.values.flatten
30
+ errors.map { |e| error_to_hash(e, project) }
31
+ end
32
+
33
+ def configerrors_to_h(project)
34
+ configerrors = project.report_diff.configerror_diffs_by_rule.values.flatten
35
+ configerrors.map { |e| configerror_to_hash(e) }
36
+ end
37
+
38
+ def violations_to_hash(project)
39
+ filename_index = []
40
+ all_vs = []
41
+ project.report_diff.violation_diffs_by_file.each do |file, vs|
42
+ file_ref = filename_index.size
43
+ filename_index.push(project.get_local_path(file))
44
+ vs.each do |v|
45
+ all_vs.push(make_violation_hash(file_ref, v))
46
+ end
47
+ end
48
+
49
+ {
50
+ 'file_index' => filename_index,
51
+ 'violations' => all_vs
52
+ }
53
+ end
54
+
55
+ def link_template(project)
56
+ l_str = project.type == 'git' ? 'L' : 'l'
57
+ "#{project.webview_url}/{file}##{l_str}{line}"
58
+ end
59
+
60
+ def violation_type(violation)
61
+ if violation.changed?
62
+ '~'
63
+ elsif violation.branch == 'patch'
64
+ '+'
65
+ else
66
+ '-'
67
+ end
68
+ end
69
+
70
+ def make_violation_hash(file_ref, violation)
71
+ h = {
72
+ 't' => violation_type(violation),
73
+ 'l' => violation.line,
74
+ 'f' => file_ref,
75
+ 'r' => violation.rule_name,
76
+ 'm' => violation.changed? ? diff_fragments(violation) : violation.message
77
+ }
78
+ h['ol'] = violation.old_line if violation.changed? && violation.line != violation.old_line
79
+ h
80
+ end
81
+
82
+ def diff_fragments(violation)
83
+ diff = Differ.diff_by_word(violation.message, violation.old_message)
84
+ diff.format_as(:html)
85
+ end
86
+
87
+ def error_to_hash(error, project)
88
+ escaped_stacktrace = sanitize_stacktrace(error)
89
+ old_stacktrace = error.old_error.nil? ? nil : sanitize_stacktrace(error.old_error)
90
+
91
+ {
92
+ 'file_url' => project.get_webview_url(error.filename),
93
+ 'stack_trace_html' => escaped_stacktrace,
94
+ 'old_stack_trace_html' => old_stacktrace,
95
+ 'short_message' => error.short_message,
96
+ 'short_filename' => error.short_filename,
97
+ 'filename' => error.filename,
98
+ 'change_type' => change_type(error)
99
+ }
100
+ end
101
+
102
+ def sanitize_stacktrace(error)
103
+ CGI.escapeHTML(error.stack_trace)
104
+ .gsub(error.filename, '<span class="meta-var">$FILE</span>')
105
+ .gsub(/\w++(?=\(\w++\.java:\d++\))/, '<span class="stack-trace-method">\\0</span>')
106
+ end
107
+
108
+ def configerror_to_hash(configerror)
109
+ {
110
+ 'rule' => configerror.rulename,
111
+ 'message' => configerror.msg,
112
+ 'change_type' => change_type(configerror)
113
+ }
114
+ end
115
+
116
+ def change_type(item)
117
+ if item.branch == BASE
118
+ 'removed'
119
+ elsif item.changed?
120
+ 'changed'
121
+ else
122
+ 'added'
123
+ end
124
+ end
125
+ end
126
+ end
@@ -9,8 +9,9 @@ module PmdTester
9
9
  # Attention: we only consider java rulesets now.
10
10
  class RuleSetBuilder
11
11
  include PmdTester
12
- ALL_RULE_SETS = Set['bestpractices', 'codestyle', 'design', 'documentation',
13
- 'errorprone', 'multithreading', 'performance', 'security'].freeze
12
+ ALL_CATEGORIES = Set['bestpractices.xml', 'codestyle.xml', 'design.xml', 'documentation.xml',
13
+ 'errorprone.xml', 'multithreading.xml', 'performance.xml',
14
+ 'security.xml'].freeze
14
15
  PATH_TO_PMD_JAVA_BASED_RULES =
15
16
  'pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule'
16
17
  PATH_TO_PMD_XPATH_BASED_RULES = 'pmd-java/src/main/resources/category/java'
@@ -25,19 +26,35 @@ module PmdTester
25
26
 
26
27
  def build
27
28
  filenames = diff_filenames
28
- rule_sets = get_rule_sets(filenames)
29
- output_filter_set(rule_sets)
30
- build_config_file(rule_sets)
31
- logger.debug "Dynamic configuration: #{[rule_sets]}"
32
- rule_sets
29
+ rule_refs = get_rule_refs(filenames)
30
+ output_filter_set(rule_refs)
31
+ build_config_file(rule_refs)
32
+ logger.debug "Dynamic configuration: #{[rule_refs]}"
33
+ rule_refs
33
34
  end
34
35
 
35
- def output_filter_set(rule_sets)
36
- if rule_sets == ALL_RULE_SETS
37
- # if `rule_sets` contains all rule sets, than no need to filter the baseline
38
- @options.filter_set = nil
36
+ def output_filter_set(rule_refs)
37
+ if rule_refs == ALL_CATEGORIES
38
+ if @options.mode == Options::ONLINE
39
+ @options.filter_set = Set[]
40
+ doc = File.open(@options.patch_config) { |f| Nokogiri::XML(f) }
41
+ rules = doc.css('ruleset rule')
42
+ rules.each do |r|
43
+ ref = r.attributes['ref'].content
44
+ ref.delete_prefix!('category/java/')
45
+ @options.filter_set.add(ref)
46
+ end
47
+
48
+ logger.debug "Using filter based on patch config #{@options.patch_config}: " \
49
+ "#{@options.filter_set}"
50
+ else
51
+ # if `rule_refs` contains all categories, then no need to filter the baseline
52
+ logger.debug 'No filter when comparing patch to baseline'
53
+ @options.filter_set = nil
54
+ end
39
55
  else
40
- @options.filter_set = rule_sets
56
+ logger.debug "Filter is now #{rule_refs}"
57
+ @options.filter_set = rule_refs
41
58
  end
42
59
  end
43
60
 
@@ -53,58 +70,86 @@ module PmdTester
53
70
  filenames.split("\n")
54
71
  end
55
72
 
56
- def get_rule_sets(filenames)
57
- rule_sets = Set[]
73
+ def get_rule_refs(filenames)
74
+ categories, rules = determine_categories_rules(filenames)
75
+ logger.debug "Categories: #{categories}"
76
+ logger.debug "Rules: #{rules}"
77
+
78
+ # filter out all individual rules that are already covered by a complete category
79
+ categories.each do |cat|
80
+ rules.delete_if { |e| e.start_with?(cat) }
81
+ end
82
+
83
+ refs = Set[]
84
+ refs.merge(categories)
85
+ refs.merge(rules)
86
+ refs
87
+ end
88
+
89
+ def determine_categories_rules(filenames)
90
+ categories = Set[]
91
+ rules = Set[]
58
92
  filenames.each do |filename|
59
- match_data = %r{#{PATH_TO_PMD_JAVA_BASED_RULES}/([^/]+)/[^/]+Rule.java}.match(filename)
60
- if match_data.nil?
61
- match_data = %r{#{PATH_TO_PMD_XPATH_BASED_RULES}/([^/]+).xml}.match(filename)
93
+ match_data = check_single_filename(filename)
94
+
95
+ unless match_data.nil?
96
+ if match_data.size == 2
97
+ categories.add("#{match_data[1]}.xml")
98
+ else
99
+ rules.add("#{match_data[1]}.xml/#{match_data[2]}")
100
+ end
62
101
  end
63
102
 
64
- category = if match_data.nil?
65
- nil
66
- else
67
- match_data[1]
68
- end
103
+ next unless match_data.nil?
69
104
 
70
- if category.nil?
71
- rule_sets = ALL_RULE_SETS
72
- break
73
- else
74
- rule_sets.add(category)
75
- end
105
+ logger.debug "Change doesn't match specific rule/category - enable all rules"
106
+ categories = ALL_CATEGORIES
107
+ rules.clear
108
+ break
76
109
  end
77
- rule_sets
110
+ [categories, rules]
78
111
  end
79
112
 
80
- def build_config_file(rule_sets)
81
- if rule_sets.empty?
113
+ def check_single_filename(filename)
114
+ logger.debug "Checking #{filename}"
115
+ match_data = %r{#{PATH_TO_PMD_JAVA_BASED_RULES}/([^/]+)/([^/]+)Rule.java}.match(filename)
116
+ match_data = %r{#{PATH_TO_PMD_XPATH_BASED_RULES}/([^/]+).xml}.match(filename) if match_data.nil?
117
+ logger.debug "Matches: #{match_data.inspect}"
118
+ match_data
119
+ end
120
+
121
+ def build_config_file(rule_refs)
122
+ if rule_refs.empty?
82
123
  logger.info NO_JAVA_RULES_CHANGED_MESSAGE
83
124
  return
84
125
  end
85
126
 
86
- doc = Nokogiri::XML(File.read(PATH_TO_ALL_JAVA_RULES))
87
- doc.search('rule').each do |rule|
88
- rule.remove unless match_ref?(rule, rule_sets)
127
+ if rule_refs == ALL_CATEGORIES
128
+ logger.debug 'All rules are used. Not generating a dynamic ruleset.'
129
+ logger.debug "Using the configured/default ruleset base_config=#{@options.base_config} "\
130
+ "patch_config=#{@options.patch_config}"
131
+ return
89
132
  end
90
133
 
91
- description = doc.at_css('description')
92
- description.content = 'The ruleset generated by PmdTester dynamically'
93
-
94
- write_dynamic_file(doc)
134
+ write_dynamic_file(rule_refs)
95
135
  end
96
136
 
97
- def match_ref?(rule_node, rule_sets)
98
- rule_sets.each do |rule_set|
99
- return true unless rule_node['ref'].index(rule_set).nil?
137
+ def write_dynamic_file(rule_refs)
138
+ logger.debug "Generating dynamic configuration for: #{[rule_refs]}"
139
+ builder = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
140
+ xml.ruleset('xmlns' => 'http://pmd.sourceforge.net/ruleset/2.0.0',
141
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
142
+ 'xsi:schemaLocation' => 'http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd',
143
+ 'name' => 'Dynamic PmdTester Ruleset') do
144
+ xml.description 'The ruleset generated by PmdTester dynamically'
145
+ rule_refs.each do |entry|
146
+ xml.rule('ref' => "category/java/#{entry}")
147
+ end
148
+ end
100
149
  end
101
-
102
- false
103
- end
104
-
105
- def write_dynamic_file(doc)
150
+ doc = builder.to_xml(indent: 4, encoding: 'UTF-8')
106
151
  File.open(PATH_TO_DYNAMIC_CONFIG, 'w') do |x|
107
- x << doc.to_s.gsub(/\n\s+\n/, "\n")
152
+ x << doc.gsub(/\n\s+\n/, "\n")
108
153
  end
109
154
  @options.base_config = PATH_TO_DYNAMIC_CONFIG
110
155
  @options.patch_config = PATH_TO_DYNAMIC_CONFIG
@@ -6,16 +6,16 @@ module PmdTester
6
6
  # Helper class that provides a simple progress logging
7
7
  class SimpleProgressLogger
8
8
  include PmdTester
9
- def initialize(project_name)
10
- @project_name = project_name
9
+ def initialize(task_name)
10
+ @task_name = task_name
11
11
  end
12
12
 
13
13
  def start
14
- logger.info "Starting to generate #{@project_name}'s PMD report"
14
+ logger.info "Starting #{@task_name}"
15
15
  message_counter = 1
16
16
  @scheduler = Rufus::Scheduler.new
17
17
  @scheduler.every '2m' do
18
- logger.info "Still generating #{@project_name}'s PMD report (#{message_counter})..."
18
+ logger.info "Still #{@task_name} (#{message_counter})..."
19
19
  message_counter += 1
20
20
  end
21
21
  end
@@ -2,159 +2,90 @@
2
2
 
3
3
  module PmdTester
4
4
  # Building summary report to show the details about projects and pmd branchs
5
- class SummaryReportBuilder < HtmlReportBuilder
5
+ class SummaryReportBuilder
6
6
  include PmdTester
7
- REPORT_DIR = 'target/reports/diff'
8
- BASE_CONFIG_PATH = 'target/reports/diff/base_config.xml'
9
- PATCH_CONFIG_PATH = 'target/reports/diff/patch_config.xml'
10
- INDEX_PATH = 'target/reports/diff/index.html'
11
-
12
- def build(projects, base_name, patch_name)
13
- @projects = projects
14
- @base_details = get_branch_details(base_name)
15
- @patch_details = get_branch_details(patch_name)
16
-
17
- FileUtils.mkdir_p(REPORT_DIR) unless File.directory?(REPORT_DIR)
18
- index = File.new(INDEX_PATH, 'w')
19
-
20
- html_report = build_html_report('Summary report')
21
- copy_css(REPORT_DIR)
22
-
23
- index.puts html_report
24
- index.close
25
-
26
- logger.info 'Built summary report successfully!'
27
- end
28
-
29
- def get_branch_details(branch_name)
30
- details = PmdBranchDetail.new(branch_name)
31
- details.load
32
- end
7
+ include LiquidRenderer
8
+ include ProjectHasher
33
9
 
34
- def build_body(doc)
35
- build_branch_details_section(doc)
36
- build_projects_section(doc)
37
- end
10
+ REPORT_DIR = 'target/reports/diff'
11
+ BASE_CONFIG_NAME = 'base_config.xml'
12
+ PATCH_CONFIG_NAME = 'patch_config.xml'
38
13
 
39
- def build_branch_details_section(doc)
40
- doc.div(class: 'section', id: 'branchdetails') do
41
- doc.h2 'Branch details:'
42
- build_branch_details_table(doc)
14
+ def write_all_projects(projects, base_details, patch_details)
15
+ projects.each do |project|
16
+ process_project(project, "#{REPORT_DIR}/#{project.name}")
43
17
  end
44
- end
18
+ logger.info 'Built all difference reports successfully!'
45
19
 
46
- def build_branch_details_table(doc)
47
- doc.table(class: 'bodyTable', border: '0') do
48
- build_branch_table_head(doc)
49
- build_branch_table_body(doc)
50
- end
20
+ FileUtils.mkdir_p(REPORT_DIR)
21
+ write_structure(REPORT_DIR)
22
+ copy_configs(REPORT_DIR, base_details, patch_details)
23
+ write_index(REPORT_DIR, base_details, patch_details, projects)
24
+ logger.info "Built summary report successfully in #{REPORT_DIR}!"
51
25
  end
52
26
 
53
- def build_branch_table_head(doc)
54
- doc.thead do
55
- doc.tr do
56
- doc.th 'Item'
57
- doc.th 'base'
58
- doc.th 'patch'
59
- end
60
- end
61
- end
27
+ private
62
28
 
63
- def build_branch_table_body(doc)
64
- doc.tbody do
65
- build_branch_table_row(doc, 'branch name', @base_details.branch_name,
66
- @patch_details.branch_name)
67
- build_branch_table_row(doc, 'branch last commit sha', @base_details.branch_last_sha,
68
- @patch_details.branch_last_sha)
69
- build_branch_table_row(doc, 'branch last commit message', @base_details.branch_last_message,
70
- @patch_details.branch_last_message)
71
- build_branch_table_row(doc, 'total execution time', @base_details.format_execution_time,
72
- @patch_details.format_execution_time)
73
- build_branch_table_row(doc, 'jdk version', @base_details.jdk_version,
74
- @patch_details.jdk_version)
75
- build_branch_table_row(doc, 'language', @base_details.language,
76
- @patch_details.language)
77
- build_branch_config_table_row(doc)
78
- end
29
+ def process_project(project, dir)
30
+ logger.info "Rendering #{project.name}..."
31
+ LiquidProjectRenderer.new.write_project_index(project, dir)
79
32
  end
80
33
 
81
- def build_branch_config_table_row(doc)
82
- doc.tr do
83
- doc.td(class: 'c') { doc.text 'branch configuration' }
84
- base_config_src_path = @base_details.target_branch_config_path
85
- copy_branch_config_file(base_config_src_path, BASE_CONFIG_PATH)
86
- doc.td(class: 'a') do
87
- doc.a(href: './base_config.xml') { doc.text 'base config' }
88
- end
89
- patch_config_stc_path = @patch_details.target_branch_config_path
90
- FileUtils.cp(patch_config_stc_path, PATCH_CONFIG_PATH)
91
- doc.td(class: 'b') do
92
- doc.a(href: './patch_config.xml') { doc.text 'patch config' }
93
- end
94
- end
34
+ def write_structure(target_root)
35
+ logger.info 'Copying resources...'
36
+ copy_resource('css', target_root)
37
+ copy_resource('js', target_root)
95
38
  end
96
39
 
97
- def copy_branch_config_file(src, dest)
98
- FileUtils.cp(src, dest) if File.exist?(src)
40
+ def copy_configs(target_root, base_details, patch_details)
41
+ copy_file(base_details.target_branch_config_path, "#{target_root}/#{BASE_CONFIG_NAME}")
42
+ copy_file(patch_details.target_branch_config_path, "#{target_root}/#{PATCH_CONFIG_NAME}")
99
43
  end
100
44
 
101
- def build_branch_table_row(doc, item, base, patch)
102
- doc.tr do
103
- doc.td(class: 'c') { doc.text item }
104
- doc.td(class: 'a') { doc.text base }
105
- doc.td(class: 'b') { doc.text patch }
106
- end
45
+ def copy_file(src, dest)
46
+ FileUtils.cp(src, dest) if File.exist?(src)
107
47
  end
108
48
 
109
- def build_projects_section(doc)
110
- doc.div(class: 'section', id: 'projects') do
111
- doc.h2 'Projects:'
112
- build_projects_table(doc)
49
+ def write_index(target_root, base_details, patch_details, projects)
50
+ projects = projects.map do |p|
51
+ {
52
+ 'name' => p.name,
53
+ 'tag' => p.tag,
54
+ 'report_url' => "./#{p.name}/index.html",
55
+ **report_diff_to_h(p.report_diff)
56
+ }
113
57
  end
114
- end
115
58
 
116
- def build_projects_table(doc)
117
- doc.table(class: 'bodyTable', border: '0') do
118
- build_projects_table_head(doc)
119
- build_projects_table_body(doc)
120
- end
59
+ env = {
60
+ 'comparison_url' => create_comparison_url(base_details, patch_details),
61
+ 'base' => to_liquid(base_details, BASE_CONFIG_NAME),
62
+ 'patch' => to_liquid(patch_details, PATCH_CONFIG_NAME),
63
+ 'projects' => projects
64
+ }
65
+ logger.info 'Writing /index.html...'
66
+ render_and_write('project_index.html', "#{target_root}/index.html", env)
121
67
  end
122
68
 
123
- def build_projects_table_head(doc)
124
- doc.thead do
125
- doc.tr do
126
- doc.th 'project name'
127
- doc.th 'project branch/tag'
128
- doc.th 'removed|new errors size'
129
- doc.th 'removed|new violations size'
130
- doc.th 'removed|new configerrors size'
131
- end
132
- end
69
+ def create_comparison_url(base_details, patch_details)
70
+ base = CGI.escape(base_details.branch_name)
71
+ patch = CGI.escape(patch_details.branch_last_sha)
72
+ "https://github.com/pmd/pmd/compare/#{base}...#{patch}"
133
73
  end
134
74
 
135
- def build_projects_table_body(doc)
136
- doc.tbody do
137
- @projects.each do |project|
138
- doc.tr do
139
- doc.td do
140
- doc.a(href: project.diff_report_index_ref_path) { doc.text project.name }
141
- end
142
- doc.td project.tag
143
- doc.td do
144
- build_table_content_for(doc, project.removed_errors_size,
145
- project.new_errors_size)
146
- end
147
- doc.td do
148
- build_table_content_for(doc, project.removed_violations_size,
149
- project.new_violations_size)
150
- end
151
- doc.td do
152
- build_table_content_for(doc, project.removed_configerrors_size,
153
- project.new_configerrors_size)
154
- end
155
- end
156
- end
157
- end
75
+ def to_liquid(details, config_name)
76
+ {
77
+ 'tree_url' => "https://github.com/pmd/pmd/tree/#{CGI.escape(details.branch_last_sha)}",
78
+ 'name' => details.branch_name,
79
+ 'tip' => {
80
+ 'sha' => details.branch_last_sha,
81
+ 'message' => details.branch_last_message
82
+ },
83
+ 'execution_time' => PmdReportDetail.convert_seconds(details.execution_time),
84
+ 'jdk_info' => details.jdk_version,
85
+ 'locale' => details.language,
86
+ 'config_url' => config_name,
87
+ 'pr_number' => details.pull_request
88
+ }
158
89
  end
159
90
  end
160
91
  end