kwala 0.9.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 (67) hide show
  1. data/README +62 -0
  2. data/bin/kwala +39 -0
  3. data/lib/kwala.rb +59 -0
  4. data/lib/kwala/actions/code_change.rb +128 -0
  5. data/lib/kwala/actions/code_coverage.rb +303 -0
  6. data/lib/kwala/actions/code_duplication.rb +222 -0
  7. data/lib/kwala/actions/code_formatting.rb +358 -0
  8. data/lib/kwala/actions/cycle_detection.rb +118 -0
  9. data/lib/kwala/actions/cyclomatic_complexity.rb +159 -0
  10. data/lib/kwala/actions/dead_code.rb +68 -0
  11. data/lib/kwala/actions/executable_files_check.rb +111 -0
  12. data/lib/kwala/actions/flagged_comments.rb +128 -0
  13. data/lib/kwala/actions/gem_plugin.rb +31 -0
  14. data/lib/kwala/actions/loc_count.rb +153 -0
  15. data/lib/kwala/actions/multi_ruby_unit_test.rb +62 -0
  16. data/lib/kwala/actions/outside_links.rb +85 -0
  17. data/lib/kwala/actions/rails_migrate.rb +62 -0
  18. data/lib/kwala/actions/strange_requires.rb +106 -0
  19. data/lib/kwala/actions/syntax_check.rb +130 -0
  20. data/lib/kwala/actions/unit_test.rb +368 -0
  21. data/lib/kwala/build_action.rb +88 -0
  22. data/lib/kwala/build_context.rb +49 -0
  23. data/lib/kwala/command_line_runner.rb +184 -0
  24. data/lib/kwala/ext/demos.jar +0 -0
  25. data/lib/kwala/ext/pmd.jar +0 -0
  26. data/lib/kwala/ext/prefuse.jar +0 -0
  27. data/lib/kwala/extensions.rb +36 -0
  28. data/lib/kwala/lib/code_analyzer.rb +678 -0
  29. data/lib/kwala/lib/cycle_detector.rb +793 -0
  30. data/lib/kwala/lib/strange_requires_detector.rb +145 -0
  31. data/lib/kwala/notification.rb +45 -0
  32. data/lib/kwala/notifications/email.rb +54 -0
  33. data/lib/kwala/notifications/rss.rb +151 -0
  34. data/lib/kwala/project_builder_utils.rb +178 -0
  35. data/lib/kwala/templates/build_template.html +33 -0
  36. data/lib/kwala/templates/code_change_summary.html +27 -0
  37. data/lib/kwala/templates/code_coverage_detailed.html +25 -0
  38. data/lib/kwala/templates/code_coverage_summary.html +30 -0
  39. data/lib/kwala/templates/code_duplication_detailed.html +45 -0
  40. data/lib/kwala/templates/code_duplication_summary.html +9 -0
  41. data/lib/kwala/templates/code_formatting_detailed.html +27 -0
  42. data/lib/kwala/templates/code_formatting_summary.html +11 -0
  43. data/lib/kwala/templates/cycle_detection_detailed.html +20 -0
  44. data/lib/kwala/templates/cycle_detection_summary.html +7 -0
  45. data/lib/kwala/templates/cyclomatic_complexity_summary.html +27 -0
  46. data/lib/kwala/templates/executable_files_check_detailed.html +31 -0
  47. data/lib/kwala/templates/executable_files_check_summary.html +20 -0
  48. data/lib/kwala/templates/flagged_comments_detailed.html +22 -0
  49. data/lib/kwala/templates/flagged_comments_summary.html +10 -0
  50. data/lib/kwala/templates/loc_count_detailed.html +24 -0
  51. data/lib/kwala/templates/loc_count_summary.html +14 -0
  52. data/lib/kwala/templates/mdd/1.png +0 -0
  53. data/lib/kwala/templates/mdd/2.png +0 -0
  54. data/lib/kwala/templates/mdd/3.png +0 -0
  55. data/lib/kwala/templates/mdd/4.png +0 -0
  56. data/lib/kwala/templates/mdd/5.png +0 -0
  57. data/lib/kwala/templates/outside_links_summary.html +4 -0
  58. data/lib/kwala/templates/strange_requires_detailed.html +23 -0
  59. data/lib/kwala/templates/strange_requires_summary.html +13 -0
  60. data/lib/kwala/templates/style.css +95 -0
  61. data/lib/kwala/templates/syntax_check_detailed.html +21 -0
  62. data/lib/kwala/templates/syntax_check_summary.html +7 -0
  63. data/lib/kwala/templates/unit_test_detailed.html +66 -0
  64. data/lib/kwala/templates/unit_test_summary.html +70 -0
  65. data/test/tc_build_action.rb +32 -0
  66. data/test/tc_templates.rb +24 -0
  67. metadata +118 -0
