crazy_ivan 1.1.1 → 1.2.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.
@@ -1,11 +1,12 @@
1
1
  class TestRunner
2
- def initialize(project_path)
2
+ def initialize(project_path, report_assembler)
3
3
  @project_path = project_path
4
4
  @results = {:project_name => File.basename(@project_path),
5
5
  :version => {:output => '', :error => '', :exit_status => ''},
6
6
  :update => {:output => '', :error => '', :exit_status => ''},
7
7
  :test => {:output => '', :error => '', :exit_status => ''},
8
8
  :timestamp => {:start => nil, :finish => nil}}
9
+ @report_assembler = report_assembler
9
10
  end
10
11
 
11
12
  attr_reader :results
@@ -31,26 +32,74 @@ class TestRunner
31
32
  Dir.chdir(@project_path) do
32
33
  if File.exists?(script_path)
33
34
  if !File.stat(script_path).executable?
34
- abort "#{@project_path}/.ci/#{name} script not executable"
35
+ msg = "#{@project_path}/.ci/#{name} script not executable"
36
+ Syslog.warning msg
37
+ abort msg
35
38
  elsif File.open(script_path).read.empty?
36
- abort "#{@project_path}/.ci/#{name} script empty"
39
+ msg = "#{@project_path}/.ci/#{name} script empty"
40
+ Syslog.warning msg
41
+ abort msg
37
42
  end
38
43
  else
39
- abort "#{@project_path}/.ci/#{name} script missing"
44
+ msg = "#{@project_path}/.ci/#{name} script missing"
45
+ Syslog.warning msg
46
+ abort msg
40
47
  end
41
48
  end
42
49
  end
43
50
 
44
- def run_script(name)
51
+ def run_script(name, options = {})
45
52
  output = ''
46
53
  error = ''
47
54
  exit_status = ''
48
55
 
49
56
  Dir.chdir(@project_path) do
57
+ Syslog.debug "Opening up the pipe to #{script_path(name)}"
58
+
50
59
  status = Open4::popen4(script_path(name)) do |pid, stdin, stdout, stderr|
51
60
  stdin.close # Close to prevent hanging if the script wants input
52
- output = stdout.read
53
- error = stderr.read
61
+
62
+ until stdout.eof? && stderr.eof? do
63
+ ready_io_streams = select( [stdout], nil, [stderr], 3600 )
64
+
65
+ script_output = ready_io_streams[0].pop
66
+ script_error = ready_io_streams[2].pop
67
+
68
+ if script_output && !script_output.eof?
69
+ o = script_output.readpartial(4096)
70
+ print o
71
+ output << o
72
+
73
+ if options[:stream_test_results?]
74
+ @results[:test][:output] = output
75
+ @report_assembler.update_project(self)
76
+ end
77
+ end
78
+
79
+ if script_error && !script_error.eof?
80
+ e = script_error.readpartial(4096)
81
+ print e
82
+ error << e
83
+
84
+ if options[:stream_test_results?]
85
+ @results[:test][:error] = error
86
+ @report_assembler.update_project(self)
87
+ end
88
+ end
89
+
90
+ # FIXME - this feels like I'm using IO.select wrong
91
+ if script_output.eof? && script_error.nil?
92
+ # there's no more output to SDOUT, and there aren't any errors
93
+ e = stderr.read
94
+ error << e
95
+ print e
96
+
97
+ if options[:stream_test_results?]
98
+ @results[:test][:error] = error
99
+ @report_assembler.update_project(self)
100
+ end
101
+ end
102
+ end
54
103
  end
55
104
 
56
105
  exit_status = status.exitstatus
@@ -60,7 +109,6 @@ class TestRunner
60
109
  end
61
110
 
62
111
  def run_conclusion_script
63
-
64
112
  # REFACTOR do this asynchronously so the next tests don't wait on running the conclusion
65
113
 
66
114
  Dir.chdir(@project_path) do
@@ -103,7 +151,7 @@ class TestRunner
103
151
  def test!
