kwala 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +62 -0
- data/bin/kwala +39 -0
- data/lib/kwala.rb +59 -0
- data/lib/kwala/actions/code_change.rb +128 -0
- data/lib/kwala/actions/code_coverage.rb +303 -0
- data/lib/kwala/actions/code_duplication.rb +222 -0
- data/lib/kwala/actions/code_formatting.rb +358 -0
- data/lib/kwala/actions/cycle_detection.rb +118 -0
- data/lib/kwala/actions/cyclomatic_complexity.rb +159 -0
- data/lib/kwala/actions/dead_code.rb +68 -0
- data/lib/kwala/actions/executable_files_check.rb +111 -0
- data/lib/kwala/actions/flagged_comments.rb +128 -0
- data/lib/kwala/actions/gem_plugin.rb +31 -0
- data/lib/kwala/actions/loc_count.rb +153 -0
- data/lib/kwala/actions/multi_ruby_unit_test.rb +62 -0
- data/lib/kwala/actions/outside_links.rb +85 -0
- data/lib/kwala/actions/rails_migrate.rb +62 -0
- data/lib/kwala/actions/strange_requires.rb +106 -0
- data/lib/kwala/actions/syntax_check.rb +130 -0
- data/lib/kwala/actions/unit_test.rb +368 -0
- data/lib/kwala/build_action.rb +88 -0
- data/lib/kwala/build_context.rb +49 -0
- data/lib/kwala/command_line_runner.rb +184 -0
- data/lib/kwala/ext/demos.jar +0 -0
- data/lib/kwala/ext/pmd.jar +0 -0
- data/lib/kwala/ext/prefuse.jar +0 -0
- data/lib/kwala/extensions.rb +36 -0
- data/lib/kwala/lib/code_analyzer.rb +678 -0
- data/lib/kwala/lib/cycle_detector.rb +793 -0
- data/lib/kwala/lib/strange_requires_detector.rb +145 -0
- data/lib/kwala/notification.rb +45 -0
- data/lib/kwala/notifications/email.rb +54 -0
- data/lib/kwala/notifications/rss.rb +151 -0
- data/lib/kwala/project_builder_utils.rb +178 -0
- data/lib/kwala/templates/build_template.html +33 -0
- data/lib/kwala/templates/code_change_summary.html +27 -0
- data/lib/kwala/templates/code_coverage_detailed.html +25 -0
- data/lib/kwala/templates/code_coverage_summary.html +30 -0
- data/lib/kwala/templates/code_duplication_detailed.html +45 -0
- data/lib/kwala/templates/code_duplication_summary.html +9 -0
- data/lib/kwala/templates/code_formatting_detailed.html +27 -0
- data/lib/kwala/templates/code_formatting_summary.html +11 -0
- data/lib/kwala/templates/cycle_detection_detailed.html +20 -0
- data/lib/kwala/templates/cycle_detection_summary.html +7 -0
- data/lib/kwala/templates/cyclomatic_complexity_summary.html +27 -0
- data/lib/kwala/templates/executable_files_check_detailed.html +31 -0
- data/lib/kwala/templates/executable_files_check_summary.html +20 -0
- data/lib/kwala/templates/flagged_comments_detailed.html +22 -0
- data/lib/kwala/templates/flagged_comments_summary.html +10 -0
- data/lib/kwala/templates/loc_count_detailed.html +24 -0
- data/lib/kwala/templates/loc_count_summary.html +14 -0
- data/lib/kwala/templates/mdd/1.png +0 -0
- data/lib/kwala/templates/mdd/2.png +0 -0
- data/lib/kwala/templates/mdd/3.png +0 -0
- data/lib/kwala/templates/mdd/4.png +0 -0
- data/lib/kwala/templates/mdd/5.png +0 -0
- data/lib/kwala/templates/outside_links_summary.html +4 -0
- data/lib/kwala/templates/strange_requires_detailed.html +23 -0
- data/lib/kwala/templates/strange_requires_summary.html +13 -0
- data/lib/kwala/templates/style.css +95 -0
- data/lib/kwala/templates/syntax_check_detailed.html +21 -0
- data/lib/kwala/templates/syntax_check_summary.html +7 -0
- data/lib/kwala/templates/unit_test_detailed.html +66 -0
- data/lib/kwala/templates/unit_test_summary.html +70 -0
- data/test/tc_build_action.rb +32 -0
- data/test/tc_templates.rb +24 -0
- metadata +118 -0
@@ -0,0 +1,145 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
# Copyright (c) 2006, Ubiquitous Business Technology (http://ubit.com)
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
7
|
+
# modification, are permitted provided that the following conditions are
|
8
|
+
# met:
|
9
|
+
#
|
10
|
+
#
|
11
|
+
# * Redistributions of source code must retain the above copyright
|
12
|
+
# notice, this list of conditions and the following disclaimer.
|
13
|
+
#
|
14
|
+
# * Redistributions in binary form must reproduce the above
|
15
|
+
# copyright notice, this list of conditions and the following
|
16
|
+
# disclaimer in the documentation and/or other materials provided
|
17
|
+
# with the distribution.
|
18
|
+
#
|
19
|
+
# * Neither the name of Ubit nor the names of its
|
20
|
+
# contributors may be used to endorse or promote products derived
|
21
|
+
# from this software without specific prior written permission.
|
22
|
+
#
|
23
|
+
#
|
24
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
25
|
+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
26
|
+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
27
|
+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
28
|
+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
29
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
30
|
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
31
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
32
|
+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
33
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
34
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
35
|
+
#
|
36
|
+
# == Author
|
37
|
+
# Michael Reinsch (mr@ubit.com)
|
38
|
+
|
39
|
+
require File.dirname(__FILE__) + "/cycle_detector" # for ReqWalker
|
40
|
+
|
41
|
+
class Dir
|
42
|
+
def == (other)
|
43
|
+
case other
|
44
|
+
when String; return path == other
|
45
|
+
when Dir; return path == other.path
|
46
|
+
when FakeDir; return path == other.path
|
47
|
+
end
|
48
|
+
false
|
49
|
+
end
|
50
|
+
|
51
|
+
def is_subdir_of?(dir)
|
52
|
+
return path[0..dir.path.length-1] == dir.path
|
53
|
+
end
|
54
|
+
|
55
|
+
def cut(basedir)
|
56
|
+
path
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
class StrangeRequiresDetector
|
62
|
+
|
63
|
+
attr_writer :print_strategy
|
64
|
+
|
65
|
+
def initialize(printer)
|
66
|
+
@graph = Hash.new
|
67
|
+
@printer = printer
|
68
|
+
@file_filter = Proc.new do |files|
|
69
|
+
files.find_all do |f|
|
70
|
+
/tests\// !~ f && /extensions\// !~ f &&
|
71
|
+
/tools\// !~ f && /\/ms\// !~ f && /\/site\// !~f
|
72
|
+
end
|
73
|
+
end
|
74
|
+
@req_filter = Proc.new do |reqs|
|
75
|
+
reqs.find_all do |r|
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def cut_base(filename, basename)
|
82
|
+
filename.gsub(/^#{basename}/, '')
|
83
|
+
end
|
84
|
+
|
85
|
+
def check(dir, basedir = "")
|
86
|
+
files = ReqWalker.find_ruby_files(dir)
|
87
|
+
@file_filter.call(files).each do |f|
|
88
|
+
ReqWalker.get_requires(f).each do |r|
|
89
|
+
r = ReqWalker.find_file(r)
|
90
|
+
if r && /\/lib\/ruby\// !~ r
|
91
|
+
f_base = Dir.new(File.dirname(f))
|
92
|
+
r_base = Dir.new(File.dirname(r))
|
93
|
+
# well, we assume that a directory models our "class category"...
|
94
|
+
if f_base != r_base
|
95
|
+
if r_base.is_subdir_of?(f_base)
|
96
|
+
@printer.subdir_requirement(cut_base(f,basedir), cut_base(r,basedir))
|
97
|
+
else
|
98
|
+
@printer.parent_dir_requirement(cut_base(f,basedir), cut_base(r,basedir))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
if __FILE__ == $0
|
110
|
+
class STDOUTPrintStrategy
|
111
|
+
def subdir_requirement(from, to)
|
112
|
+
puts "yellow: #{from} -> #{to}"
|
113
|
+
end
|
114
|
+
def parent_dir_requirement(from, to)
|
115
|
+
puts "red : #{from} -> #{to}"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
require 'getoptlong'
|
120
|
+
|
121
|
+
srd = StrangeRequiresDetector.new(STDOUTPrintStrategy.new)
|
122
|
+
dir = "./"
|
123
|
+
|
124
|
+
begin
|
125
|
+
options = GetoptLong.new(
|
126
|
+
["-d","--dir", GetoptLong::REQUIRED_ARGUMENT]
|
127
|
+
)
|
128
|
+
|
129
|
+
options.each do |opt,arg|
|
130
|
+
case opt
|
131
|
+
when "-d"
|
132
|
+
dir = arg
|
133
|
+
else
|
134
|
+
STDERR.puts "Unknown command #{ opt }"
|
135
|
+
exit 1
|
136
|
+
end
|
137
|
+
end
|
138
|
+
rescue => err
|
139
|
+
STDERR.puts "Error invalid command"
|
140
|
+
exit 1
|
141
|
+
end
|
142
|
+
|
143
|
+
srd.check(dir)
|
144
|
+
|
145
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
class Notification
|
3
|
+
@@notifications = []
|
4
|
+
|
5
|
+
def self.context=(context)
|
6
|
+
@context = context
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.context
|
10
|
+
@context
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.notify(event, result, data)
|
14
|
+
self.notifications.each { |n| n.notify(event, result, data) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.add_notification(obj)
|
18
|
+
self.notifications << obj
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.notifications
|
22
|
+
if @@notifications.empty?
|
23
|
+
Dir.glob(NOTIFICATION_DIR + "/*.rb").sort.each do |file|
|
24
|
+
begin
|
25
|
+
require file
|
26
|
+
rescue Exception => err
|
27
|
+
$stderr.puts "Notification error loading #{file}", err.class, err.message
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
@@notifications
|
32
|
+
end
|
33
|
+
|
34
|
+
def notify(event, result = nil, data = nil)
|
35
|
+
if self.respond_to?(event)
|
36
|
+
self.send(event, result, data, Notification.context)
|
37
|
+
else
|
38
|
+
undefined_event(event, result, data, Notification.context)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def undefined_event(event, result, data, context)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'net/smtp'
|
2
|
+
|
3
|
+
class EmailNotification < Notification
|
4
|
+
|
5
|
+
def unit_test(result, data, context)
|
6
|
+
# TODO: Probably should move this to a configuration based
|
7
|
+
from_addr = ""
|
8
|
+
to_addr = [ "" ]
|
9
|
+
smtp_server = ""
|
10
|
+
smtp_port = 25
|
11
|
+
|
12
|
+
# Get the time and set the status msg
|
13
|
+
now = Time.now
|
14
|
+
status_msg = "Unit tests run #{result.to_s} at"
|
15
|
+
|
16
|
+
# just file name for now
|
17
|
+
data ||= {}
|
18
|
+
data = Array(data[:filtered_results]).map do |d|
|
19
|
+
d[:file_name]
|
20
|
+
end
|
21
|
+
|
22
|
+
if !data.empty?
|
23
|
+
failure_msg = <<-MSG
|
24
|
+
Test files that reported failures:
|
25
|
+
#{data.join("\n")}
|
26
|
+
MSG
|
27
|
+
else
|
28
|
+
failure_msg = ''
|
29
|
+
end
|
30
|
+
|
31
|
+
# Create the SMTP envelope
|
32
|
+
msgstr = <<-END_OF_MESSAGE
|
33
|
+
From: Kwala Continuous Integration <EMAIL>
|
34
|
+
To: Edge Dev Mailing List <EMAIL>
|
35
|
+
Subject: [Kwala] edge build #{context.vcs_num} #{result}
|
36
|
+
Date: #{now.strftime("%a, %d %b %Y %X +0900")}
|
37
|
+
Message-Id: <#{now.strftime("%m%d%Y%H%M%S")}@EMAIL>
|
38
|
+
|
39
|
+
Result Summary: #{result.to_s}
|
40
|
+
See Results at https://BASEURL/kwala/
|
41
|
+
|
42
|
+
#{failure_msg}
|
43
|
+
|
44
|
+
Commit Info:
|
45
|
+
#{context.vcs_info}
|
46
|
+
END_OF_MESSAGE
|
47
|
+
|
48
|
+
# Send the message
|
49
|
+
Net::SMTP.start(smtp_server, smtp_port) do |smtp|
|
50
|
+
smtp.send_message(msgstr, from_addr, *to_addr)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'pstore'
|
2
|
+
require 'rss/2.0'
|
3
|
+
require 'rss/utils'
|
4
|
+
|
5
|
+
class RSSNotification < Notification
|
6
|
+
|
7
|
+
def unit_test(result, data, context)
|
8
|
+
@test_results = data[:test_results]
|
9
|
+
|
10
|
+
update_pstore(context)
|
11
|
+
make_rss(context)
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
PSTORE_FILE_NAME = 'prev_test_results.pstore'
|
17
|
+
|
18
|
+
RSS_FILE_NAME = 'unit_test_results.rss'
|
19
|
+
|
20
|
+
class UnitTestResult
|
21
|
+
attr_reader :filename, :test_results, :errors, :warnings, :first_seen
|
22
|
+
attr_accessor :last_seen
|
23
|
+
|
24
|
+
def initialize(result_hash, timestamp)
|
25
|
+
@filename = result_hash[:file_name]
|
26
|
+
@errors = result_hash[:sys_errors]
|
27
|
+
@warnings = result_hash[:warnings]
|
28
|
+
@first_seen = @last_seen = timestamp
|
29
|
+
|
30
|
+
@test_results = Hash.new
|
31
|
+
result_hash[:version].each do |r|
|
32
|
+
if !r[:test_results].empty?
|
33
|
+
@test_results[r[:version_name]] = r[:test_results]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def pass?
|
39
|
+
@errors.empty? && @warnings.empty? && @test_results.empty?
|
40
|
+
end
|
41
|
+
|
42
|
+
def ==(obj)
|
43
|
+
res = obj.kind_of?(UnitTestResult)
|
44
|
+
res &= obj.filename == @filename
|
45
|
+
res &= obj.errors == @errors
|
46
|
+
res &= obj.warnings == @warnings
|
47
|
+
res &= obj.test_results == @test_results
|
48
|
+
res
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_rss_item(compare_timestamp)
|
52
|
+
if @last_seen < compare_timestamp
|
53
|
+
defect_status = "Fixed"
|
54
|
+
else
|
55
|
+
defect_status = "New"
|
56
|
+
end
|
57
|
+
|
58
|
+
item = RSS::Rss::Channel::Item.new
|
59
|
+
item.title = "#{@first_seen.strftime("%b %d, %H:%M")} - #{defect_status} defects in #{@filename}"
|
60
|
+
item.pubDate = @first_seen
|
61
|
+
item.description = defect_details_html
|
62
|
+
item
|
63
|
+
end
|
64
|
+
|
65
|
+
protected
|
66
|
+
|
67
|
+
def system_messages_html(messages, type)
|
68
|
+
details = ""
|
69
|
+
if !messages.empty?
|
70
|
+
details << "<h2>System #{type}s</h2>\n<pre>\n"
|
71
|
+
details << RSS::Utils.html_escape(messages.join)
|
72
|
+
details << "\n</pre>\n"
|
73
|
+
end
|
74
|
+
details
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_result_html
|
78
|
+
details = ""
|
79
|
+
@test_results.each do |ruby_ver, defects|
|
80
|
+
details << "<h2>Test Results for #{ruby_ver}</h2>\n"
|
81
|
+
defects.each do |d|
|
82
|
+
details << "<pre>\n"
|
83
|
+
details << RSS::Utils.html_escape(d[:test_info])
|
84
|
+
details << "\n</pre>\n"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
details
|
88
|
+
end
|
89
|
+
|
90
|
+
def defect_details_html
|
91
|
+
details = ""
|
92
|
+
details << test_result_html
|
93
|
+
details << system_messages_html(@errors, "Error")
|
94
|
+
details << system_messages_html(@warnings, "Warning")
|
95
|
+
details
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def create_pstore(context)
|
100
|
+
PStore.new(File.join(context.output_directory, PSTORE_FILE_NAME))
|
101
|
+
end
|
102
|
+
|
103
|
+
def update_pstore(context)
|
104
|
+
create_pstore(context).transaction do |store|
|
105
|
+
run_timestamp = Time.now
|
106
|
+
del_timestamp = run_timestamp - 12*60*60 # 12 hours
|
107
|
+
prev_test_results = store.fetch(:prev_test_results, Hash.new)
|
108
|
+
|
109
|
+
prev_test_results.delete_if do |key, res|
|
110
|
+
res.last_seen <= del_timestamp
|
111
|
+
end
|
112
|
+
|
113
|
+
@test_results.each do |cur_res_hash|
|
114
|
+
cur_res = UnitTestResult.new(cur_res_hash, run_timestamp)
|
115
|
+
next if cur_res.pass?
|
116
|
+
|
117
|
+
prev_res = prev_test_results[cur_res.filename]
|
118
|
+
if prev_res.nil? || cur_res != prev_res
|
119
|
+
# new defects
|
120
|
+
prev_test_results[cur_res.filename] = cur_res
|
121
|
+
else
|
122
|
+
# same'ol defect
|
123
|
+
prev_res.last_seen = run_timestamp
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
store[:last_run_timestamp] = run_timestamp
|
128
|
+
store[:prev_test_results] = prev_test_results
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def make_rss(context)
|
133
|
+
rss = RSS::Rss.new("2.0")
|
134
|
+
chan = RSS::Rss::Channel.new
|
135
|
+
chan.title = chan.description = "Unit Test Results for #{context.project_name}"
|
136
|
+
rss.channel = chan
|
137
|
+
|
138
|
+
create_pstore(context).transaction(true) do |store|
|
139
|
+
last_run_timestamp = store.fetch(:last_run_timestamp, 0)
|
140
|
+
test_results = store.fetch(:prev_test_results, Hash.new)
|
141
|
+
|
142
|
+
test_results.each do |key, res|
|
143
|
+
chan.items << res.to_rss_item(last_run_timestamp)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
File.open(File.join(context.output_directory, RSS_FILE_NAME), "w") {|f| f.puts(rss.to_s) }
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
module ProjectBuilderUtils
|
2
|
+
|
3
|
+
def ProjectBuilderUtils.find_ruby_files(dir)
|
4
|
+
ruby_files = Array.new
|
5
|
+
|
6
|
+
Find.find(dir) do |path|
|
7
|
+
if File.directory?(path)
|
8
|
+
# Ignore all directories starting with .
|
9
|
+
if File.basename(path)[0] == ?. || File.basename(path) =~ /vendor/
|
10
|
+
Find.prune
|
11
|
+
else
|
12
|
+
next
|
13
|
+
end
|
14
|
+
elsif File.file?(path)
|
15
|
+
case path.to_s
|
16
|
+
when /\.rb$/
|
17
|
+
ruby_files << path
|
18
|
+
when /\.rb~$/
|
19
|
+
next
|
20
|
+
when ProjectBuilderUtils.contains_ruby_execution_command?(path)
|
21
|
+
ruby_files << path
|
22
|
+
else
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
ruby_files = ruby_files.sort
|
28
|
+
end
|
29
|
+
|
30
|
+
def ProjectBuilderUtils.strip_base_dir(base_dir, file)
|
31
|
+
m = /(#{base_dir})(.*)/.match(file)
|
32
|
+
m[2][1..-1]
|
33
|
+
end
|
34
|
+
|
35
|
+
def ProjectBuilderUtils.get_ruby_and_unit_test_files(dir=Dir.pwd)
|
36
|
+
ruby_files = ProjectBuilderUtils.find_ruby_files(dir)
|
37
|
+
|
38
|
+
# remove all test case files
|
39
|
+
test_files, ruby_files = ruby_files.partition do |f|
|
40
|
+
f =~ /\/(tc|ts)_.+\.rb$|.*_test.rb$/
|
41
|
+
end
|
42
|
+
|
43
|
+
# XXX: UGLY HACK TO AVOID SIMULATION
|
44
|
+
test_files.delete_if { |f| f =~ /test\/simulation\// }
|
45
|
+
|
46
|
+
[ruby_files.uniq, test_files.sort.uniq]
|
47
|
+
end
|
48
|
+
|
49
|
+
def ProjectBuilderUtils.clean_trailing_slash(dir)
|
50
|
+
if dir[dir.size - 1] == ?/
|
51
|
+
if dir.size > 1
|
52
|
+
dir = dir[0..dir.size - 2]
|
53
|
+
else
|
54
|
+
dir = "."
|
55
|
+
end
|
56
|
+
end
|
57
|
+
dir
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
def ProjectBuilderUtils.expand_template(template, data)
|
62
|
+
str = StringIO.new
|
63
|
+
template.expand(str, data)
|
64
|
+
str.string
|
65
|
+
end
|
66
|
+
|
67
|
+
def ProjectBuilderUtils.load_actions
|
68
|
+
Dir.glob(ACTION_DIR + "/*.rb").sort.each do |action_file|
|
69
|
+
require(action_file)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def ProjectBuilderUtils.contains_ruby_execution_command?(path)
|
74
|
+
File.open(path, "r") do |f|
|
75
|
+
return (f.read(1024).to_s =~ /^#!.*ruby/)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def ProjectBuilderUtils.expand_summary_template(data, project_name, summaries)
|
81
|
+
template = TemplateFile.new("#{TEMPLATE_DIR}/build_template.html")
|
82
|
+
data[:action_summaries] = summaries
|
83
|
+
ProjectBuilderUtils.expand_template(template, data)
|
84
|
+
end
|
85
|
+
|
86
|
+
def ProjectBuilderUtils.make_summary_report(context, summaries)
|
87
|
+
if context.vcs_num
|
88
|
+
|
89
|
+
context.amrita_data[:vcs] = {
|
90
|
+
:vcs_num => context.vcs_num,
|
91
|
+
:vcs_info => context.vcs_info
|
92
|
+
}
|
93
|
+
end
|
94
|
+
context.build_complete
|
95
|
+
|
96
|
+
exp_res = ProjectBuilderUtils.expand_summary_template(context.amrita_data, context.project_name, summaries)
|
97
|
+
if context.output_directory
|
98
|
+
sum_file = "#{context.output_directory}/index.html"
|
99
|
+
File.open(sum_file, "w") do |f|
|
100
|
+
f.puts exp_res
|
101
|
+
end
|
102
|
+
else
|
103
|
+
STDOUT.puts exp_res
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Pull this into better place
|
108
|
+
def ProjectBuilderUtils.calc_score(context, action_instances)
|
109
|
+
scores = action_instances.map { |a| a.score }
|
110
|
+
scores.compact!
|
111
|
+
|
112
|
+
# If action doesn't calculate score, just default to 1 for now
|
113
|
+
if !scores.empty?
|
114
|
+
total = scores.inject(0) { |s,v| s + v }
|
115
|
+
|
116
|
+
# puts "Total : #{total} SIZE: #{scores.size}"
|
117
|
+
score = ((total / (scores.size * 10).to_f) * 10).ceil
|
118
|
+
img_num = ProjectBuilderUtils.image_for_score(score)
|
119
|
+
# puts "!!!!!!!!!!!!!!!!!! SCORE : #{score} IMG #{img_num} !!!!!!!!!!!"
|
120
|
+
else
|
121
|
+
img_num = 1
|
122
|
+
end
|
123
|
+
# Mascot Driven Development!
|
124
|
+
context.amrita_data[:mdd] = Amrita.a(:src => "mdd/#{img_num}.png")
|
125
|
+
end
|
126
|
+
|
127
|
+
def ProjectBuilderUtils.image_for_score(score)
|
128
|
+
case score
|
129
|
+
when 8..10 : 1
|
130
|
+
when 6..7 : 2
|
131
|
+
when 4..5 : 3
|
132
|
+
when 3..2 : 4
|
133
|
+
else
|
134
|
+
5
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def ProjectBuilderUtils.make_detailed_reports(context, action_instances)
|
139
|
+
if context.output_directory
|
140
|
+
action_instances.each do |action|
|
141
|
+
det_file, det_res = action.detailed_display(context)
|
142
|
+
if det_file
|
143
|
+
File.open(det_file, "w") do |f|
|
144
|
+
f.puts det_res
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
ProjectBuilderUtils.calc_score(context, action_instances)
|
151
|
+
end
|
152
|
+
|
153
|
+
# returns an array of the actions that were successfully built
|
154
|
+
def ProjectBuilderUtils.build_actions(action_instances, context)
|
155
|
+
success = Array.new
|
156
|
+
|
157
|
+
action_instances.each do |action|
|
158
|
+
begin
|
159
|
+
action.build_action(context)
|
160
|
+
success << action
|
161
|
+
rescue KwalaAbortBuildException => err
|
162
|
+
# We want to failed action, but stop the others
|
163
|
+
success << action
|
164
|
+
break
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
success
|
169
|
+
end
|
170
|
+
|
171
|
+
def ProjectBuilderUtils.build_summaries(action_instances, context)
|
172
|
+
action_instances.map do |action|
|
173
|
+
{ :summary => action.summary_display(context) }
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
|
178
|
+
end
|