@@ -0,0 +1,118 @@
1
+ # $Id: cycle_detection.rb 32 2008-04-02 07:50:18Z zev $
2
+ #
3
+ # Implements a Ruby require cycle detection and graphing action.
4
+
5
+ # Copyright (c) 2006, Ubiquitous Business Technology (http://ubit.com)
6
+ # All rights reserved.
7
+ #
8
+ # Redistribution and use in source and binary forms, with or without
9
+ # modification, are permitted provided that the following conditions are
10
+ # met:
11
+ #
12
+ #
13
+ # * Redistributions of source code must retain the above copyright
14
+ # notice, this list of conditions and the following disclaimer.
15
+ #
16
+ # * Redistributions in binary form must reproduce the above
17
+ # copyright notice, this list of conditions and the following
18
+ # disclaimer in the documentation and/or other materials provided
19
+ # with the distribution.
20
+ #
21
+ # * Neither the name of Ubit nor the names of its
22
+ # contributors may be used to endorse or promote products derived
23
+ # from this software without specific prior written permission.
24
+ #
25
+ #
26
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37
+ #
38
+ # == Author
39
+ # Zev Blut (zb@ubit.com)
40
+
41
+ require 'stringio'
42
+
43
+ require "#{LIB_DIR}/cycle_detector"
44
+
45
+ class CycleDetectionAction < BuildAction
46
+
47
+ def initialize
48
+ @cycle_detector = CycleDetector.new
49
+ @cycle_detector.print_strategy = self
50
+ @output_directory = nil
51
+ @cycles = nil
52
+ end
53
+
54
+ def build_action(context)
55
+ # convert obj?
56
+ @cycle_detector.make_graph(context.project_directory)
57
+ end
58
+
59
+
60
+ def summary_display(context)
61
+ template = TemplateFile.new(self.class.summary_template_file)
62
+ @cycle_detector.print_unique_cycles( @cycle_detector.find_cycles )
63
+
64
+ if @cycles.empty?
65
+ context.amrita_data[:cycle_results] = "There are no require cycles."
66
+ else
67
+ data = @cycles.map do |cycle|
68
+ {
69
+ :name => "#{ cycle.first} <=> #{ cycle.last }",
70
+ :list => cycle
71
+ }
72
+ end
73
+ context.amrita_data[:cycle_results] = { :entry => data }
74
+ end
75
+
76
+
77
+ loc_base = "#{context.project_name}_cycle.html"
78
+ context.amrita_data[:cycle_details] =
79
+ Amrita.e(:a, :href => loc_base ) { "Require Graph Details" }
80
+
81
+ summary_expand(template, context)
82
+ end
83
+
84
+ def detailed_display(context)
85
+ template = TemplateFile.new(self.class.detailed_template_file)
86
+
87
+ @output_directory = context.output_directory
88
+ @cycle_detector.print_graph
89
+
90
+ ["demos.jar", "prefuse.jar"].each do |jarfile|
91
+ FileUtils.cp(EXTERNAL_DIR + jarfile, context.output_directory)
92
+ end
93
+
94
+ det_res = ProjectBuilderUtils.expand_template(template, context.amrita_data)
95
+ det_file = "#{context.output_directory}/#{context.project_name}_cycle.html"
96
+ [det_file, det_res]
97
+ end
98
+
99
+
100
+ def print_graph(graph)
101
+ graph_ml = CycleDetector::GraphMLPrintStrategy.new
102
+ begin
103
+ oldstd = $stdout
104
+ $stdout = StringIO.new
105
+ graph_ml.print_graph(graph)
106
+ gxml = $stdout.string
107
+ ensure
108
+ $stdout = oldstd
109
+ end
110
+ # now dump that into the out dir
111
+ File.open(@output_directory + "/require_graph.xml", "w") { |f| f.puts gxml }
112
+ end
113
+
114
+ def print_cycles(cycles)
115
+ @cycles = cycles
116
+ end
117
+
118
+ end
@@ -0,0 +1,159 @@
1
+ # $Id: cyclomatic_complexity.rb 34 2008-04-03 06:01:19Z zev $
2
+ #
3
+ # This file checks the cyclomatic complexity of a source base.
4
+ #
5
+ # Uses Saikuro http://saikuro.rubyforge.org
6
+
7
+ # Copyright (c) 2006, Ubiquitous Business Technology (http://ubit.com)
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are
12
+ # met:
13
+ #
14
+ #
15
+ # * Redistributions of source code must retain the above copyright
16
+ # notice, this list of conditions and the following disclaimer.
17
+ #
18
+ # * Redistributions in binary form must reproduce the above
19
+ # copyright notice, this list of conditions and the following
20
+ # disclaimer in the documentation and/or other materials provided
21
+ # with the distribution.
22
+ #
23
+ # * Neither the name of Ubit nor the names of its
24
+ # contributors may be used to endorse or promote products derived
25
+ # from this software without specific prior written permission.
26
+ #
27
+ #
28
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
+ #
40
+ # == Author
41
+ # Zev Blut (zb@ubit.com)
42
+
43
+ class CyclomaticComplexityAction < BuildAction
44
+
45
+ def initialize
46
+ @limit = 11 # 1 more for header
47
+ @cyclo_count = nil
48
+ @bound = 0 # This should be methods instead of files.
49
+ end
50
+
51
+ def build_action(context)
52
+ args = [ "-o #{context.output_directory}" ]
53
+ args << [ "-c -t"]
54
+ args << [ "-y 0 -w 11 -e 15"]
55
+ args << [ "-k 0 -s 30 -d 35"]
56
+ args << [ "-i #{context.project_directory}" ]
57
+
58
+ stdout_data = stderr_data = Array.new
59
+ system("/usr/bin/env saikuro #{args.join(" ")}")
60
+ @cyclo_count = get_cyclo_violations(context)
61
+ @bound = context.ruby_files.size + context.test_files.size
62
+ end
63
+
64
+ def summary_display(context)
65
+ template = TemplateFile.new(self.class.summary_template_file)
66
+ context.amrita_data[:saikuro_metrics_summary] = {
67
+ :cyclo_summary => get_cyclo_summary(context),
68
+ :token_summary => get_token_summary(context),
69
+ :cyclo_violations => get_cyclo_violations(context),
70
+ :token_violations => get_token_violations(context),
71
+ :cyclo_link => Amrita::e(:a, :href => "index_cyclo.html") { "Cyclomatic Complexity Details" },
72
+ :token_link => Amrita::e(:a, :href => "index_token.html") { "Token Count Details" }
73
+ }
74
+ summary_expand(template, context)
75
+ end
76
+
77
+ def score
78
+ # high complexity is probably one of the things that should weigh higher.
79
+ score_scaling(( @cyclo_count[:errors] * 3 + @cyclo_count[:warnings]), @bound)
80
+ end
81
+
82
+ def self.detailed_template_file
83
+ nil
84
+ end
85
+
86
+ def get_cyclo_summary(context)
87
+ get_summary_from_file(context.output_directory + "/index_cyclo.html")
88
+ end
89
+
90
+ def get_token_summary(context)
91
+ get_summary_from_file(context.output_directory + "/index_token.html")
92
+ end
93
+
94
+ def get_cyclo_violations(context)
95
+ count_violations(context.output_directory + "/index_cyclo.html")
96
+ end
97
+
98
+ def get_token_violations(context)
99
+ count_violations(context.output_directory + "/index_token.html")
100
+ end
101
+
102
+ def get_summary_from_file(file)
103
+ if !File.exists?(file)
104
+ STDERR.puts "Saikuro : Cannot find #{file}"
105
+ return ""
106
+ end
107
+ data = IO.readlines(file).join
108
+ res = ""
109
+ if m = /(<table.*?>)(.*)(<\/table>)/mi.match(data)
110
+ head = m[1]
111
+ data = m[2]
112
+ tail = m[3]
113
+ rows = Array.new
114
+ while rows.size < @limit && m = /(<tr>.*?<\/tr>)(.*)/mi.match(data)
115
+ rows << m[1]
116
+ data = m[2]
117
+ end
118
+
119
+ res = head.to_s + rows.join("\n") + tail.to_s
120
+ end
121
+
122
+ SanitizedString.new(res)
123
+ end
124
+
125
+
126
+ # XXX: This is basically the same as get_summary_from_file, there
127
+ # should be a nice way to reuse the logic in a more clean way.
128
+ def count_violations(file)
129
+ if !File.exists?(file)
130
+ return 0
131
+ end
132
+
133
+ data = IO.readlines(file).join
134
+ errors = 0
135
+ warnings = 0
136
+
137
+ if m = /(<table.*?>)(.*)(<\/table>)/mi.match(data)
138
+ data = m[2]
139
+ while m = /(<tr>.*?<\/tr>)(.*)/mi.match(data)
140
+ if /^<tr><th>/ !~ m[1]
141
+ # Make sure not to count the header row.
142
+ if /class=\"error\"/ =~ m[1]
143
+ errors += 1
144
+ elsif /class=\"warning\"/ =~ m[1]
145
+ warnings += 1
146
+ end
147
+ end
148
+ data = m[2]
149
+ end
150
+ end
151
+
152
+ {
153
+ :total => errors + warnings,
154
+ :errors => errors,
155
+ :warnings => warnings
156
+ }
157
+ end
158
+
159
+ end
@@ -0,0 +1,68 @@
1
+ # $Id$
2
+ #
3
+ # This file handles logic for dead code checking
4
+
5
+ # All rights reserved.
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions are
9
+ # met:
10
+ #
11
+ #
12
+ # * Redistributions of source code must retain the above copyright
13
+ # notice, this list of conditions and the following disclaimer.
14
+ #
15
+ # * Redistributions in binary form must reproduce the above
16
+ # copyright notice, this list of conditions and the following
17
+ # disclaimer in the documentation and/or other materials provided
18
+ # with the distribution.
19
+ #
20
+ # * Neither the name of Ubit nor the names of its
21
+ # contributors may be used to endorse or promote products derived
22
+ # from this software without specific prior written permission.
23
+ #
24
+ #
25
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
+ #
37
+ # == Author
38
+ # Zev Blut ()
39
+
40
+ class DeadCodeAction < BuildAction
41
+
42
+ def initialize
43
+ end
44
+
45
+ def build_action(context)
46
+ end
47
+
48
+ def summary_display(context)
49
+ template = TemplateFile.new("#{TEMPLATE_DIR}/dead_code_template.html")
50
+ summary_expand(template, context)
51
+ end
52
+
53
+ def detailed_display(context)
54
+ context.amrita_data[:syntax_results] = {
55
+ :entry => @syntax_results
56
+ }
57
+ det_res = expand_syntax_check_template(context.amrita_data[:syntax_results])
58
+ det_file = "#{context.output_directory}/#{context.project_name}_dead_code.html"
59
+ [det_file, det_res]
60
+ end
61
+
62
+ def score
63
+ 10
64
+ end
65
+
66
+
67
+
68
+ end
@@ -0,0 +1,111 @@
1
+ # $Id: executable_files_check.rb 29 2008-04-02 07:32:44Z zev $
2
+ #
3
+ # This action checks for the code size within ruby scripts starting with
4
+ # a shell bang. By our convention, we want to keep those files as small
5
+ # as possible, all logic should be in reusable classes.
6
+
7
+ # Copyright (c) 2006, Ubiquitous Business Technology (http://ubit.com)
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are
12
+ # met:
13
+ #
14
+ #
15
+ # * Redistributions of source code must retain the above copyright
16
+ # notice, this list of conditions and the following disclaimer.
17
+ #
18
+ # * Redistributions in binary form must reproduce the above
19
+ # copyright notice, this list of conditions and the following
20
+ # disclaimer in the documentation and/or other materials provided
21
+ # with the distribution.
22
+ #
23
+ # * Neither the name of Ubit nor the names of its
24
+ # contributors may be used to endorse or promote products derived
25
+ # from this software without specific prior written permission.
26
+ #
27
+ #
28
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
+ #
40
+ # == Author
41
+ # Michael Reinsch (mr@ubit.com)
42
+
43
+ require File.dirname(__FILE__) + "/loc_count.rb"
44
+
45
+ class ExecutableFilesCheckAction < BuildAction
46
+ include LOCCountModule
47
+
48
+ def initialize
49
+ @problem_files = Array.new
50
+ @shellbang_files_LOC = Hash.new
51
+ end
52
+
53
+ def build_action(context)
54
+ context.ruby_files.each do |f|
55
+ exec = File.executable?(f)
56
+ bang = ("#!/" == IO.read(f, 3, 0))
57
+ if exec ^ bang
58
+ if exec
59
+ prob = "executable, no shell bang"
60
+ else
61
+ prob = "shell bang, not executable"
62
+ end
63
+ @problem_files << { :filename => f, :problem => prob }
64
+ end
65
+ @shellbang_files_LOC[f] = count_loc(f) if bang
66
+ end
67
+ @sorted_shellbang_files_LOC = @shellbang_files_LOC.sort {|a,b| b[1] <=> a[1] }
68
+ end
69
+
70
+ def summary_display(context)
71
+ template = TemplateFile.new(self.class.summary_template_file)
72
+
73
+ res = {}
74
+ if @problem_files.size > 0
75
+ res[:problem_files] = { :files => @problem_files }
76
+ end
77
+
78
+ # get top 10
79
+ top_ten_files = @sorted_shellbang_files_LOC[0..9]
80
+ res[:files] = top_ten_files.map do |f, l|
81
+ { :filename => f, :lines_of_code => l }
82
+ end
83
+
84
+ context.amrita_data[:executable_files_check_results] = res
85
+ execfilecodesize_base = "#{context.project_name}_executable_files_check.html"
86
+ context.amrita_data[:executable_files_check_details] =
87
+ (Amrita::e(:a, :href => execfilecodesize_base) { "Executable Files Check Details" } )
88
+
89
+ summary_expand(template, context)
90
+ end
91
+
92
+ def detailed_display(context)
93
+ template = TemplateFile.new(self.class.detailed_template_file)
94
+
95
+ res = {}
96
+ if @problem_files.size > 0
97
+ res[:problem_files] = { :files => @problem_files }
98
+ end
99
+
100
+ res[:files] = @sorted_shellbang_files_LOC.map do |f, l|
101
+ { :filename => f, :lines_of_code => l }
102
+ end
103
+
104
+ context.amrita_data[:executable_files_check_results] = res
105
+
106
+ det_res = ProjectBuilderUtils.expand_template(template, context.amrita_data)
107
+ det_file = "#{context.output_directory}/#{context.project_name}_executable_files_check.html"
108
+ [det_file, det_res]
109
+ end
110
+
111
+ end