pmdtester 1.0.0.pre.beta2 → 1.1.1
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.
- checksums.yaml +4 -4
- data/.ci/build.sh +67 -0
- data/.ci/files/env.gpg +1 -0
- data/.ci/inc/install-openjdk.inc +26 -0
- data/.ci/manual-integration-tests.sh +20 -0
- data/.github/workflows/build.yml +39 -0
- data/.github/workflows/manual-integration-tests.yml +32 -0
- data/.gitignore +9 -0
- data/.hoerc +1 -0
- data/.rubocop.yml +21 -2
- data/.rubocop_todo.yml +7 -8
- data/.ruby-version +1 -0
- data/Gemfile +1 -13
- data/History.md +108 -0
- data/Manifest.txt +60 -0
- data/README.rdoc +130 -7
- data/Rakefile +31 -17
- data/bin/pmdtester +1 -1
- data/config/all-java.xml +1 -1
- data/config/design.xml +1 -1
- data/config/projectlist_1_0_0.xsd +2 -1
- data/config/projectlist_1_1_0.xsd +31 -0
- data/lib/pmdtester.rb +48 -0
- data/lib/pmdtester/builders/liquid_renderer.rb +73 -0
- data/lib/pmdtester/builders/pmd_report_builder.rb +133 -64
- data/lib/pmdtester/builders/project_builder.rb +100 -0
- data/lib/pmdtester/builders/project_hasher.rb +126 -0
- data/lib/pmdtester/builders/rule_set_builder.rb +95 -51
- data/lib/pmdtester/builders/simple_progress_logger.rb +27 -0
- data/lib/pmdtester/builders/summary_report_builder.rb +62 -120
- data/lib/pmdtester/cmd.rb +15 -1
- data/lib/pmdtester/collection_by_file.rb +55 -0
- data/lib/pmdtester/parsers/options.rb +33 -10
- data/lib/pmdtester/parsers/pmd_report_document.rb +79 -29
- data/lib/pmdtester/parsers/projects_parser.rb +2 -6
- data/lib/pmdtester/pmd_branch_detail.rb +36 -13
- data/lib/pmdtester/pmd_configerror.rb +62 -0
- data/lib/pmdtester/pmd_error.rb +34 -34
- data/lib/pmdtester/pmd_report_detail.rb +10 -13
- data/lib/pmdtester/pmd_tester_utils.rb +57 -0
- data/lib/pmdtester/pmd_violation.rb +66 -26
- data/lib/pmdtester/project.rb +28 -25
- data/lib/pmdtester/report_diff.rb +194 -70
- data/lib/pmdtester/resource_locator.rb +4 -0
- data/lib/pmdtester/runner.rb +82 -57
- data/pmdtester.gemspec +64 -0
- data/resources/_includes/diff_pill_row.html +6 -0
- data/resources/css/bootstrap.min.css +7 -0
- data/resources/css/datatables.min.css +36 -0
- data/resources/css/pmd-tester.css +131 -0
- data/resources/js/bootstrap.min.js +7 -0
- data/resources/js/code-snippets.js +66 -0
- data/resources/js/datatables.min.js +726 -0
- data/resources/js/jquery-3.2.1.slim.min.js +4 -0
- data/resources/js/jquery.min.js +2 -0
- data/resources/js/popper.min.js +5 -0
- data/resources/js/project-report.js +136 -0
- data/resources/project_diff_report.html +205 -0
- data/resources/project_index.html +102 -0
- metadata +117 -44
- data/.travis.yml +0 -22
- data/lib/pmdtester/builders/diff_builder.rb +0 -35
- data/lib/pmdtester/builders/diff_report_builder.rb +0 -226
- data/lib/pmdtester/builders/html_report_builder.rb +0 -34
- data/lib/pmdtester/pmdtester.rb +0 -17
- data/resources/css/maven-base.css +0 -155
- data/resources/css/maven-theme.css +0 -171
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'tempfile'
|
|
5
|
+
|
|
6
|
+
module PmdTester
|
|
7
|
+
# Clones and builds the projects, that are configured in the project-list.xml
|
|
8
|
+
class ProjectBuilder
|
|
9
|
+
include PmdTester
|
|
10
|
+
|
|
11
|
+
def initialize(projects)
|
|
12
|
+
@projects = projects
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def clone_projects
|
|
16
|
+
logger.info 'Cloning projects started'
|
|
17
|
+
|
|
18
|
+
@projects.each do |project|
|
|
19
|
+
logger.info "Start cloning #{project.name} repository"
|
|
20
|
+
path = project.local_source_path
|
|
21
|
+
clone_cmd = "#{project.type} clone #{project.connection} #{path}"
|
|
22
|
+
if File.exist?(path)
|
|
23
|
+
logger.warn "Skipping clone, project path #{path} already exists"
|
|
24
|
+
else
|
|
25
|
+
Cmd.execute(clone_cmd)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
Dir.chdir(path) do
|
|
29
|
+
execute_reset_cmd(project.type, project.tag)
|
|
30
|
+
end
|
|
31
|
+
logger.info "Cloning #{project.name} completed"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
logger.info 'Cloning projects completed'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def build_projects
|
|
38
|
+
logger.info 'Building projects started'
|
|
39
|
+
|
|
40
|
+
@projects.each do |project|
|
|
41
|
+
path = project.local_source_path
|
|
42
|
+
Dir.chdir(path) do
|
|
43
|
+
progress_logger = SimpleProgressLogger.new("building #{project.name} in #{path}")
|
|
44
|
+
progress_logger.start
|
|
45
|
+
prepare_project(project)
|
|
46
|
+
progress_logger.stop
|
|
47
|
+
end
|
|
48
|
+
logger.info "Building #{project.name} completed"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
logger.info 'Building projects completed'
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def prepare_project(project)
|
|
57
|
+
# Note: current working directory is the project directory,
|
|
58
|
+
# where the source code has been cloned to
|
|
59
|
+
if project.build_command
|
|
60
|
+
logger.debug "Executing build-command: #{project.build_command}"
|
|
61
|
+
run_as_script(Dir.getwd, project.build_command)
|
|
62
|
+
end
|
|
63
|
+
if project.auxclasspath_command
|
|
64
|
+
logger.debug "Executing auxclasspath-command: #{project.auxclasspath_command}"
|
|
65
|
+
auxclasspath = run_as_script(Dir.getwd, project.auxclasspath_command)
|
|
66
|
+
project.auxclasspath = "-auxclasspath #{auxclasspath}"
|
|
67
|
+
else
|
|
68
|
+
project.auxclasspath = ''
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def run_as_script(path, command)
|
|
73
|
+
script = Tempfile.new(['pmd-regression-', '.sh'], path)
|
|
74
|
+
logger.debug "Creating script #{script.path}"
|
|
75
|
+
begin
|
|
76
|
+
script.write(command)
|
|
77
|
+
script.close
|
|
78
|
+
shell = 'sh -xe'
|
|
79
|
+
if command.start_with?('#!')
|
|
80
|
+
shell = command.lines[0].chomp[2..] # remove leading "#!"
|
|
81
|
+
end
|
|
82
|
+
stdout = Cmd.execute("#{shell} #{script.path}")
|
|
83
|
+
ensure
|
|
84
|
+
script.unlink
|
|
85
|
+
end
|
|
86
|
+
stdout
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def execute_reset_cmd(type, tag)
|
|
90
|
+
case type
|
|
91
|
+
when 'git'
|
|
92
|
+
reset_cmd = "git checkout #{tag}; git reset --hard #{tag}"
|
|
93
|
+
when 'hg'
|
|
94
|
+
reset_cmd = "hg up #{tag}"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
Cmd.execute(reset_cmd)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -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.transform_keys(&:to_s),
|
|
13
|
+
'error_counts' => rdiff.error_counts.to_h.transform_keys(&:to_s),
|
|
14
|
+
'configerror_counts' => rdiff.configerror_counts.to_h.transform_keys(&:to_s),
|
|
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
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require 'nokogiri'
|
|
4
4
|
require 'set'
|
|
5
|
-
require_relative '../cmd'
|
|
6
|
-
require_relative '../resource_locator'
|
|
7
5
|
|
|
8
6
|
module PmdTester
|
|
9
7
|
# This class is responsible for generation dynamic configuration
|
|
@@ -11,8 +9,9 @@ module PmdTester
|
|
|
11
9
|
# Attention: we only consider java rulesets now.
|
|
12
10
|
class RuleSetBuilder
|
|
13
11
|
include PmdTester
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
ALL_CATEGORIES = Set['bestpractices.xml', 'codestyle.xml', 'design.xml', 'documentation.xml',
|
|
13
|
+
'errorprone.xml', 'multithreading.xml', 'performance.xml',
|
|
14
|
+
'security.xml'].freeze
|
|
16
15
|
PATH_TO_PMD_JAVA_BASED_RULES =
|
|
17
16
|
'pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule'
|
|
18
17
|
PATH_TO_PMD_XPATH_BASED_RULES = 'pmd-java/src/main/resources/category/java'
|
|
@@ -27,18 +26,35 @@ module PmdTester
|
|
|
27
26
|
|
|
28
27
|
def build
|
|
29
28
|
filenames = diff_filenames
|
|
30
|
-
|
|
31
|
-
output_filter_set(
|
|
32
|
-
build_config_file(
|
|
33
|
-
logger.debug "Dynamic configuration: #{[
|
|
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
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
def output_filter_set(
|
|
37
|
-
if
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
40
55
|
else
|
|
41
|
-
|
|
56
|
+
logger.debug "Filter is now #{rule_refs}"
|
|
57
|
+
@options.filter_set = rule_refs
|
|
42
58
|
end
|
|
43
59
|
end
|
|
44
60
|
|
|
@@ -48,64 +64,92 @@ module PmdTester
|
|
|
48
64
|
base = @options.base_branch
|
|
49
65
|
patch = @options.patch_branch
|
|
50
66
|
# We only need to support git here, since PMD's repo is using git.
|
|
51
|
-
diff_cmd = "git diff --name-only #{base}..#{patch} -- pmd/
|
|
67
|
+
diff_cmd = "git diff --name-only #{base}..#{patch} -- pmd-core/src/main pmd-java/src/main"
|
|
52
68
|
filenames = Cmd.execute(diff_cmd)
|
|
53
69
|
end
|
|
54
70
|
filenames.split("\n")
|
|
55
71
|
end
|
|
56
72
|
|
|
57
|
-
def
|
|
58
|
-
|
|
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[]
|
|
59
92
|
filenames.each do |filename|
|
|
60
|
-
match_data =
|
|
61
|
-
|
|
62
|
-
|
|
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
|
|
63
101
|
end
|
|
64
102
|
|
|
65
|
-
|
|
66
|
-
nil
|
|
67
|
-
else
|
|
68
|
-
match_data[1]
|
|
69
|
-
end
|
|
103
|
+
next unless match_data.nil?
|
|
70
104
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
rule_sets.add(category)
|
|
76
|
-
end
|
|
105
|
+
logger.debug "Change doesn't match specific rule/category - enable all rules"
|
|
106
|
+
categories = ALL_CATEGORIES
|
|
107
|
+
rules.clear
|
|
108
|
+
break
|
|
77
109
|
end
|
|
78
|
-
|
|
110
|
+
[categories, rules]
|
|
79
111
|
end
|
|
80
112
|
|
|
81
|
-
def
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
|
86
120
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
121
|
+
def build_config_file(rule_refs)
|
|
122
|
+
if rule_refs.empty?
|
|
123
|
+
logger.info NO_JAVA_RULES_CHANGED_MESSAGE
|
|
124
|
+
return
|
|
90
125
|
end
|
|
91
126
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def match_ref?(rule_node, rule_sets)
|
|
99
|
-
rule_sets.each do |rule_set|
|
|
100
|
-
return true unless rule_node['ref'].index(rule_set).nil?
|
|
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
|
|
101
132
|
end
|
|
102
133
|
|
|
103
|
-
|
|
134
|
+
write_dynamic_file(rule_refs)
|
|
104
135
|
end
|
|
105
136
|
|
|
106
|
-
def write_dynamic_file(
|
|
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
|
|
149
|
+
end
|
|
150
|
+
doc = builder.to_xml(indent: 4, encoding: 'UTF-8')
|
|
107
151
|
File.open(PATH_TO_DYNAMIC_CONFIG, 'w') do |x|
|
|
108
|
-
x << doc.
|
|
152
|
+
x << doc.gsub(/\n\s+\n/, "\n")
|
|
109
153
|
end
|
|
110
154
|
@options.base_config = PATH_TO_DYNAMIC_CONFIG
|
|
111
155
|
@options.patch_config = PATH_TO_DYNAMIC_CONFIG
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rufus-scheduler'
|
|
4
|
+
|
|
5
|
+
module PmdTester
|
|
6
|
+
# Helper class that provides a simple progress logging
|
|
7
|
+
class SimpleProgressLogger
|
|
8
|
+
include PmdTester
|
|
9
|
+
def initialize(task_name)
|
|
10
|
+
@task_name = task_name
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def start
|
|
14
|
+
logger.info "Starting #{@task_name}"
|
|
15
|
+
message_counter = 1
|
|
16
|
+
@scheduler = Rufus::Scheduler.new
|
|
17
|
+
@scheduler.every '2m' do
|
|
18
|
+
logger.info "Still #{@task_name} (#{message_counter})..."
|
|
19
|
+
message_counter += 1
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def stop
|
|
24
|
+
@scheduler.shutdown
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|