cijoe 0.1.2 → 0.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.
@@ -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