pmdtester 1.0.1 → 1.1.0

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