cuke_ci_workers 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/cuke_ci_install +75 -0
- data/bin/cuke_ci_runner +7 -0
- data/lib/cuke_ci_workers/notifier.rb +80 -0
- data/lib/cuke_ci_workers/runner.rb +167 -0
- data/lib/cuke_ci_workers/worker.rb +47 -0
- data/templates/post-commit +11 -0
- data/templates/post-commit.erb +10 -0
- data/templates/post-receive +9 -0
- data/templates/runner_config.yml.erb +10 -0
- metadata +80 -0
data/bin/cuke_ci_install
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'erb'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
def file_from_template(name, dest, project_id, api_key)
|
7
|
+
File.open(dest, 'w') do |f|
|
8
|
+
f.puts(render_template(name, project_id, api_key))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def render_template(name, project_id, api_key)
|
13
|
+
file_name = File.expand_path("../../templates/#{name}.erb", __FILE__)
|
14
|
+
erb = ERB.new(File.read(file_name))
|
15
|
+
erb.result(binding)
|
16
|
+
end
|
17
|
+
|
18
|
+
options = {}
|
19
|
+
options[:hook] = nil
|
20
|
+
options[:runner] = false
|
21
|
+
|
22
|
+
opts = OptionParser.new do |opts|
|
23
|
+
opts.banner = "Usage: [options] PROJECT_ID API_KEY"
|
24
|
+
|
25
|
+
opts.on "--runner-config", "Install runner config" do
|
26
|
+
options[:runner_config] = true
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on "--post-commit", "Install post commit hook" do
|
30
|
+
options[:hook] = 'post-commit'
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on "--post-receive", "Install post receive hook" do
|
34
|
+
options[:hook] = 'post-receive'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
begin
|
39
|
+
opts.parse!
|
40
|
+
rescue
|
41
|
+
puts opts.help
|
42
|
+
exit
|
43
|
+
end
|
44
|
+
|
45
|
+
if ARGV.length != 2
|
46
|
+
puts opts.help
|
47
|
+
else
|
48
|
+
api_key = ARGV.pop
|
49
|
+
project_id = ARGV.pop
|
50
|
+
|
51
|
+
if options[:hook]
|
52
|
+
performed = false
|
53
|
+
|
54
|
+
%w(.git/hooks hooks).each do |dir|
|
55
|
+
hook_file_name = File.join(dir, options[:hook])
|
56
|
+
|
57
|
+
if File.exists?(dir)
|
58
|
+
file_from_template(options[:hook], hook_file_name, project_id, api_key)
|
59
|
+
File.chmod(0770, hook_file_name)
|
60
|
+
|
61
|
+
puts "Hook installed in '#{hook_file_name}'."
|
62
|
+
performed = true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
unless performed
|
67
|
+
puts 'Not in a git direcotry. Hook not installed.'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
if options[:runner_config]
|
72
|
+
file_from_template('runner_config.yml', 'cuke_ci_runner_config.yml', project_id, api_key)
|
73
|
+
puts 'Runner config installed.'
|
74
|
+
end
|
75
|
+
end
|
data/bin/cuke_ci_runner
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'net/http'
|
4
|
+
require 'uri'
|
5
|
+
require 'json'
|
6
|
+
require 'grit'
|
7
|
+
|
8
|
+
require 'cuke_ci_workers/worker'
|
9
|
+
|
10
|
+
module CukeCIWorkers
|
11
|
+
class Notifier < Worker
|
12
|
+
def initialize(server_url, log_file_name, log_level = nil)
|
13
|
+
super(log_file_name, log_level)
|
14
|
+
@server_uri = URI.parse(server_url)
|
15
|
+
end
|
16
|
+
|
17
|
+
def post_commit
|
18
|
+
json = generate_json_for_head
|
19
|
+
push_json(json)
|
20
|
+
rescue Exception => e
|
21
|
+
log_exception(e)
|
22
|
+
end
|
23
|
+
|
24
|
+
def post_reveive(before_rev, after_rev, ref)
|
25
|
+
json = generate_json(before_rev, after_rev, ref)
|
26
|
+
push_json(json)
|
27
|
+
rescue Exception => e
|
28
|
+
log_exception(e)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def push_json(json)
|
34
|
+
Net::HTTP.start(@server_uri.host, @server_uri.port) do |http|
|
35
|
+
http.post("#{@server_uri.path}?#{@server_uri.query}", json, 'Content-Type' => 'application/json')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def generate_json_for_head
|
40
|
+
generate_json('HEAD^', 'HEAD', repository.head.name)
|
41
|
+
end
|
42
|
+
|
43
|
+
def generate_json(before_rev, after_rev, ref)
|
44
|
+
before = repository.commit(before_rev)
|
45
|
+
after = repository.commit(after_rev)
|
46
|
+
|
47
|
+
{:payload => {
|
48
|
+
:before => before.sha,
|
49
|
+
:after => after.sha,
|
50
|
+
:ref => ref,
|
51
|
+
:commits => repository.commits_between(before, after).collect do |commit|
|
52
|
+
{
|
53
|
+
:id => commit.sha,
|
54
|
+
:message => commit.message,
|
55
|
+
:timestamp => commit.committed_date.xmlschema,
|
56
|
+
:url => '',
|
57
|
+
:added => [],
|
58
|
+
:removed => [],
|
59
|
+
:modified => [],
|
60
|
+
:author => {
|
61
|
+
:name => commit.author.name,
|
62
|
+
:email => commit.author.email
|
63
|
+
}
|
64
|
+
}
|
65
|
+
end,
|
66
|
+
}
|
67
|
+
}.to_json
|
68
|
+
end
|
69
|
+
|
70
|
+
def repository
|
71
|
+
Grit::Repo.new(".")
|
72
|
+
end
|
73
|
+
|
74
|
+
class << self
|
75
|
+
def parse!(args)
|
76
|
+
Notifier.new(*args)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'net/http'
|
4
|
+
require 'uri'
|
5
|
+
require 'json'
|
6
|
+
require 'optparse'
|
7
|
+
require 'fileutils'
|
8
|
+
require 'yaml'
|
9
|
+
require 'logger'
|
10
|
+
|
11
|
+
require 'cuke_ci_workers/worker'
|
12
|
+
|
13
|
+
module CukeCIWorkers
|
14
|
+
class Runner < Worker
|
15
|
+
def initialize(options)
|
16
|
+
@configuration_file_name = options[:configuration_file_name]
|
17
|
+
@configuration = YAML.load_file(@configuration_file_name)
|
18
|
+
|
19
|
+
@poll_uri = URI.parse(@configuration['poll_url'])
|
20
|
+
@push_url = @configuration['push_url']
|
21
|
+
|
22
|
+
@verbose = options[:verbose]
|
23
|
+
|
24
|
+
super(@configuration['log_file_name'], @configuration['log_level'])
|
25
|
+
end
|
26
|
+
|
27
|
+
def run
|
28
|
+
$running = true
|
29
|
+
Signal.trap('INT') { $running = false }
|
30
|
+
|
31
|
+
while($running) do
|
32
|
+
begin
|
33
|
+
run_present = poll_for_run
|
34
|
+
#rescue Exception => e
|
35
|
+
# log_exception(e)
|
36
|
+
end
|
37
|
+
|
38
|
+
if run_present
|
39
|
+
begin
|
40
|
+
pull_repo
|
41
|
+
checkout_commit
|
42
|
+
prepare
|
43
|
+
|
44
|
+
if result = cucumber
|
45
|
+
push_result(result)
|
46
|
+
else
|
47
|
+
push_error
|
48
|
+
end
|
49
|
+
#rescue Exception => e
|
50
|
+
# log_exception(e)
|
51
|
+
end
|
52
|
+
else
|
53
|
+
sleep 5
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def poll_for_run
|
61
|
+
message('Polling for run')
|
62
|
+
result = Net::HTTP.get(@poll_uri)
|
63
|
+
|
64
|
+
begin
|
65
|
+
hash = JSON.parse(result)
|
66
|
+
rescue JSON::ParserError
|
67
|
+
hash = {}
|
68
|
+
end
|
69
|
+
|
70
|
+
@commit_hash = hash['commit_hash']
|
71
|
+
@run_id = hash['id']
|
72
|
+
|
73
|
+
@commit_hash
|
74
|
+
end
|
75
|
+
|
76
|
+
def pull_repo
|
77
|
+
message("Pulling repository")
|
78
|
+
run_command(:pull)
|
79
|
+
end
|
80
|
+
|
81
|
+
def checkout_commit
|
82
|
+
message("Checking out #{@commit_hash}")
|
83
|
+
run_command(:check_out, @commit_hash)
|
84
|
+
end
|
85
|
+
|
86
|
+
def prepare
|
87
|
+
message('Preparing working directory')
|
88
|
+
return
|
89
|
+
run_command(:prepare)
|
90
|
+
end
|
91
|
+
|
92
|
+
def cucumber
|
93
|
+
message('Running Cucumber')
|
94
|
+
output = run_command(:cucumber, temp_file_name)
|
95
|
+
|
96
|
+
if $?.success?
|
97
|
+
File.read(temp_file_name)
|
98
|
+
else
|
99
|
+
logger.error("Cucumber failed: #{output}")
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def push_result(result)
|
105
|
+
message('Pushing results')
|
106
|
+
logger.info "Pushing result: #{result}"
|
107
|
+
|
108
|
+
json = "{'payload': #{result}}"
|
109
|
+
|
110
|
+
push_json(json)
|
111
|
+
end
|
112
|
+
|
113
|
+
def push_error
|
114
|
+
message('Pushing error')
|
115
|
+
json = "{'error': 'Cucumber did not complete successfully.'}"
|
116
|
+
logger.info "Pushing error: #{json}"
|
117
|
+
|
118
|
+
push_json(json)
|
119
|
+
end
|
120
|
+
|
121
|
+
def push_json(json)
|
122
|
+
url = @push_url % @run_id
|
123
|
+
@push_uri = URI.parse(url)
|
124
|
+
|
125
|
+
message "Pushing json to #{url}"
|
126
|
+
|
127
|
+
Net::HTTP.start(@push_uri.host, @push_uri.port) do |http|
|
128
|
+
http.post("#{@push_uri.path}?#{@push_uri.query}", json, 'Content-Type' => 'application/json')
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def temp_file_name
|
133
|
+
@configuration['temp_file_name']
|
134
|
+
end
|
135
|
+
|
136
|
+
class << self
|
137
|
+
def parse!(args)
|
138
|
+
default_runner_config_file_name = './cuke_ci_runner_config.yml'
|
139
|
+
options = {}
|
140
|
+
|
141
|
+
opts = OptionParser.new do |opts|
|
142
|
+
opts.banner = 'Usage: ...'
|
143
|
+
|
144
|
+
opts.on("-c", "--configuration CONFIGURATION_FILE_NAME", "Path to configuration file. Defaults to '#{default_runner_config_file_name}'.") do |file_name|
|
145
|
+
options[:configuration_file_name] = file_name
|
146
|
+
end
|
147
|
+
|
148
|
+
opts.on("-v", "--verbose", "Be verbose") do |v|
|
149
|
+
options[:verbose] = true
|
150
|
+
end
|
151
|
+
|
152
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
153
|
+
puts opts
|
154
|
+
exit
|
155
|
+
end
|
156
|
+
end
|
157
|
+
opts.parse!
|
158
|
+
|
159
|
+
options[:configuration_file_name] ||= default_runner_config_file_name
|
160
|
+
self.new(options)
|
161
|
+
rescue OptionParser::ParseError
|
162
|
+
puts opts
|
163
|
+
exit
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'net/http'
|
3
|
+
require 'uri'
|
4
|
+
require 'json'
|
5
|
+
require 'yaml'
|
6
|
+
require 'logger'
|
7
|
+
|
8
|
+
module CukeCIWorkers
|
9
|
+
class Worker
|
10
|
+
attr_accessor :logger
|
11
|
+
|
12
|
+
def initialize(log_file_name, log_level = nil)
|
13
|
+
@logger = Logger.new(log_file_name)
|
14
|
+
@logger.level = Logger.const_get(log_level) rescue Logger::INFO
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def log_exception(e)
|
20
|
+
logger.error(e.message + "\n " + e.backtrace * "\n ")
|
21
|
+
end
|
22
|
+
|
23
|
+
def message(text)
|
24
|
+
logger.info text
|
25
|
+
if @verbose
|
26
|
+
puts("========= #{text} =========")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def run_command(key, *args)
|
31
|
+
command = interpolate_command(key, args)
|
32
|
+
logger.info " #{command}"
|
33
|
+
|
34
|
+
if @verbose
|
35
|
+
puts command
|
36
|
+
`#{command}`
|
37
|
+
else
|
38
|
+
`#{command} 2> /dev/null`
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def interpolate_command(key, args)
|
43
|
+
command = @configuration['commands'][key.to_s]
|
44
|
+
command % args
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
server_url = 'http://localhost:3000/projects/1/receipt?api_key=3d177538439c92c8d278208ff1ae098f'
|
4
|
+
log_file_name = 'cuke_ci_notifier.log'
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'cuke_ci_workers/notifier'
|
8
|
+
# require '/home/tim/code/rr10/rr10-team-179/cucumber-ci-workers/lib/cuke_ci_workers/notifier'
|
9
|
+
|
10
|
+
CukeCIWorkers::Notifier.new(server_url, log_file_name).post_commit
|
11
|
+
|
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
server_url = 'http://localhost:3000/projects/<%= project_id %>/receipt?api_key=<%= api_key %>'
|
4
|
+
log_file_name = 'cuke_ci_notifier.log'
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'cuke_ci_workers/notifier'
|
8
|
+
|
9
|
+
CukeCIWorkers::Notifier.new(server_url, log_file_name).post_commit
|
10
|
+
|
@@ -0,0 +1,10 @@
|
|
1
|
+
temp_file_name: "cucumber.out"
|
2
|
+
log_file_name: "cuke_ci_runner.log"
|
3
|
+
log_level: "WARN"
|
4
|
+
poll_url: "http://localhost:3000/projects/<%= project_id %>/runs/next?api_key=<%= api_key %>"
|
5
|
+
push_url: "http://localhost:3000/projects/<%= project_id %>/runs/%s/results?api_key=<%= api_key %>"
|
6
|
+
commands:
|
7
|
+
pull: "git pull origin master"
|
8
|
+
check_out: "git reset --hard %s"
|
9
|
+
prepare: "rake db:reset"
|
10
|
+
cucumber: "cucumber --format json --out %s"
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cuke_ci_workers
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 9
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: "0.1"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Gregor Weckbecker
|
13
|
+
- Vangelis Tsoumenis
|
14
|
+
- Tim Fischbach
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2010-10-17 00:00:00 +02:00
|
20
|
+
default_executable:
|
21
|
+
dependencies: []
|
22
|
+
|
23
|
+
description: Cucumber based continuous integration.
|
24
|
+
email:
|
25
|
+
- info@timfischbach.de
|
26
|
+
executables:
|
27
|
+
- cuke_ci_runner
|
28
|
+
- cuke_ci_install
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- bin/cuke_ci_runner
|
35
|
+
- bin/cuke_ci_install
|
36
|
+
- lib/cuke_ci_workers/worker.rb
|
37
|
+
- lib/cuke_ci_workers/runner.rb
|
38
|
+
- lib/cuke_ci_workers/notifier.rb
|
39
|
+
- templates/post-commit
|
40
|
+
- templates/runner_config.yml.erb
|
41
|
+
- templates/post-commit.erb
|
42
|
+
- templates/post-receive
|
43
|
+
has_rdoc: true
|
44
|
+
homepage: http://whiteboard.r10.railsrumble.com/
|
45
|
+
licenses: []
|
46
|
+
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
hash: 23
|
67
|
+
segments:
|
68
|
+
- 1
|
69
|
+
- 3
|
70
|
+
- 6
|
71
|
+
version: 1.3.6
|
72
|
+
requirements: []
|
73
|
+
|
74
|
+
rubyforge_project:
|
75
|
+
rubygems_version: 1.3.7
|
76
|
+
signing_key:
|
77
|
+
specification_version: 3
|
78
|
+
summary: Runner and notifier tool for CukeCI.
|
79
|
+
test_files: []
|
80
|
+
|