104
152
  if @results[:version][:exit_status] == '0'
105
153
  Syslog.debug "Testing #{@results[:project_name]} build #{@results[:version][:output]}"
106
- @results[:test][:output], @results[:test][:error], @results[:test][:exit_status] = run_script('test')
154
+ @results[:test][:output], @results[:test][:error], @results[:test][:exit_status] = run_script('test', :stream_test_results? => true)
107
155
  else
108
156
  Syslog.debug "Failed to test #{project_name}; version exit status was #{@results[:version][:exit_status]}"
109
157
  end
@@ -2,5 +2,5 @@ begin
2
2
  require 'open4'
3
3
  rescue LoadError
4
4
  $LOAD_PATH.unshift(File.dirname(__FILE__) + '/open4-1.0.1/lib')
5
- require 'open4.rb'
5
+ require 'open4'
6
6
  end
@@ -1,3 +1,3 @@
1
1
  module CrazyIvan
2
- VERSION = '1.1.1'
2
+ VERSION = '1.2.0'
3
3
  end
data/lib/crazy_ivan.rb CHANGED
@@ -1,29 +1,28 @@
1
1
  require 'syslog'
2
2
  require 'fileutils'
3
+ require 'yaml'
4
+ require 'crazy_ivan/process_manager'
3
5
  require 'crazy_ivan/report_assembler'
4
6
  require 'crazy_ivan/test_runner'
5
- require 'crazy_ivan/html_asset_crush'
6
7
  require 'crazy_ivan/version'
7
8
  require 'crazy_ivan/vendor/json'
8
9
  require 'crazy_ivan/vendor/open4'
9
10
  require 'crazy_ivan/vendor/tmpdir'
10
11
 
11
12
  module CrazyIvan
12
- # VERSION = '1.0.0'
13
-
14
13
  def self.setup
15
14
  puts
16
15
  puts "Preparing per-project continuous integration configuration scripts"
17
16
  puts
18
17
 
19
- Dir['*'].each do |dir|
18
+ Dir['*/'].each do |dir|
20
19
  Dir.chdir(dir) do
21
20
  FileUtils.mkdir_p('.ci')
22
21
 
23
22
  Dir.chdir('.ci') do
24
- puts " #{dir}/.ci"
23
+ puts " #{dir}.ci"
25
24
  if File.exists?('version')
26
- puts " #{' ' * (dir + "/.ci").size}/version already exists - skipping"
25
+ puts " #{' ' * (dir + "/.ci").size}/version already exists - skipping"
27
26
  else
28
27
  File.open('version', 'w+') do |f|
29
28
  f.puts "#!/usr/bin/env ruby"
@@ -34,11 +33,11 @@ module CrazyIvan
34
33
  f.puts
35
34
  f.puts "puts `git show`[/^commit (.+)$/, 1]"
36
35
  end
37
- puts " #{' ' * (dir + "/.ci").size}/version -- created"
36
+ puts " #{' ' * (dir + ".ci").size}/version -- created"
38
37
  end
39
38
 
40
39
  if File.exists?('update')
41
- puts " #{' ' * (dir + "/.ci").size}/update already exists - skipping"
40
+ puts " #{' ' * (dir + "/.ci").size}/update already exists - skipping"
42
41
  else
43
42
  File.open('update', 'w+') do |f|
44
43
  f.puts "#!/usr/bin/env bash"
@@ -50,11 +49,11 @@ module CrazyIvan
50
49
  f.puts
51
50
  f.puts "git pull"
52
51
  end
53
- puts " #{' ' * (dir + "/.ci").size}/update -- created"
52
+ puts " #{' ' * (dir + ".ci").size}/update -- created"
54
53
  end
55
54
 
56
55
  if File.exists?('test')
57
- puts " #{' ' * (dir + "/.ci").size}/test already exists -- skipping"
56
+ puts " #{' ' * (dir + ".ci").size}/test already exists -- skipping"
58
57
  else
59
58
  File.open('test', 'w+') do |f|
