cijoe 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ .DS_Store
2
+ rdoc
3
+ pkg
4
+ *.tmproj
5
+ tmp/**/*
6
+ tmp/*
@@ -89,24 +89,6 @@ Multiple Projects
89
89
  Want CI for multiple projects? Just start multiple instances of Joe!
90
90
  He can run on any port - try `cijoe -h` for more options.
91
91
 
92
- Thanks to Dean Strelau you can also run multiple instances of CI Joe using
93
- a single `config.ru` file.
94
-
95
- In particular, it is possible to mount Server at a subpath:
96
-
97
- map '/foo' do
98
- run CIJoe::Server.set(:project_path => 'projects/foo')
99
- end
100
-
101
- and you can even run multiple instances of Joe if you subclass:
102
-
103
- map '/foo' do
104
- run Class.new(CIJoe::Server).set(:project_path => 'projects/foo')
105
- end
106
- map '/bar' do
107
- run Class.new(CIJoe::Server).set(:project_path => 'projects/bar')
108
- end
109
-
110
92
 
111
93
  HTTP Auth
112
94
  ---------
data/Rakefile CHANGED
@@ -16,10 +16,43 @@ begin
16
16
  gemspec.authors = ["Chris Wanstrath"]
17
17
  gemspec.add_dependency 'choice'
18
18
  gemspec.add_dependency 'sinatra'
19
- gemspec.add_dependency 'open4'
20
19
  gemspec.version = CIJoe::Version.to_s
21
20
  end
22
21
  rescue LoadError
23
22
  puts "Jeweler not available."
24
23
  puts "Install it with: gem install jeweler"
25
24
  end
25
+
26
+ require 'rake/testtask'
27
+ Rake::TestTask.new(:test) do |test|
28
+ test.libs << 'lib' << 'test'
29
+ test.pattern = 'test/**/test_*.rb'
30
+ test.verbose = true
31
+ end
32
+
33
+ begin
34
+ require 'rcov/rcovtask'
35
+ Rcov::RcovTask.new do |test|
36
+ test.libs << 'test'
37
+ test.pattern = 'test/**/test_*.rb'
38
+ test.verbose = true
39
+ end
40
+ rescue LoadError
41
+ task :rcov do
42
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
43
+ end
44
+ end
45
+
46
+ task :test => :check_dependencies
47
+
48
+ task :default => :test
49
+
50
+ require 'rake/rdoctask'
51
+ Rake::RDocTask.new do |rdoc|
52
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
53
+
54
+ rdoc.rdoc_dir = 'rdoc'
55
+ rdoc.title = "someproject #{version}"
56
+ rdoc.rdoc_files.include('README*')
57
+ rdoc.rdoc_files.include('lib/**/*.rb')
58
+ end
data/deps.rip CHANGED
@@ -2,4 +2,3 @@ git://github.com/collectiveidea/tinder.git 1.2.0
2
2
  git://github.com/sinatra/sinatra.git 0.9.4
3
3
  git://github.com/rack/rack.git 1.0
4
4
  git://github.com/defunkt/choice.git 8b125564
