pmdtester 1.0.0.pre.beta3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.hoerc +1 -1
- data/.rubocop.yml +7 -0
- data/.rubocop_todo.yml +7 -8
- data/.travis.yml +20 -2
- data/Gemfile +1 -12
- data/History.md +37 -0
- data/Manifest.txt +6 -0
- data/README.rdoc +72 -46
- data/Rakefile +23 -13
- data/lib/pmdtester.rb +8 -1
- data/lib/pmdtester/builders/diff_builder.rb +8 -7
- data/lib/pmdtester/builders/diff_report/configerrors.rb +50 -0
- data/lib/pmdtester/builders/diff_report/errors.rb +71 -0
- data/lib/pmdtester/builders/diff_report/violations.rb +77 -0
- data/lib/pmdtester/builders/diff_report_builder.rb +23 -149
- data/lib/pmdtester/builders/html_report_builder.rb +23 -0
- data/lib/pmdtester/builders/pmd_report_builder.rb +69 -23
- data/lib/pmdtester/builders/rule_set_builder.rb +3 -2
- data/lib/pmdtester/builders/simple_progress_logger.rb +27 -0
- data/lib/pmdtester/builders/summary_report_builder.rb +19 -5
- data/lib/pmdtester/cmd.rb +15 -1
- data/lib/pmdtester/parsers/options.rb +6 -2
- data/lib/pmdtester/parsers/pmd_report_document.rb +11 -5
- data/lib/pmdtester/pmd_branch_detail.rb +17 -3
- data/lib/pmdtester/pmd_configerror.rb +63 -0
- data/lib/pmdtester/pmd_violation.rb +2 -0
- data/lib/pmdtester/project.rb +35 -3
- data/lib/pmdtester/report_diff.rb +67 -21
- data/lib/pmdtester/runner.rb +40 -15
- data/pmdtester.gemspec +73 -0
- metadata +62 -25
@@ -4,6 +4,7 @@ module PmdTester
|
|
4
4
|
# This class is the parent of all classes which is used to build html report
|
5
5
|
class HtmlReportBuilder
|
6
6
|
CSS_SRC_DIR = ResourceLocator.locate('resources/css')
|
7
|
+
NO_DIFFERENCES_MESSAGE = 'No differences found!'
|
7
8
|
|
8
9
|
def build_html_report(title_name)
|
9
10
|
html_builder = Nokogiri::HTML::Builder.new do |doc|
|
@@ -25,9 +26,31 @@ module PmdTester
|
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
29
|
+
def build_table_head(doc, *columns)
|
30
|
+
doc.thead do
|
31
|
+
doc.tr do
|
32
|
+
columns.each do |column|
|
33
|
+
doc.th column
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def build_table_anchor_column(doc, prefix, index)
|
40
|
+
doc.td do
|
41
|
+
doc.a(id: "#{prefix}#{index}", href: "##{prefix}#{index}") { doc.text '#' }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
28
45
|
def copy_css(report_dir)
|
29
46
|
css_dest_dir = "#{report_dir}/css"
|
30
47
|
FileUtils.copy_entry(CSS_SRC_DIR, css_dest_dir)
|
31
48
|
end
|
49
|
+
|
50
|
+
def build_table_content_for(doc, removed_size, new_size)
|
51
|
+
doc.font(color: 'red') { doc.text "-#{removed_size}" }
|
52
|
+
doc.text ' | '
|
53
|
+
doc.font(color: 'green') { doc.text "+#{new_size}" }
|
54
|
+
end
|
32
55
|
end
|
33
56
|
end
|
@@ -1,17 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'fileutils'
|
4
|
+
require 'rufus-scheduler'
|
4
5
|
|
5
6
|
module PmdTester
|
6
7
|
# Building pmd xml reports according to a list of standard
|
7
8
|
# projects and branch of pmd source code
|
8
9
|
class PmdReportBuilder
|
9
10
|
include PmdTester
|
10
|
-
def initialize(branch_config, projects, local_git_repo, pmd_branch_name)
|
11
|
+
def initialize(branch_config, projects, local_git_repo, pmd_branch_name, threads = 1)
|
11
12
|
@branch_config = branch_config
|
12
13
|
@projects = projects
|
13
14
|
@local_git_repo = local_git_repo
|
14
15
|
@pmd_branch_name = pmd_branch_name
|
16
|
+
@threads = threads
|
15
17
|
@pwd = Dir.getwd
|
16
18
|
|
17
19
|
@pmd_branch_details = PmdBranchDetail.new(pmd_branch_name)
|
@@ -28,7 +30,7 @@ module PmdTester
|
|
28
30
|
Cmd.execute(reset_cmd)
|
29
31
|
end
|
30
32
|
|
31
|
-
def
|
33
|
+
def clone_projects
|
32
34
|
logger.info 'Cloning projects started'
|
33
35
|
|
34
36
|
@projects.each do |project|
|
@@ -46,29 +48,58 @@ module PmdTester
|
|
46
48
|
end
|
47
49
|
logger.info "Cloning #{project.name} completed"
|
48
50
|
end
|
51
|
+
|
52
|
+
logger.info 'Cloning projects completed'
|
49
53
|
end
|
50
54
|
|
51
55
|
def get_pmd_binary_file
|
52
|
-
logger.info
|
56
|
+
logger.info "#{@pmd_branch_name}: Start packaging PMD"
|
53
57
|
Dir.chdir(@local_git_repo) do
|
54
|
-
|
55
|
-
Cmd.execute(
|
58
|
+
current_head_sha = Cmd.execute('git rev-parse HEAD')
|
59
|
+
current_branch_sha = Cmd.execute("git rev-parse #{@pmd_branch_name}")
|
56
60
|
|
57
|
-
@
|
58
|
-
@pmd_branch_details.branch_last_message = get_last_commit_message
|
61
|
+
@pmd_version = determine_pmd_version
|
59
62
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
+
# in case we are already on the correct branch
|
64
|
+
# and a binary zip already exists...
|
65
|
+
if current_head_sha == current_branch_sha &&
|
66
|
+
File.exist?("pmd-dist/target/pmd-bin-#{@pmd_version}.zip")
|
67
|
+
logger.warn "#{@pmd_branch_name}: Skipping packaging - zip for " \
|
68
|
+
"#{@pmd_version} already exists"
|
69
|
+
else
|
70
|
+
build_pmd
|
71
|
+
end
|
63
72
|
|
64
|
-
|
65
|
-
|
66
|
-
@pmd_version = Cmd.execute(version_cmd)
|
73
|
+
@pmd_branch_details.branch_last_sha = get_last_commit_sha
|
74
|
+
@pmd_branch_details.branch_last_message = get_last_commit_message
|
67
75
|
|
76
|
+
logger.info "#{@pmd_branch_name}: Extracting the zip"
|
68
77
|
unzip_cmd = "unzip -qo pmd-dist/target/pmd-bin-#{@pmd_version}.zip -d #{@pwd}/target"
|
69
78
|
Cmd.execute(unzip_cmd)
|
70
79
|
end
|
71
|
-
logger.info
|
80
|
+
logger.info "#{@pmd_branch_name}: Packaging PMD completed"
|
81
|
+
end
|
82
|
+
|
83
|
+
def build_pmd
|
84
|
+
logger.info "#{@pmd_branch_name}: Checking out the branch"
|
85
|
+
checkout_cmd = "git checkout #{@pmd_branch_name}"
|
86
|
+
Cmd.execute(checkout_cmd)
|
87
|
+
|
88
|
+
# determine the version again - it might be different in the other branch
|
89
|
+
@pmd_version = determine_pmd_version
|
90
|
+
|
91
|
+
logger.info "#{@pmd_branch_name}: Building PMD #{@pmd_version}..."
|
92
|
+
package_cmd = './mvnw clean package' \
|
93
|
+
' -Dmaven.test.skip=true' \
|
94
|
+
' -Dmaven.javadoc.skip=true' \
|
95
|
+
' -Dmaven.source.skip=true'
|
96
|
+
Cmd.execute(package_cmd)
|
97
|
+
end
|
98
|
+
|
99
|
+
def determine_pmd_version
|
100
|
+
version_cmd = "./mvnw -q -Dexec.executable=\"echo\" -Dexec.args='${project.version}' " \
|
101
|
+
'--non-recursive org.codehaus.mojo:exec-maven-plugin:1.5.0:exec'
|
102
|
+
Cmd.execute(version_cmd)
|
72
103
|
end
|
73
104
|
|
74
105
|
def get_last_commit_sha
|
@@ -81,26 +112,40 @@ module PmdTester
|
|
81
112
|
Cmd.execute(get_last_commit_message_cmd)
|
82
113
|
end
|
83
114
|
|
84
|
-
def generate_pmd_report(
|
115
|
+
def generate_pmd_report(project)
|
85
116
|
run_path = "target/pmd-bin-#{@pmd_version}/bin/run.sh"
|
86
|
-
pmd_cmd = "#{run_path} pmd -d #{
|
87
|
-
"-
|
117
|
+
pmd_cmd = "#{run_path} pmd -d #{project.local_source_path} -f xml " \
|
118
|
+
"-R #{project.get_config_path(@pmd_branch_name)} " \
|
119
|
+
"-r #{project.get_pmd_report_path(@pmd_branch_name)} " \
|
120
|
+
"-failOnViolation false -t #{@threads}"
|
88
121
|
start_time = Time.now
|
89
122
|
Cmd.execute(pmd_cmd)
|
90
123
|
end_time = Time.now
|
91
124
|
[end_time - start_time, end_time]
|
92
125
|
end
|
93
126
|
|
127
|
+
def generate_config_for(project)
|
128
|
+
doc = Nokogiri::XML(File.read(@branch_config))
|
129
|
+
ruleset = doc.at_css('ruleset')
|
130
|
+
project.exclude_pattern.each do |exclude_pattern|
|
131
|
+
ruleset.add_child("<exclude-pattern>#{exclude_pattern}</exclude-pattern>")
|
132
|
+
end
|
133
|
+
|
134
|
+
File.open(project.get_config_path(@pmd_branch_name), 'w') do |x|
|
135
|
+
x << doc.to_s
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
94
139
|
def generate_pmd_reports
|
95
140
|
logger.info "Generating PMD report started -- branch #{@pmd_branch_name}"
|
96
|
-
get_pmd_binary_file
|
97
141
|
|
98
142
|
sum_time = 0
|
99
143
|
@projects.each do |project|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
144
|
+
progress_logger = SimpleProgressLogger.new(project.name)
|
145
|
+
progress_logger.start
|
146
|
+
generate_config_for(project)
|
147
|
+
execution_time, end_time = generate_pmd_report(project)
|
148
|
+
progress_logger.stop
|
104
149
|
sum_time += execution_time
|
105
150
|
|
106
151
|
report_details = PmdReportDetail.new
|
@@ -117,7 +162,8 @@ module PmdTester
|
|
117
162
|
end
|
118
163
|
|
119
164
|
def build
|
120
|
-
|
165
|
+
clone_projects
|
166
|
+
get_pmd_binary_file
|
121
167
|
generate_pmd_reports
|
122
168
|
end
|
123
169
|
end
|
@@ -29,6 +29,7 @@ module PmdTester
|
|
29
29
|
output_filter_set(rule_sets)
|
30
30
|
build_config_file(rule_sets)
|
31
31
|
logger.debug "Dynamic configuration: #{[rule_sets]}"
|
32
|
+
rule_sets
|
32
33
|
end
|
33
34
|
|
34
35
|
def output_filter_set(rule_sets)
|
@@ -46,7 +47,7 @@ module PmdTester
|
|
46
47
|
base = @options.base_branch
|
47
48
|
patch = @options.patch_branch
|
48
49
|
# We only need to support git here, since PMD's repo is using git.
|
49
|
-
diff_cmd = "git diff --name-only #{base}..#{patch} -- pmd-core pmd-java"
|
50
|
+
diff_cmd = "git diff --name-only #{base}..#{patch} -- pmd-core/src/main pmd-java/src/main"
|
50
51
|
filenames = Cmd.execute(diff_cmd)
|
51
52
|
end
|
52
53
|
filenames.split("\n")
|
@@ -79,7 +80,7 @@ module PmdTester
|
|
79
80
|
def build_config_file(rule_sets)
|
80
81
|
if rule_sets.empty?
|
81
82
|
logger.info NO_JAVA_RULES_CHANGED_MESSAGE
|
82
|
-
|
83
|
+
return
|
83
84
|
end
|
84
85
|
|
85
86
|
doc = Nokogiri::XML(File.read(PATH_TO_ALL_JAVA_RULES))
|
@@ -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(project_name)
|
10
|
+
@project_name = project_name
|
11
|
+
end
|
12
|
+
|
13
|
+
def start
|
14
|
+
logger.info "Starting to generate #{@project_name}'s PMD report"
|
15
|
+
message_counter = 1
|
16
|
+
@scheduler = Rufus::Scheduler.new
|
17
|
+
@scheduler.every '2m' do
|
18
|
+
logger.info "Still generating #{@project_name}'s PMD report (#{message_counter})..."
|
19
|
+
message_counter += 1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def stop
|
24
|
+
@scheduler.shutdown
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -29,7 +29,6 @@ module PmdTester
|
|
29
29
|
def get_branch_details(branch_name)
|
30
30
|
details = PmdBranchDetail.new(branch_name)
|
31
31
|
details.load
|
32
|
-
details
|
33
32
|
end
|
34
33
|
|
35
34
|
def build_body(doc)
|
@@ -71,6 +70,10 @@ module PmdTester
|
|
71
70
|
@patch_details.branch_last_message)
|
72
71
|
build_branch_table_row(doc, 'total execution time', @base_details.format_execution_time,
|
73
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)
|
74
77
|
build_branch_config_table_row(doc)
|
75
78
|
end
|
76
79
|
end
|
@@ -122,8 +125,9 @@ module PmdTester
|
|
122
125
|
doc.tr do
|
123
126
|
doc.th 'project name'
|
124
127
|
doc.th 'project branch/tag'
|
125
|
-
doc.th '
|
126
|
-
doc.th '
|
128
|
+
doc.th 'removed|new errors size'
|
129
|
+
doc.th 'removed|new violations size'
|
130
|
+
doc.th 'removed|new configerrors size'
|
127
131
|
end
|
128
132
|
end
|
129
133
|
end
|
@@ -136,8 +140,18 @@ module PmdTester
|
|
136
140
|
doc.a(href: project.diff_report_index_ref_path) { doc.text project.name }
|
137
141
|
end
|
138
142
|
doc.td project.tag
|
139
|
-
doc.td
|
140
|
-
|
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
|
141
155
|
end
|
142
156
|
end
|
143
157
|
end
|
data/lib/pmdtester/cmd.rb
CHANGED
@@ -7,20 +7,34 @@ module PmdTester
|
|
7
7
|
class Cmd
|
8
8
|
extend PmdTester
|
9
9
|
def self.execute(cmd)
|
10
|
+
stdout, _stderr, _status = internal_execute(cmd)
|
11
|
+
stdout&.chomp!
|
12
|
+
stdout
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.stderr_of(cmd)
|
16
|
+
_stdout, stderr, _status = internal_execute(cmd)
|
17
|
+
stderr
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.internal_execute(cmd)
|
10
21
|
logger.debug "execute command '#{cmd}'"
|
11
22
|
|
12
23
|
stdout, stderr, status = Open3.capture3("#{cmd};")
|
13
24
|
|
14
25
|
logger.debug stdout
|
15
26
|
unless status.success?
|
27
|
+
logger.error stdout
|
16
28
|
logger.error stderr
|
17
29
|
raise CmdException.new(cmd, stderr)
|
18
30
|
end
|
19
31
|
|
20
32
|
stdout&.chomp!
|
21
33
|
|
22
|
-
stdout
|
34
|
+
[stdout, stderr, status]
|
23
35
|
end
|
36
|
+
|
37
|
+
private_class_method :internal_execute
|
24
38
|
end
|
25
39
|
|
26
40
|
# The exception should be raised when the shell command failed.
|
@@ -25,6 +25,7 @@ module PmdTester
|
|
25
25
|
attr_reader :config
|
26
26
|
attr_reader :project_list
|
27
27
|
attr_reader :mode
|
28
|
+
attr_reader :threads
|
28
29
|
attr_reader :html_flag
|
29
30
|
attr_reader :auto_config_flag
|
30
31
|
attr_reader :debug_flag
|
@@ -40,6 +41,7 @@ module PmdTester
|
|
40
41
|
@config = options[:c]
|
41
42
|
@project_list = options[:l]
|
42
43
|
@mode = options[:m]
|
44
|
+
@threads = options[:t]
|
43
45
|
@html_flag = options[:f]
|
44
46
|
@auto_config_flag = options[:a]
|
45
47
|
@debug_flag = options[:d]
|
@@ -61,8 +63,8 @@ module PmdTester
|
|
61
63
|
single: Set this option to 'single' if your patch branch contains changes
|
62
64
|
for any option that can't work on master/base branch
|
63
65
|
online: Set this option to 'online' if you want to download
|
64
|
-
|
65
|
-
local: Default option is 'local'
|
66
|
+
the PMD report of master/base branch rather than generating it locally
|
67
|
+
local: Default option is 'local', PMD reports for the base and patch branches are generated locally.
|
66
68
|
DOC
|
67
69
|
|
68
70
|
Slop.parse argv do |o|
|
@@ -79,6 +81,8 @@ module PmdTester
|
|
79
81
|
'path to the file which contains the list of standard projects',
|
80
82
|
default: DEFAULT_LIST_PATH
|
81
83
|
o.string '-m', '--mode', mode_message, default: 'local'
|
84
|
+
o.integer '-t', '--threads', 'Sets the number of threads used by PMD.' \
|
85
|
+
' Set threads to 0 to disable multi-threading processing.', default: 1
|
82
86
|
o.bool '-f', '--html-flag',
|
83
87
|
'whether to not generate the html diff report in single mode'
|
84
88
|
o.bool '-a', '--auto-gen-config',
|
@@ -7,12 +7,15 @@ module PmdTester
|
|
7
7
|
class PmdReportDocument < Nokogiri::XML::SAX::Document
|
8
8
|
attr_reader :violations
|
9
9
|
attr_reader :errors
|
10
|
+
attr_reader :configerrors
|
10
11
|
def initialize(branch_name, working_dir, filter_set = nil)
|
11
12
|
@violations = PmdViolations.new
|
12
13
|
@errors = PmdErrors.new
|
14
|
+
@configerrors = PmdConfigErrors.new
|
13
15
|
@current_violations = []
|
14
16
|
@current_violation = nil
|
15
17
|
@current_error = nil
|
18
|
+
@current_configerror = nil
|
16
19
|
@current_element = ''
|
17
20
|
@filename = ''
|
18
21
|
@filter_set = filter_set
|
@@ -34,6 +37,8 @@ module PmdTester
|
|
34
37
|
@current_filename = remove_work_dir!(attrs['filename'])
|
35
38
|
remove_work_dir!(attrs['msg'])
|
36
39
|
@current_error = PmdError.new(attrs, @branch_name)
|
40
|
+
when 'configerror'
|
41
|
+
@current_configerror = PmdConfigError.new(attrs, @branch_name)
|
37
42
|
end
|
38
43
|
end
|
39
44
|
|
@@ -42,15 +47,13 @@ module PmdTester
|
|
42
47
|
end
|
43
48
|
|
44
49
|
def characters(string)
|
45
|
-
@current_violation
|
50
|
+
@current_violation&.text = string
|
46
51
|
end
|
47
52
|
|
48
53
|
def end_element(name)
|
49
54
|
case name
|
50
55
|
when 'file'
|
51
|
-
|
52
|
-
@violations.add_violations_by_filename(@current_filename, @current_violations)
|
53
|
-
end
|
56
|
+
@violations.add_violations_by_filename(@current_filename, @current_violations)
|
54
57
|
@current_filename = nil
|
55
58
|
when 'violation'
|
56
59
|
@current_violations.push(@current_violation) if match_filter_set?(@current_violation)
|
@@ -59,12 +62,15 @@ module PmdTester
|
|
59
62
|
@errors.add_error_by_filename(@current_filename, @current_error)
|
60
63
|
@current_filename = nil
|
61
64
|
@current_error = nil
|
65
|
+
when 'configerror'
|
66
|
+
@configerrors.add_error(@current_configerror)
|
67
|
+
@current_configerror = nil
|
62
68
|
end
|
63
69
|
end
|
64
70
|
|
65
71
|
def cdata_block(string)
|
66
72
|
remove_work_dir!(string)
|
67
|
-
@current_error
|
73
|
+
@current_error&.text = string
|
68
74
|
end
|
69
75
|
|
70
76
|
def match_filter_set?(violation)
|
@@ -5,11 +5,15 @@ require 'json'
|
|
5
5
|
module PmdTester
|
6
6
|
# This class represents all details about branch of pmd
|
7
7
|
class PmdBranchDetail
|
8
|
+
include PmdTester
|
9
|
+
|
8
10
|
attr_accessor :branch_last_sha
|
9
11
|
attr_accessor :branch_last_message
|
10
12
|
attr_accessor :branch_name
|
11
13
|
# The branch's execution time on all standard projects
|
12
14
|
attr_accessor :execution_time
|
15
|
+
attr_reader :jdk_version
|
16
|
+
attr_reader :language
|
13
17
|
|
14
18
|
def self.branch_filename(branch_name)
|
15
19
|
branch_name&.tr('/', '_')
|
@@ -22,6 +26,9 @@ module PmdTester
|
|
22
26
|
branch_filename = PmdBranchDetail.branch_filename(branch_name)
|
23
27
|
@base_branch_dir = "target/reports/#{branch_filename}" unless @branch_name.nil?
|
24
28
|
@execution_time = 0
|
29
|
+
# the result of command 'java -version' is going to stderr
|
30
|
+
@jdk_version = Cmd.stderr_of('java -version')
|
31
|
+
@language = ENV['LANG']
|
25
32
|
end
|
26
33
|
|
27
34
|
def load
|
@@ -31,20 +38,27 @@ module PmdTester
|
|
31
38
|
@branch_last_message = hash['branch_last_message']
|
32
39
|
@branch_name = hash['branch_name']
|
33
40
|
@execution_time = hash['execution_time']
|
34
|
-
hash
|
41
|
+
@jdk_version = hash['jdk_version']
|
42
|
+
@language = hash['language']
|
35
43
|
else
|
36
|
-
|
44
|
+
@jdk_version = ''
|
45
|
+
@language = ''
|
46
|
+
logger.warn "#{branch_details_path} doesn't exist!"
|
37
47
|
end
|
48
|
+
self
|
38
49
|
end
|
39
50
|
|
40
51
|
def save
|
41
52
|
hash = { branch_last_sha: @branch_last_sha,
|
42
53
|
branch_last_message: @branch_last_message,
|
43
54
|
branch_name: @branch_name,
|
44
|
-
execution_time: @execution_time
|
55
|
+
execution_time: @execution_time,
|
56
|
+
jdk_version: @jdk_version,
|
57
|
+
language: @language }
|
45
58
|
file = File.new(branch_details_path, 'w')
|
46
59
|
file.puts JSON.generate(hash)
|
47
60
|
file.close
|
61
|
+
self
|
48
62
|
end
|
49
63
|
|
50
64
|
def branch_details_path
|