60
59
  f.puts "#!/usr/bin/env bash"
@@ -64,11 +63,11 @@ module CrazyIvan
64
63
  f.puts
65
64
  f.puts "rake"
66
65
  end
67
- puts " #{' ' * (dir + "/.ci").size}/test -- created"
66
+ puts " #{' ' * (dir + ".ci").size}/test -- created"
68
67
  end
69
68
 
70
69
  if File.exists?('conclusion')
71
- puts " #{' ' * (dir + "/.ci").size}/conclusion already exists -- skipping"
70
+ puts " #{' ' * (dir + ".ci").size}/conclusion already exists -- skipping"
72
71
  else
73
72
  File.open('conclusion', 'w+') do |f|
74
73
  f.puts "#!/usr/bin/env ruby"
@@ -78,14 +77,14 @@ module CrazyIvan
78
77
  f.puts "# If you're interested in bouncing the message to campfire, "
79
78
  f.puts "# emailing, or otherwise sending notifications, this is the place to do it."
80
79
  f.puts
81
- f.puts "# To enable campfire notifications, uncomment the next lines:"
80
+ f.puts "# To enable campfire notifications, uncomment the next lines and make sure you have the httparty gem installed:"
82
81
  f.puts "# CAMPFIRE_ROOM_URL = 'http://your-company.campfirenow.com/room/265250'"
83
82
  f.puts "# CAMPFIRE_API_KEY = '23b8al234gkj80a3e372133l4k4j34275f80ef8971'"
84
83
  f.puts "# CRAZY_IVAN_REPORTS_URL = 'http://ci.your-projects.com'"
85
84
  f.puts "# IO.popen(\"test_report2campfire \#{CAMPFIRE_ROOM_URL} \#{CAMPFIRE_API_KEY} \#{CRAZY_IVAN_REPORTS_URL}\", 'w') {|f| f.puts STDIN.read }"
86
85
  f.puts
87
86
  end
88
- puts " #{' ' * (dir + "/.ci").size}/conclusion -- created"
87
+ puts " #{' ' * (dir + ".ci").size}/conclusion -- created"
89
88
  end
90
89
  puts
91
90
 
@@ -100,7 +99,10 @@ module CrazyIvan
100
99
  puts
101
100
  puts " crazy_ivan /path/to/directory/your/reports/go"
102
101
  puts
103
- puts "then look at index.html in that path to confirm that everything is ok."
102
+ puts "then look at index.html in that path to confirm that everything is ok. "
103
+ puts
104
+ puts "To force a re-run of the same build version of a project, delete its test "
105
+ puts "results directory from the /path/to/directory/your/reports/go"
104
106
  puts
105
107
  puts "If things look good, then set up a cron job or other script to run"
106
108
  puts "crazy_ivan on a periodic basis."
@@ -119,6 +121,7 @@ module CrazyIvan
119
121
  report = ReportAssembler.new(Dir.pwd, output_directory)
120
122
  report.generate
121
123
 
124
+ # TODO this should really indicate how many projects were tested
122
125
  msg = "Generated test reports for #{report.runners.size} projects"
123
126
  Syslog.info(msg)
124
127
  puts msg
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: crazy_ivan
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Edward Ocampo-Gooding
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-12 00:00:00 -05:00
12
+ date: 2010-02-01 00:00:00 -05:00
13
13
  default_executable: crazy_ivan
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -52,19 +52,10 @@ extra_rdoc_files: []
52
52
  files:
53
53
  - bin/crazy_ivan
54
54
  - bin/test_report2campfire
55
- - lib/crazy_ivan/html_asset_crush.rb
55
+ - lib/crazy_ivan/process_manager.rb
56
56
  - lib/crazy_ivan/report_assembler.rb
57
- - lib/crazy_ivan/templates/css/ci.css
58
57
  - lib/crazy_ivan/templates/index.html
59
- - lib/crazy_ivan/templates/javascript/builder.js
60
- - lib/crazy_ivan/templates/javascript/controls.js
61
58
  - lib/crazy_ivan/templates/javascript/date.js