5
- git://github.com/ahoward/open4.git 25c3ed8
@@ -0,0 +1,15 @@
1
+ # Example CI Joe rackup config. Drop a cijoe.ru file
2
+ # in your projects direct
3
+ require 'cijoe'
4
+
5
+ # setup middleware
6
+ use Rack::CommonLogger
7
+
8
+ # configure joe
9
+ CIJoe::Server.configure do |config|
10
+ config.set :project_path, File.dirname(__FILE__)
11
+ config.set :show_exceptions, true
12
+ config.set :lock, true
13
+ end
14
+
15
+ run CIJoe::Server
@@ -0,0 +1,53 @@
1
+ #!/bin/sh
2
+ ### BEGIN INIT INFO
3
+ # Provides: cijoe
4
+ # Required-Start: $syslog $local_fs $network
5
+ # Required-Stop: $syslog $local_fs $network
6
+ # Default-Start: 2 3 4 5
7
+ # Default-Stop: 0 1
8
+ # Description: Run the CIJoe CI server. Yo Joe!!
9
+ ### END INIT INFO
10
+
11
+ . /lib/lsb/init-functions
12
+
13
+ REPO=/path/to/your/git/repository
14
+ PORT=4567
15
+
16
+ NAME=cijoe
17
+ INSTALL_DIR=/usr/sbin
18
+ DAEMON=$INSTALL_DIR/$NAME
19
+ DAEMON_ARGS="-p $PORT $REPO"
20
+ PIDFILE=/var/run/$NAME.pid
21
+ DAEMON_USER=www-data
22
+ DAEMON_GROUP=$DAEMON_USER
23
+
24
+ # test -f $DAEMON || exit 0
25
+ # test -f $PROJECT_DIR || exit 0
26
+
27
+ case "$1" in
28
+ start)
29
+ log_daemon_msg "Starting cijoe" "cijoe"
30
+ start-stop-daemon --background --make-pidfile --exec $DAEMON --start --name $NAME --pidfile $PIDFILE --chuid $DAEMON_USER:$DAEMON_GROUP -- $DAEMON_ARGS
31
+ log_end_msg $?
32
+ ;;
33
+ stop)
34
+ log_daemon_msg "Stopping cijoe" "cijoe"
35
+ start-stop-daemon --stop --pidfile $PIDFILE --quiet --retry 10
36
+ log_end_msg $?
37
+ ;;
38
+ restart)
39
+ log_daemon_msg "Restarting cijoe" "cijoe"
40
+ start-stop-daemon --stop --pidfile $PIDFILE --quiet --retry 10
41
+ start-stop-daemon --background --make-pidfile --exec $DAEMON --start --name $NAME --pidfile $PIDFILE --chuid $DAEMON_USER:$DAEMON_GROUP -- $DAEMON_ARGS
42
+ log_end_msg $?
43
+ ;;
44
+ status)
45
+ status_of_proc $DAEMON $NAME && exit 0 || exit $?
46
+ ;;
47
+ *)
48
+ log_action_msg "Usage: /etc/init.d/cijoe (start|stop|restart)"
49
+ exit 2
50
+ ;;
51
+ esac
52
+
53
+ exit 0
@@ -13,12 +13,6 @@
13
13
  #
14
14
  # Seriously, I'm gonna be nuts about keeping this simple.
15
15
 
16
- begin
17
- require 'open4'
18
- rescue LoadError
19
- abort "** Please install open4"
20
- end
21
-
22
16
  require 'cijoe/version'
23
17
  require 'cijoe/config'
24
18
  require 'cijoe/commit'
@@ -49,7 +43,7 @@ class CIJoe
49
43
 
50
44
  # the pid of the running child process
51
45
  def pid
52
- building? and @pid
46
+ building? and current_build.pid
53
47
  end
54
48
 
55
49
  # kill the child and exit
@@ -75,6 +69,8 @@ class CIJoe
75
69
  @current_build.output = output
76
70
  @last_build = @current_build
77
71
  @current_build = nil
72
+ write_build 'current', @current_build
73
+ write_build 'last', @last_build
78
74
  @last_build.notify if @last_build.respond_to? :notify
79
75
  end
80
76
 
@@ -83,21 +79,25 @@ class CIJoe
83
79
  def build
84
80
  return if building?
85
81
  @current_build = Build.new(@user, @project)
82
+ write_build 'current', @current_build
86
83
  Thread.new { build! }
87
84
  end
88
85
 
89
86
  # update git then run the build
90
87
  def build!
91
- out, err, status = '', '', nil
88
+ build = @current_build
89
+ output = ''
92
90
  git_update
93
- @current_build.sha = git_sha
91
+ build.sha = git_sha
92
+ write_build 'current', build
94
93
 
95
- status = Open4.popen4(runner_command) do |pid, stdin, stdout, stderr|
96
- @pid = pid
97
- err, out = stderr.read.strip, stdout.read.strip
94
+ IO.popen("#{runner_command} 2>&1") do |pipe|
95
+ build.pid = pid
96
+ write_build 'current', build
97
+ output = pipe.read
98
98
  end
99
99
 
100
- status.exitstatus.to_i == 0 ? build_worked(out) : build_failed(out, err)
100
+ $?.exitstatus.to_i == 0 ? build_worked(output) : build_failed('', output)
101
101
  rescue Object => e
102
102
  build_failed('', e.to_s)
103
103
  end
@@ -129,7 +129,46 @@ class CIJoe
129
129
  # massage our repo