62
- - lib/crazy_ivan/templates/javascript/dragdrop.js
63
- - lib/crazy_ivan/templates/javascript/effects.js
64
- - lib/crazy_ivan/templates/javascript/json-template.js
65
- - lib/crazy_ivan/templates/javascript/prototype.js
66
- - lib/crazy_ivan/templates/javascript/scriptaculous.js
67
- - lib/crazy_ivan/templates/javascript/slider.js
68
59
  - lib/crazy_ivan/test_runner.rb
69
60
  - lib/crazy_ivan/vendor/core_ext/tmpdir.rb
70
61
  - lib/crazy_ivan/vendor/json-1.1.7/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkComparison.log
@@ -1,56 +0,0 @@
1
- # Parses an html file's <head> section,
2
- # looks for <script ... > and <link rel="stylesheet" ... >
3
- # and crunches them all together into one file
4
- #
5
- # Warning: this script is super-ghetto and probably not robust.
6
- # If you're concerned, use a real lexer/parser.
7
-
8
- require 'strscan'
9
-
10
- module HtmlAssetCrush
11
- def self.source_for(asset_path)
12
- case asset_path
13
- when /js$/
14
- <<-JS
15
- <script type="text/javascript" charset="utf-8">
16
- #{File.open(asset_path).read}
17
- </script>
18
- JS
19
- when /css$/
20
- <<-CSS
21
- <style type="text/css">
22
- #{File.open(asset_path).read}
23
- </style>
24
- CSS
25
- end
26
- rescue Errno::ENOENT
27
- raise "Could not find #{asset_path} to bring in"
28
- end
29
-
30
- def self.crush(html_filepath)
31
- Dir.chdir(File.dirname(html_filepath)) do
32
- html = File.open(html_filepath).read
33
- crushed_html = ""
34
-
35
- s = StringScanner.new(html)
36
-
37
- js = /<script.+? src=['"](.+)['"].+?\/script>/
38
- css = /<link .+? href=['"](.+?)['"].+?>/
39
- asset = Regexp.union(js, css)
40
-
41
- while result = s.scan_until(asset) do
42
- asset_path = s[2] || s[1]
43
-
44
- # Weird that pre_match doesn't do this
45
- # crushed_html << s.pre_match
46
- crushed_html << result[0...(-s.matched_size)]
47
-
48
- crushed_html << source_for(asset_path) + "\n"
49
- end
50
-
51
- crushed_html << s.rest
52
-
53
- return crushed_html
54
- end
55
- end
56
- end
@@ -1,34 +0,0 @@
1
- body {
2
- margin: 2.5em 3em;
3
- padding: 0;
4
- background: #fff;
5
- color: #333;
6
- font: 100%/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
7
- }
8
-
9
- h1 { margin: 0;}
10
-
11
- pre { margin: 0;}
12
-
13
- .error {
14
- color: red;
15
- }
16
-
17
- .project h2 { margin: 12px 0 0 0;}
18
-
19
- .tests { margin: 0 0 0 18px;}
20
- .tests .test { font-size: 80%; margin-right: 8px;}
21
- .tests .latest { font-size: 100%;}
22
-
23
- .results .result .timestamp { margin-right: 12px;}
24
- .results .result .version { margin: 6px 0 6px 12px }
25
- .results .result .output { padding: 5px; color: silver; background:black; margin: 12px 18px 8px 18px;}
26
- .results .result .output .update { margin: 6px 0 6px 12px }
27
- .results .result .output .test { margin: 6px 0 6px 12px }
28
-
29
- .footer {
30
- margin: 24px 0 0 0;
31
- font-size: 60%;
32
- width: 100%;
33
- text-align: center;
34
- }
@@ -1,136 +0,0 @@
1
- // script.aculo.us builder.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009
2
-
3
- // Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
4
- //
5
- // script.aculo.us is freely distributable under the terms of an MIT-style license.
6
- // For details, see the script.aculo.us web site: http://script.aculo.us/
7
-
8
- var Builder = {
9
- NODEMAP: {
10
- AREA: 'map',
11
- CAPTION: 'table',
12
- COL: 'table',
13
- COLGROUP: 'table',
14
- LEGEND: 'fieldset',
15
- OPTGROUP: 'select',
16
- OPTION: 'select',
17
- PARAM: 'object',
18
- TBODY: 'table',
19
- TD: 'table',
20
- TFOOT: 'table',
21
- TH: 'table',
22
- THEAD: 'table',
23
- TR: 'table'
24
- },
25
- // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
26
- // due to a Firefox bug
27
- node: function(elementName) {
28
- elementName = elementName.toUpperCase();
29
-
30
- // try innerHTML approach
31
- var parentTag = this.NODEMAP[elementName] || 'div';
32
- var parentElement = document.createElement(parentTag);
33
- try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
34
- parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
35
- } catch(e) {}
36
- var element = parentElement.firstChild || null;
37
-
38
- // see if browser added wrapping tags
39
- if(element && (element.tagName.toUpperCase() != elementName))
40
- element = element.getElementsByTagName(elementName)[0];
41
-
42
- // fallback to createElement approach
43
- if(!element) element = document.createElement(elementName);
44
-
45
- // abort if nothing could be created
46
- if(!element) return;
47
-
48
- // attributes (or text)
49
- if(arguments[1])
50
- if(this._isStringOrNumber(arguments[1]) ||
51
- (arguments[1] instanceof Array) ||
52
- arguments[1].tagName) {
53
- this._children(element, arguments[1]);
54
- } else {
55
- var attrs = this._attributes(arguments[1]);
56
- if(attrs.length) {
57
- try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
58
- parentElement.innerHTML = "<" +elementName + " " +
59
- attrs + "></" + elementName + ">";
60
- } catch(e) {}
61
- element = parentElement.firstChild || null;
62
- // workaround firefox 1.0.X bug
63
- if(!element) {
64
- element = document.createElement(elementName);
65
- for(attr in arguments[1])
66
- element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
67
- }
68
- if(element.tagName.toUpperCase() != elementName)
69
- element = parentElement.getElementsByTagName(elementName)[0];
70
- }
71
- }
72
-
73
- // text, or array of children
74
- if(arguments[2])
75
- this._children(element, arguments[2]);
76
-
77
- return $(element);
78
- },
79
- _text: function(text) {
80
- return document.createTextNode(text);
81
- },
82
-
83
- ATTR_MAP: {
84
- 'className': 'class',
85
- 'htmlFor': 'for'
86
- },
87
-
88
- _attributes: function(attributes) {
89
- var attrs = [];
90
- for(attribute in attributes)
91
- attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
92
- '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'&quot;') + '"');
93
- return attrs.join(" ");
94
- },
95
- _children: function(element, children) {
96
- if(children.tagName) {
97
- element.appendChild(children);
98
- return;
99
- }
100
- if(typeof children=='object') { // array can hold nodes and text
101
- children.flatten().each( function(e) {
102
- if(typeof e=='object')
103
- element.appendChild(e);
104
- else
105
- if(Builder._isStringOrNumber(e))
106
- element.appendChild(Builder._text(e));
107
- });
108
- } else
109
- if(Builder._isStringOrNumber(children))
110
- element.appendChild(Builder._text(children));
111
- },
112
- _isStringOrNumber: function(param) {
113
- return(typeof param=='string' || typeof param=='number');
114
- },
115
- build: function(html) {
116
- var element = this.node('div');
117
- $(element).update(html.strip());
118
- return element.down();
119
- },
120
- dump: function(scope) {
121
- if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope
122
-
123
- var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
124
- "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
125
- "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
126
- "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
127
- "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
128
- "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
129
-
130
- tags.each( function(tag){
131
- scope[tag] = function() {
132
- return Builder.node.apply(Builder, [tag].concat($A(arguments)));
133
- };
134
- });
135
- }
136
- };