130
130
  def run_hook(hook)
131
131
  if File.exists?(file=".git/hooks/#{hook}") && File.executable?(file)
132
- `sh #{file}`
132
+ data =
133
+ if @last_build && @last_build.commit
134
+ {
135
+ "MESSAGE" => @last_build.commit.message,
136
+ "AUTHOR" => @last_build.commit.author,
137
+ "SHA" => @last_build.commit.sha,
138
+ "OUTPUT" => @last_build.clean_output
139
+ }
140
+ else
141
+ {}
142
+ end
143
+ env = data.collect { |k, v| %(#{k}=#{v.inspect}) }.join(" ")
144
+ `#{env} sh #{file}`
145
+ end
146
+ end
147
+
148
+ # restore current / last build state from disk.
149
+ def restore
150
+ @last_build = read_build('last')
151
+ @current_build = read_build('current')
152
+ Process.kill(0, @current_build.pid) if @current_build && @current_build.pid
153
+ rescue Errno::ESRCH
154
+ # build pid isn't running anymore. assume previous
155
+ # server died and reset.
156
+ @current_build = nil
157
+ end
158
+
159
+ # write build info for build to file.
160
+ def write_build(name, build)
161
+ filename = ".git/builds/#{name}"
162
+ Dir.mkdir '.git/builds' unless File.directory?('.git/builds')
163
+ if build
164
+ build.dump filename
165
+ elsif File.exist?(filename)
166
+ File.unlink filename
133
167
  end
134
168
  end
169
+
170
+ # load build info from file.
171
+ def read_build(name)
172
+ Build.load(".git/builds/#{name}")
173
+ end
135
174
  end
@@ -1,8 +1,10 @@
1
+ require 'yaml'
2
+
1
3
  class CIJoe
2
- class Build < Struct.new(:user, :project, :started_at, :finished_at, :sha, :status, :output)
4
+ class Build < Struct.new(:user, :project, :started_at, :finished_at, :sha, :status, :output, :pid)
3
5
  def initialize(*args)
4
6
  super
5
- self.started_at = Time.now
7
+ self.started_at ||= Time.now
6
8
  end
7
9
 
8
10
  def status
@@ -27,7 +29,21 @@ class CIJoe
27
29
  end
28
30
 
29
31
  def commit
32
+ return if sha.nil?
30
33
  @commit ||= Commit.new(sha, user, project)
31
34
  end
35
+
36
+ def dump(file)
37
+ config = [user, project, started_at, finished_at, sha, status, output, pid]
38
+ data = YAML.dump(config)
39
+ File.open(file, 'wb') { |io| io.write(data) }
40
+ end
41
+
42
+ def self.load(file)
43
+ if File.exist?(file)
44
+ config = YAML.load(File.read(file))
45
+ new *config
46
+ end
47
+ end
32
48
  end
33
49
  end
@@ -5,15 +5,15 @@ class CIJoe
5
5
  end
6
6
 
7
7
  def author
8
- raw_commit_lines[1].split(':')[-1]
8
+ raw_commit_lines.grep(/Author:/).first.split(':', 2)[-1]
9
9
  end
10
10
 
11
11
  def committed_at
12
- raw_commit_lines[2].split(':', 2)[-1]
12
+ raw_commit_lines.grep(/Date:/).first.split(':', 2)[-1]
13
13
  end
14
14
 
15
15
  def message
16
- raw_commit_lines[4].split(':')[-1].strip
16
+ raw_commit.split("\n\n", 3)[1].to_s.strip
17
17
  end
18
18
 
19
19
  def raw_commit
@@ -14,11 +14,29 @@ class CIJoe
14
14
  end
15
15
 
16
16
  def to_s
17
- `git config #{config_string}`.chomp
17
+ git_command = "git config #{config_string}"
18
+ result = `#{git_command} 2>&1`.chomp
19
+ process_status = $?
20
+
21
+ if successful_command?(process_status) || config_command_with_empty_value?(result,process_status)
22
+ return result
23
+ else
24
+ raise "Error calling git config, is a recent version of git installed? Command: #{git_command}, Error: #{result}"
25
+ end
18
26
  end
19
27
 
20
28
  def config_string
21
29
  @parent ? "#{@parent.config_string}.#{@command}" : @command
22
30
  end
31
+
32
+ private
33
+
34
+ def successful_command?(process_status)
35
+ process_status.exitstatus.to_i == 0
36
+ end
37
+
38
+ def config_command_with_empty_value?(result, process_status)
39
+ process_status.exitstatus.to_i == 1 && result.empty?
40
+ end
23
41
  end
24
42
  end
@@ -8,6 +8,9 @@ class CIJoe
8
8
  set :views, "#{dir}/views"
9
9
  set :public, "#{dir}/public"
10
10
  set :static, true
11
+ set :lock, true
12
+
13
+ before { @joe.restore }
11
14
 
12
15
  get '/?' do
13
16
  erb(:template, {}, :joe => @joe)
@@ -1,3 +1,3 @@
1
1
  class CIJoe
2
- Version = "0.1.2"
2
+ Version = "0.2.0"
3
3
  end
@@ -2,6 +2,7 @@
2
2
  <html>
3
3
  <head>
4
4
  <link href="<%= cijoe_root %>/screen.css" media="screen" rel="stylesheet" type="text/css" />
5
+ <link rel="shortcut icon" href="<%= cijoe_root %>/favicon.ico" type="image/x-icon" />
5
6
  <title><%= h(joe.project) %>: CI Joe</title>
6
7
  </head>
7
8
  <body>
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ require 'cijoe'
7
+
8
+ class Test::Unit::TestCase
9
+ end
@@ -0,0 +1,17 @@
1
+ require 'helper'
2
+
3
+ class TestCIJoe < Test::Unit::TestCase
4
+ def test_raise_error_on_invalid_command
5
+ assert_raise RuntimeError, LoadError do
6
+ CIJoe::Config.new('--invalid').to_s
7
+ end
8
+ end
9
+
10
+ def test_return_value_of_config
11
+ assert_equal `git config blame`.chomp, CIJoe::Config.new('blame').to_s
12
+ end
13
+
14
+ def test_return_empty_string_when_config_does_not_exist
15
+ assert_equal '', CIJoe::Config.new('invalid').to_s
16
+ end
17
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cijoe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Wanstrath
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-25 00:00:00 -07:00
12
+ date: 2009-12-24 00:00:00 -08:00
13
13
  default_executable: cijoe
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -32,16 +32,6 @@ dependencies:
32
32
  - !ruby/object:Gem::Version
33
33
  version: "0"
34
34
  version:
35
- - !ruby/object:Gem::Dependency
36
- name: open4
37
- type: :runtime
38
- version_requirement:
39
- version_requirements: !ruby/object:Gem::Requirement
40
- requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- version: "0"
44
- version:
45
35
  description: CI Joe is a simple Continuous Integration server.
46
36
  email: chris@ozmm.org
47
37
  executables:
@@ -52,21 +42,27 @@ extra_rdoc_files:
52
42
  - LICENSE
53
43
  - README.markdown
54
44
  files:
45
+ - .gitignore
55
46
  - LICENSE
56
47
  - README.markdown
57
48
  - Rakefile
58
49
  - bin/cijoe
59
50
  - deps.rip
51
+ - examples/cijoe.ru
52
+ - examples/cijoed
60
53
  - lib/cijoe.rb
61
54
  - lib/cijoe/build.rb
62
55
  - lib/cijoe/campfire.rb
63
56
  - lib/cijoe/commit.rb
64
57
  - lib/cijoe/config.rb
58
+ - lib/cijoe/public/favicon.ico
65
59
  - lib/cijoe/public/octocat.png
66
60
  - lib/cijoe/public/screen.css
67
61
  - lib/cijoe/server.rb
68
62
  - lib/cijoe/version.rb
69
63
  - lib/cijoe/views/template.erb
64
+ - test/helper.rb
65
+ - test/test_cijoe.rb
70
66
  has_rdoc: true
71
67
  homepage: http://github.com/defunkt/cijoe
72
68
  licenses: []
@@ -95,5 +91,6 @@ rubygems_version: 1.3.5
95
91
  signing_key:
96
92
  specification_version: 3
97
93
  summary: CI Joe is a simple Continuous Integration server.
98
- test_files: []
99
-
94
+ test_files:
95
+ - test/helper.rb
96
+ - test/test_cijoe.rb