build_status_server 0.6 → 0.7
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.
- data/Gemfile +4 -0
- data/Gemfile.lock +3 -1
- data/build_status_server.gemspec +11 -2
- data/config/config-example.yml +8 -8
- data/lib/build_status_server.rb +1 -0
- data/lib/build_status_server.rb~ +1 -1
- data/lib/build_status_server/config.rb +91 -0
- data/lib/build_status_server/server.rb +35 -77
- data/lib/build_status_server/version.rb +1 -1
- data/lib/build_status_server/version.rb~ +1 -1
- data/spec/lib/build_status_server/config_spec.rb +100 -0
- data/spec/lib/build_status_server/server_spec.rb +121 -0
- metadata +32 -21
- data/lib/build_status_server/requirements.rb~ +0 -8
- data/lib/build_status_server/server.rb~ +0 -189
- data/spec/lib/build_status_server_spec.rb +0 -124
- data/spec/lib/build_status_server_spec.rb~ +0 -124
- data/spec/spec_helper.rb~ +0 -15
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
build_status_server (0.
|
4
|
+
build_status_server (0.7)
|
5
5
|
json
|
6
6
|
|
7
7
|
GEM
|
@@ -15,6 +15,7 @@ GEM
|
|
15
15
|
rack (1.4.0)
|
16
16
|
rack-protection (1.2.0)
|
17
17
|
rack
|
18
|
+
rake (0.9.2.2)
|
18
19
|
rbx-require-relative (0.0.5)
|
19
20
|
rspec (2.9.0)
|
20
21
|
rspec-core (~> 2.9.0)
|
@@ -41,6 +42,7 @@ PLATFORMS
|
|
41
42
|
DEPENDENCIES
|
42
43
|
build_status_server!
|
43
44
|
json
|
45
|
+
rake
|
44
46
|
rspec
|
45
47
|
ruby-debug
|
46
48
|
sinatra
|
data/build_status_server.gemspec
CHANGED
@@ -16,11 +16,19 @@ built for my employer ChallengePost (http://challengepost.com). It works in
|
|
16
16
|
conjunction with our Jenkins Continuous Integration server (and its
|
17
17
|
Notification Plugin)) and an Arduino powered Traffic Light controller
|
18
18
|
(https://github.com/jcmuller/TrafficLightController) with a pseudo-RESTful API.
|
19
|
-
|
20
19
|
EOS
|
21
20
|
s.description = "A build notifier server for Jenkins CI that controls an XFD over HTTP"
|
22
21
|
|
23
|
-
s.files = Dir["{lib/**/*,spec/**/*}"] + %w(
|
22
|
+
s.files = Dir["{lib/**/*,spec/**/*}"] + %w(
|
23
|
+
Gemfile
|
24
|
+
Gemfile.lock
|
25
|
+
LICENSE
|
26
|
+
README.md
|
27
|
+
bin/build_status_server
|
28
|
+
build_status_server.gemspec
|
29
|
+
config/config-example.yml
|
30
|
+
)
|
31
|
+
|
24
32
|
s.require_path = "lib"
|
25
33
|
s.bindir = "bin"
|
26
34
|
s.executables = %w(build_status_server)
|
@@ -28,6 +36,7 @@ Notification Plugin)) and an Arduino powered Traffic Light controller
|
|
28
36
|
s.homepage = "http://github.com/jcmuller/build_status_server"
|
29
37
|
s.test_files = Dir["spec/**/*_spec.rb"]
|
30
38
|
|
39
|
+
s.add_development_dependency("rake")
|
31
40
|
s.add_development_dependency("ruby-debug")
|
32
41
|
s.add_development_dependency("sinatra")
|
33
42
|
|
data/config/config-example.yml
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
udp_server:
|
2
|
-
address:
|
2
|
+
address: 127.0.0.1
|
3
3
|
port: 1234
|
4
4
|
tcp_client:
|
5
|
-
host:
|
5
|
+
host: 127.0.0.1
|
6
6
|
port: 4567
|
7
|
-
pass:
|
8
|
-
fail:
|
7
|
+
pass: /green
|
8
|
+
fail: /red
|
9
9
|
store:
|
10
|
-
filename:
|
10
|
+
filename: /tmp/build_result.yml
|
11
11
|
mask:
|
12
|
-
regex:
|
13
|
-
policy:
|
14
|
-
verbose:
|
12
|
+
regex: !ruby/regexp /.*master.*/
|
13
|
+
policy: include
|
14
|
+
verbose: false
|
data/lib/build_status_server.rb
CHANGED
data/lib/build_status_server.rb~
CHANGED
@@ -0,0 +1,91 @@
|
|
1
|
+
module BuildStatusServer
|
2
|
+
class Config
|
3
|
+
attr_reader :config
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@config = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
# This is responsible of loading the config object
|
10
|
+
def load(options = {})
|
11
|
+
config = load_config_file(options[:config])
|
12
|
+
import_config(config, options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(meth, *args, &block)
|
16
|
+
return config[meth.to_s] if config.has_key?(meth.to_s)
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def store_file
|
21
|
+
return File.expand_path(".", store["filename"]) if store
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# This will load the passed in config object into the config attribute
|
28
|
+
def import_config(config = {}, options = {})
|
29
|
+
config["verbose"] = options[:verbose] unless options[:verbose].nil?
|
30
|
+
@config = config
|
31
|
+
end
|
32
|
+
|
33
|
+
# This is responsible to return a hash with the contents of a YAML file
|
34
|
+
def load_config_file(config_file = nil)
|
35
|
+
curated_file = nil
|
36
|
+
|
37
|
+
if config_file
|
38
|
+
f = File.expand_path(config_file)
|
39
|
+
if File.exists?(f)
|
40
|
+
curated_file = f
|
41
|
+
else
|
42
|
+
raise "Supplied config file (#{config_file}) doesn't seem to exist"
|
43
|
+
end
|
44
|
+
else
|
45
|
+
locations_to_try.each do |possible_conf_file|
|
46
|
+
f = File.expand_path(possible_conf_file)
|
47
|
+
if File.exists?(f)
|
48
|
+
curated_file = f
|
49
|
+
break
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
if curated_file.nil?
|
54
|
+
STDERR.puts <<-EOT
|
55
|
+
Looks like there isn't an available configuration file for this program.
|
56
|
+
We're very diligently going to use some sensible defaults, but you're
|
57
|
+
strongly recommended to create one in any of the following locations:
|
58
|
+
|
59
|
+
#{locations_to_try.join("\n ")}
|
60
|
+
|
61
|
+
Here is a sample of the contents for that file (and the settings we're going
|
62
|
+
to use):
|
63
|
+
|
64
|
+
#{get_example_config}
|
65
|
+
EOT
|
66
|
+
|
67
|
+
return YAML.load(get_example_config)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
YAML.load_file(curated_file).tap do |config|
|
72
|
+
raise "This is an invalid configuration file!" unless config.class == Hash
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def locations_to_try
|
77
|
+
%w(
|
78
|
+
~/.config/build_status_server/config.yml
|
79
|
+
./config/config.yml
|
80
|
+
/etc/build_status_server/config.yml
|
81
|
+
/usr/local/etc/build_status_server/config.yml
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
def get_example_config
|
86
|
+
filename = "#{File.dirname(File.expand_path(__FILE__))}/../../config/config-example.yml"
|
87
|
+
File.open(filename).read
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
@@ -1,28 +1,18 @@
|
|
1
|
+
# TODO
|
2
|
+
# move all configuration stuff to Config
|
3
|
+
# and just call config[:blaj] instead of if blah
|
1
4
|
module BuildStatusServer
|
2
5
|
class Server
|
3
|
-
attr_reader
|
4
|
-
attr_accessor :store, :mask
|
6
|
+
attr_reader :config, :store
|
5
7
|
|
6
8
|
def initialize(options = {})
|
7
|
-
|
8
|
-
|
9
|
-
@store_file = File.expand_path(".", config["store"]["filename"])
|
10
|
-
@mask = Regexp.new(config["mask"]["regex"])
|
11
|
-
@mask_policy = config["mask"]["policy"] || "exclude"
|
12
|
-
end
|
13
|
-
|
14
|
-
def load_store
|
15
|
-
@store = begin
|
16
|
-
YAML.load_file(store_file)
|
17
|
-
rescue
|
18
|
-
{}
|
19
|
-
end
|
20
|
-
@store = {} unless store.class == Hash
|
9
|
+
@config = Config.new
|
10
|
+
config.load(options)
|
21
11
|
end
|
22
12
|
|
23
13
|
def listen
|
24
14
|
sock = UDPSocket.new
|
25
|
-
udp_server = config
|
15
|
+
udp_server = config.udp_server
|
26
16
|
|
27
17
|
begin
|
28
18
|
sock.bind(udp_server["address"], udp_server["port"])
|
@@ -35,11 +25,11 @@ is listening at the same port (#{udp_server["address"]}:#{udp_server["port"]}
|
|
35
25
|
exit
|
36
26
|
end
|
37
27
|
|
38
|
-
puts "Listening on UDP #{udp_server["address"]}:#{udp_server["port"]}" if verbose
|
28
|
+
puts "Listening on UDP #{udp_server["address"]}:#{udp_server["port"]}" if config.verbose
|
39
29
|
|
40
30
|
while true
|
41
31
|
data, addr = sock.recvfrom(2048)
|
42
|
-
|
32
|
+
|
43
33
|
if process_job(data)
|
44
34
|
status = process_all_statuses
|
45
35
|
notify(status)
|
@@ -49,13 +39,25 @@ is listening at the same port (#{udp_server["address"]}:#{udp_server["port"]}
|
|
49
39
|
sock.close
|
50
40
|
end
|
51
41
|
|
42
|
+
private
|
43
|
+
|
44
|
+
def load_store
|
45
|
+
@store = begin
|
46
|
+
YAML.load_file(config.store_file)
|
47
|
+
rescue
|
48
|
+
{}
|
49
|
+
end
|
50
|
+
@store = {} unless store.class == Hash
|
51
|
+
end
|
52
|
+
|
53
|
+
|
52
54
|
def process_job(data = "{}")
|
53
55
|
job = JSON.parse(data)
|
54
56
|
|
55
57
|
build_name = job["name"]
|
56
58
|
|
57
59
|
unless should_process_build(build_name)
|
58
|
-
STDOUT.puts "Ignoring #{build_name} (#{mask}--#{
|
60
|
+
STDOUT.puts "Ignoring #{build_name} (#{config.mask["regex"]}--#{config.mask["policy"]})" if config.verbose
|
59
61
|
return false
|
60
62
|
end
|
61
63
|
|
@@ -65,77 +67,33 @@ is listening at the same port (#{udp_server["address"]}:#{udp_server["port"]}
|
|
65
67
|
return false
|
66
68
|
end
|
67
69
|
|
68
|
-
phase
|
69
|
-
status
|
70
|
+
phase = job["build"]["phase"]
|
71
|
+
status = job["build"]["status"]
|
70
72
|
|
71
73
|
if phase == "FINISHED"
|
72
|
-
STDOUT.puts "Got #{status} for #{build_name} on #{Time.now} [#{job.inspect}]" if verbose
|
74
|
+
STDOUT.puts "Got #{status} for #{build_name} on #{Time.now} [#{job.inspect}]" if config.verbose
|
73
75
|
case status
|
74
76
|
when "SUCCESS", "FAILURE"
|
75
77
|
load_store
|
76
78
|
store[build_name] = status
|
77
|
-
File.open(store_file, "w") { |file| YAML.dump(store, file) }
|
79
|
+
File.open(config.store_file, "w") { |file| YAML.dump(store, file) }
|
78
80
|
return true
|
79
81
|
end
|
80
82
|
else
|
81
|
-
STDOUT.puts "Started for #{build_name} on #{Time.now} [#{job.inspect}]" if verbose
|
83
|
+
STDOUT.puts "Started for #{build_name} on #{Time.now} [#{job.inspect}]" if config.verbose
|
82
84
|
end
|
83
85
|
|
84
86
|
return false
|
85
87
|
end
|
86
88
|
|
87
|
-
# Ensure config file exists. If not, copy example into it
|
88
|
-
def load_config_file(config_file)
|
89
|
-
curated_file = nil
|
90
|
-
|
91
|
-
if config_file
|
92
|
-
f = File.expand_path(config_file)
|
93
|
-
if File.exists?(f)
|
94
|
-
curated_file = f
|
95
|
-
else
|
96
|
-
puts "Supplied config file (#{config_file}) doesn't seem to exist" if verbose
|
97
|
-
exit
|
98
|
-
end
|
99
|
-
else
|
100
|
-
locations_to_try = %w(
|
101
|
-
~/.config/build_status_server/config.yml
|
102
|
-
config/config.yml
|
103
|
-
/etc/build_status_server/config.yml
|
104
|
-
/usr/local/etc/build_status_server/config.yml
|
105
|
-
)
|
106
|
-
|
107
|
-
locations_to_try.each do |possible_conf_file|
|
108
|
-
f = File.expand_path(possible_conf_file)
|
109
|
-
if File.exists?(f)
|
110
|
-
puts "Using #{possible_conf_file}!" if verbose if verbose
|
111
|
-
curated_file = f
|
112
|
-
break
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
puts <<-EOT
|
117
|
-
Looks like there isn't an available configuration file for this program. You
|
118
|
-
can create one in any of the following locations:
|
119
|
-
|
120
|
-
#{locations_to_try.map{|l| File.expand_path(l)}.join("\n ")}
|
121
|
-
|
122
|
-
Here is a sample of the contents for that file:
|
123
|
-
|
124
|
-
#{File.open("#{File.dirname(File.expand_path(__FILE__))}/../../config/config-example.yml").read}
|
125
|
-
|
126
|
-
EOT
|
127
|
-
|
128
|
-
exit
|
129
|
-
end
|
130
|
-
|
131
|
-
puts "Using #{curated_file}!" if verbose
|
132
|
-
@config = YAML.load_file(curated_file)
|
133
|
-
end
|
134
|
-
|
135
89
|
def should_process_build(build_name)
|
136
90
|
# If mask exists, then ...
|
137
|
-
! (
|
138
|
-
|
91
|
+
! (
|
92
|
+
!!config.mask &&
|
93
|
+
!!config.mask["regex"] &&
|
94
|
+
((config.mask["policy"] == "include" && build_name !~ config.mask["regex"]) ||
|
95
|
+
(config.mask["policy"] != "include" && build_name =~ config.mask["regex"])
|
96
|
+
))
|
139
97
|
end
|
140
98
|
|
141
99
|
def process_all_statuses
|
@@ -149,7 +107,7 @@ is listening at the same port (#{udp_server["address"]}:#{udp_server["port"]}
|
|
149
107
|
end
|
150
108
|
|
151
109
|
def notify(status)
|
152
|
-
tcp_client = config
|
110
|
+
tcp_client = config.tcp_client
|
153
111
|
|
154
112
|
attempts = 0
|
155
113
|
light = status ? tcp_client["pass"] : tcp_client["fail"]
|
@@ -160,7 +118,7 @@ is listening at the same port (#{udp_server["address"]}:#{udp_server["port"]}
|
|
160
118
|
client = TCPSocket.new(tcp_client["host"], tcp_client["port"])
|
161
119
|
client.print "GET #{light} HTTP/1.0\n\n"
|
162
120
|
answer = client.gets(nil)
|
163
|
-
STDOUT.puts answer if verbose
|
121
|
+
STDOUT.puts answer if config.verbose
|
164
122
|
client.close
|
165
123
|
end
|
166
124
|
rescue Timeout::Error => ex
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'build_status_server'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
describe BuildStatusServer::Config do
|
6
|
+
let(:config) { BuildStatusServer::Config.new }
|
7
|
+
|
8
|
+
describe "#load" do
|
9
|
+
it "should call load_config_file with options passed in" do
|
10
|
+
options = {:config => nil}
|
11
|
+
config.should_receive(:load_config_file).with(options[:config])
|
12
|
+
config.load(options)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should set the config values from yaml file" do
|
16
|
+
config.should_receive(:load_config_file).and_return(YAML.load(config.send(:get_example_config)))
|
17
|
+
config.load
|
18
|
+
config.udp_server.should == {'address' => '127.0.0.1', 'port' => 1234}
|
19
|
+
config.verbose.should == false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#load_config_file" do
|
24
|
+
it "should load the yaml file passed in as a file argument" do
|
25
|
+
file_name = nil
|
26
|
+
|
27
|
+
Tempfile.open('config') do |f|
|
28
|
+
f.puts "---"
|
29
|
+
f.puts "key: value"
|
30
|
+
f.puts "key2: value2"
|
31
|
+
file_name = f.path
|
32
|
+
end
|
33
|
+
|
34
|
+
config.send(:load_config_file, file_name).should == {
|
35
|
+
"key" => "value",
|
36
|
+
"key2" => "value2"
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should try to load paths from the locations to try" do
|
41
|
+
file_name = nil
|
42
|
+
Tempfile.open("config") do |f|
|
43
|
+
f.puts "---"
|
44
|
+
f.puts "key: value"
|
45
|
+
f.puts "key2: value2"
|
46
|
+
file_name = f.path
|
47
|
+
end
|
48
|
+
|
49
|
+
config.stub!(:locations_to_try).and_return([file_name])
|
50
|
+
config.send(:load_config_file).should == {
|
51
|
+
"key" => "value",
|
52
|
+
"key2" => "value2"
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should throw an exception if the config file doesn't exist" do
|
57
|
+
file_name = "/tmp/i_dont_exist.yml"
|
58
|
+
expect { config.send(:load_config_file, file_name) }.should raise_error RuntimeError, "Supplied config file (#{file_name}) doesn't seem to exist"
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should throw an exception if the config file isn't a hash" do
|
62
|
+
file_name = nil
|
63
|
+
Tempfile.open(file_name) do |f|
|
64
|
+
f.puts "YADDA YADDA"
|
65
|
+
file_name = f.path
|
66
|
+
end
|
67
|
+
expect { config.send(:load_config_file, file_name) }.should raise_error RuntimeError, "This is an invalid configuration file!"
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should return the default options if no default location is found" do
|
71
|
+
STDERR.should_receive(:puts)
|
72
|
+
config_hash = config.send(:load_config_file)
|
73
|
+
config_hash["udp_server"]["address"].should == '127.0.0.1'
|
74
|
+
config_hash["verbose"].should == false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "#store_file" do
|
79
|
+
it "returns the store file configured" do
|
80
|
+
config.stub!(:store).and_return("filename" => "/tmp/build_result.yml")
|
81
|
+
config.store_file.should == "/tmp/build_result.yml"
|
82
|
+
end
|
83
|
+
|
84
|
+
it "returns nil if config doesn't have store option" do
|
85
|
+
config.stub!(:store).and_return(nil)
|
86
|
+
config.store_file.should be_nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "#method_missing" do
|
91
|
+
it "should respond to methods named after elements in the config hash" do
|
92
|
+
config.send(:import_config, "blah" => 1)
|
93
|
+
config.blah.should == 1
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should not respond to methods named after elements that don't exist" do
|
97
|
+
expect { config.blah }.should raise_error NoMethodError
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'build_status_server'
|
3
|
+
|
4
|
+
describe BuildStatusServer::Server do
|
5
|
+
let(:server) { BuildStatusServer::Server.new }
|
6
|
+
|
7
|
+
before do
|
8
|
+
STDERR.should_receive :puts
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#listen"
|
12
|
+
|
13
|
+
context "private methods" do
|
14
|
+
describe "#load_store" do
|
15
|
+
before do
|
16
|
+
server.config.stub!(:store_file).and_return("/tmp/build")
|
17
|
+
end
|
18
|
+
|
19
|
+
it "initializes an empty hash if store file doesn't exist" do
|
20
|
+
server.send(:load_store)
|
21
|
+
server.store.should == {}
|
22
|
+
end
|
23
|
+
|
24
|
+
it "initializes an empty hash if store file is empty" do
|
25
|
+
require "tempfile"
|
26
|
+
f = Tempfile.new("server_spec")
|
27
|
+
server.stub!(:store_file).and_return(f.path)
|
28
|
+
|
29
|
+
server.send(:load_store)
|
30
|
+
server.store.should == {}
|
31
|
+
end
|
32
|
+
|
33
|
+
it "initializes a hash with the contents of the store file" do
|
34
|
+
server.config.stub!(:store_file).and_return("spec/support/build_result.yml")
|
35
|
+
server.send(:load_store)
|
36
|
+
|
37
|
+
server.store.should == {"blah" => "SUCCESS", "test" => "SUCCESS"}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#notify"
|
42
|
+
describe "#process_all_statuses"
|
43
|
+
describe "#process_job"
|
44
|
+
|
45
|
+
describe "#should_process_build" do
|
46
|
+
context "when mask exists" do
|
47
|
+
before do
|
48
|
+
server.stub!(:mask).and_return(%r{.*(?:master).*})
|
49
|
+
end
|
50
|
+
|
51
|
+
context "when policy is include" do
|
52
|
+
before do
|
53
|
+
server.stub!(:mask_policy).and_return("include")
|
54
|
+
end
|
55
|
+
|
56
|
+
it "ignores builds if mask doesn't match build name" do
|
57
|
+
server.send(:should_process_build, "blah-development").should be_false
|
58
|
+
end
|
59
|
+
|
60
|
+
it "processes builds if mask matches build name" do
|
61
|
+
server.send(:should_process_build, "blah-master").should be_true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "when policy is exclude" do
|
66
|
+
before do
|
67
|
+
server.config.stub!(:mask).and_return({"policy" => "exclude", "regex" => /.*(?:master).*/})
|
68
|
+
end
|
69
|
+
|
70
|
+
it "ignores builds if mask matches build name" do
|
71
|
+
server.send(:should_process_build, "blah-master").should be_false
|
72
|
+
end
|
73
|
+
|
74
|
+
it "processes builds if mask doesn't match build name" do
|
75
|
+
server.send(:should_process_build, "blah-development").should be_true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "when policy is undefined it defaults to ignore" do
|
80
|
+
before do
|
81
|
+
server.config.stub!(:mask).and_return({"policy" => nil, "regex" => /.*(?:master).*/})
|
82
|
+
end
|
83
|
+
|
84
|
+
it "ignores builds if mask matches build name" do
|
85
|
+
server.send(:should_process_build, "blah-master").should be_false
|
86
|
+
end
|
87
|
+
|
88
|
+
it "processes builds if mask doesn't match build name" do
|
89
|
+
server.send(:should_process_build, "blah-development").should be_true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context "when policy is unexpected it defaults to ignore" do
|
94
|
+
before do
|
95
|
+
server.config.stub!(:mask).and_return({"policy" => "trash", "regex" => /.*(?:master).*/})
|
96
|
+
end
|
97
|
+
|
98
|
+
it "ignores builds if mask matches build name" do
|
99
|
+
server.send(:should_process_build, "blah-master").should be_false
|
100
|
+
end
|
101
|
+
|
102
|
+
it "processes builds if mask doesn't match build name" do
|
103
|
+
server.send(:should_process_build, "blah-development").should be_true
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context "when mask doesn't" do
|
109
|
+
before do
|
110
|
+
server.config.stub!(:mask).and_return({"policy" => "include", "regex" => nil})
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should process all jobs" do
|
114
|
+
server.send(:should_process_build, "blah-development").should be_true
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# vim:set foldmethod=syntax foldlevel=1:
|
metadata
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: build_status_server
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 5
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: "0.
|
8
|
+
- 7
|
9
|
+
version: "0.7"
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Juan C. Muller
|
@@ -14,11 +14,10 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2012-
|
18
|
-
default_executable:
|
17
|
+
date: 2012-05-01 00:00:00 Z
|
19
18
|
dependencies:
|
20
19
|
- !ruby/object:Gem::Dependency
|
21
|
-
name:
|
20
|
+
name: rake
|
22
21
|
prerelease: false
|
23
22
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
23
|
none: false
|
@@ -32,7 +31,7 @@ dependencies:
|
|
32
31
|
type: :development
|
33
32
|
version_requirements: *id001
|
34
33
|
- !ruby/object:Gem::Dependency
|
35
|
-
name:
|
34
|
+
name: ruby-debug
|
36
35
|
prerelease: false
|
37
36
|
requirement: &id002 !ruby/object:Gem::Requirement
|
38
37
|
none: false
|
@@ -46,7 +45,7 @@ dependencies:
|
|
46
45
|
type: :development
|
47
46
|
version_requirements: *id002
|
48
47
|
- !ruby/object:Gem::Dependency
|
49
|
-
name:
|
48
|
+
name: sinatra
|
50
49
|
prerelease: false
|
51
50
|
requirement: &id003 !ruby/object:Gem::Requirement
|
52
51
|
none: false
|
@@ -57,8 +56,22 @@ dependencies:
|
|
57
56
|
segments:
|
58
57
|
- 0
|
59
58
|
version: "0"
|
60
|
-
type: :
|
59
|
+
type: :development
|
61
60
|
version_requirements: *id003
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: json
|
63
|
+
prerelease: false
|
64
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
hash: 3
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
version: "0"
|
73
|
+
type: :runtime
|
74
|
+
version_requirements: *id004
|
62
75
|
description: A build notifier server for Jenkins CI that controls an XFD over HTTP
|
63
76
|
email: jcmuller@gmail.com
|
64
77
|
executables:
|
@@ -68,27 +81,24 @@ extensions: []
|
|
68
81
|
extra_rdoc_files: []
|
69
82
|
|
70
83
|
files:
|
71
|
-
- lib/build_status_server/
|
84
|
+
- lib/build_status_server/config.rb
|
72
85
|
- lib/build_status_server/server.rb
|
73
|
-
- lib/build_status_server/server.rb~
|
74
86
|
- lib/build_status_server/version.rb
|
75
87
|
- lib/build_status_server/version.rb~
|
76
88
|
- lib/build_status_server.rb
|
77
89
|
- lib/build_status_server.rb~
|
78
|
-
- spec/lib/
|
79
|
-
- spec/lib/
|
90
|
+
- spec/lib/build_status_server/config_spec.rb
|
91
|
+
- spec/lib/build_status_server/server_spec.rb
|
80
92
|
- spec/spec_helper.rb
|
81
|
-
- spec/spec_helper.rb~
|
82
93
|
- spec/support/build_result.yml
|
83
94
|
- spec/support/sample.json
|
84
|
-
- bin/build_status_server
|
85
|
-
- config/config-example.yml
|
86
|
-
- LICENSE
|
87
|
-
- README.md
|
88
95
|
- Gemfile
|
89
96
|
- Gemfile.lock
|
97
|
+
- LICENSE
|
98
|
+
- README.md
|
99
|
+
- bin/build_status_server
|
90
100
|
- build_status_server.gemspec
|
91
|
-
|
101
|
+
- config/config-example.yml
|
92
102
|
homepage: http://github.com/jcmuller/build_status_server
|
93
103
|
licenses:
|
94
104
|
- GPL
|
@@ -118,9 +128,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
118
128
|
requirements: []
|
119
129
|
|
120
130
|
rubyforge_project:
|
121
|
-
rubygems_version: 1.
|
131
|
+
rubygems_version: 1.8.23
|
122
132
|
signing_key:
|
123
133
|
specification_version: 3
|
124
134
|
summary: This utility is part of an XFD (eXtreeme Feedback Device) solution designed and built for my employer ChallengePost (http://challengepost.com). It works in conjunction with our Jenkins Continuous Integration server (and its Notification Plugin)) and an Arduino powered Traffic Light controller (https://github.com/jcmuller/TrafficLightController) with a pseudo-RESTful API.
|
125
135
|
test_files:
|
126
|
-
- spec/lib/
|
136
|
+
- spec/lib/build_status_server/config_spec.rb
|
137
|
+
- spec/lib/build_status_server/server_spec.rb
|
@@ -1,189 +0,0 @@
|
|
1
|
-
module BuildStatusServer
|
2
|
-
class Server
|
3
|
-
attr_reader :config, :store_file, :mask_policy, :verbose
|
4
|
-
attr_accessor :store, :mask
|
5
|
-
|
6
|
-
def initialize(options = {})
|
7
|
-
load_config_file(options[:config])
|
8
|
-
@verbose = options[:verbose] || config["verbose"]
|
9
|
-
@store_file = File.expand_path(".", config["store"]["filename"])
|
10
|
-
@mask = Regexp.new(config["mask"]["regex"])
|
11
|
-
@mask_policy = config["mask"]["policy"] || "exclude"
|
12
|
-
end
|
13
|
-
|
14
|
-
def load_store
|
15
|
-
@store = begin
|
16
|
-
YAML.load_file(store_file)
|
17
|
-
rescue
|
18
|
-
{}
|
19
|
-
end
|
20
|
-
@store = {} unless store.class == Hash
|
21
|
-
end
|
22
|
-
|
23
|
-
def listen
|
24
|
-
sock = UDPSocket.new
|
25
|
-
udp_server = config["udp_server"]
|
26
|
-
sock.bind(udp_server["address"], udp_server["port"])
|
27
|
-
|
28
|
-
puts "Listening on UDP #{udp_server["address"]}:#{udp_server["port"]}" if verbose
|
29
|
-
|
30
|
-
while true
|
31
|
-
data, addr = sock.recvfrom(2048)
|
32
|
-
#require "ruby-debug"; debugger
|
33
|
-
if process_job(data)
|
34
|
-
status = process_all_statuses
|
35
|
-
notify(status)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
sock.close
|
40
|
-
end
|
41
|
-
|
42
|
-
def process_job(data = "{}")
|
43
|
-
job = JSON.parse(data)
|
44
|
-
|
45
|
-
build_name = job["name"]
|
46
|
-
|
47
|
-
unless should_process_build(build_name)
|
48
|
-
STDOUT.puts "Ignoring #{build_name} (#{mask}--#{mask_policy})" if verbose
|
49
|
-
return false
|
50
|
-
end
|
51
|
-
|
52
|
-
if job.class != Hash or
|
53
|
-
job["build"].class != Hash
|
54
|
-
STDERR.puts "Pinged with an invalid payload"
|
55
|
-
return false
|
56
|
-
end
|
57
|
-
|
58
|
-
phase = job["build"]["phase"]
|
59
|
-
status = job["build"]["status"]
|
60
|
-
|
61
|
-
if phase == "FINISHED"
|
62
|
-
STDOUT.puts "Got #{status} for #{build_name} on #{Time.now} [#{job.inspect}]" if verbose
|
63
|
-
case status
|
64
|
-
when "SUCCESS", "FAILURE"
|
65
|
-
load_store
|
66
|
-
store[build_name] = status
|
67
|
-
File.open(store_file, "w") { |file| YAML.dump(store, file) }
|
68
|
-
return true
|
69
|
-
end
|
70
|
-
else
|
71
|
-
STDOUT.puts "Started for #{build_name} on #{Time.now} [#{job.inspect}]" if verbose
|
72
|
-
end
|
73
|
-
|
74
|
-
return false
|
75
|
-
end
|
76
|
-
|
77
|
-
# Ensure config file exists. If not, copy example into it
|
78
|
-
def load_config_file(config_file)
|
79
|
-
curated_file = nil
|
80
|
-
|
81
|
-
if config_file
|
82
|
-
f = File.expand_path(config_file)
|
83
|
-
if File.exists?(f)
|
84
|
-
curated_file = f
|
85
|
-
else
|
86
|
-
puts "Supplied config file (#{config_file}) doesn't seem to exist" if verbose
|
87
|
-
exit
|
88
|
-
end
|
89
|
-
else
|
90
|
-
locations_to_try = %w(
|
91
|
-
~/.config/build_status_server/config.yml
|
92
|
-
config/config.yml
|
93
|
-
/etc/build_status_server/config.yml
|
94
|
-
/usr/local/etc/build_status_server/config.yml
|
95
|
-
)
|
96
|
-
|
97
|
-
locations_to_try.each do |possible_conf_file|
|
98
|
-
f = File.expand_path(possible_conf_file)
|
99
|
-
if File.exists?(f)
|
100
|
-
puts "Using #{possible_conf_file}!" if verbose if verbose
|
101
|
-
curated_file = f
|
102
|
-
break
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
puts <<-EOT
|
107
|
-
Looks like there isn't an available configuration file for this program. You
|
108
|
-
can create one in any of the following locations:
|
109
|
-
|
110
|
-
#{locations_to_try.map{|l| File.expand_path(l)}.join("\n ")}
|
111
|
-
|
112
|
-
Here is a sample of the contents for that file:
|
113
|
-
|
114
|
-
#{File.open("#{File.dirname(File.expand_path(__FILE__))}/../../config/config-example.yml").read}
|
115
|
-
|
116
|
-
EOT
|
117
|
-
|
118
|
-
exit
|
119
|
-
end
|
120
|
-
|
121
|
-
puts "Using #{curated_file}!" if verbose
|
122
|
-
@config = YAML.load_file(curated_file)
|
123
|
-
end
|
124
|
-
|
125
|
-
def should_process_build(build_name)
|
126
|
-
# If mask exists, then ...
|
127
|
-
! (!!mask && ((mask_policy == "include" && build_name !~ mask) ||
|
128
|
-
(mask_policy != "include" && build_name =~ mask)))
|
129
|
-
end
|
130
|
-
|
131
|
-
def process_all_statuses
|
132
|
-
pass = true
|
133
|
-
|
134
|
-
@store.values.each do |val|
|
135
|
-
pass &&= (val == "pass" || val == "SUCCESS")
|
136
|
-
end
|
137
|
-
|
138
|
-
pass
|
139
|
-
end
|
140
|
-
|
141
|
-
def notify(status)
|
142
|
-
tcp_client = config["tcp_client"]
|
143
|
-
|
144
|
-
attempts = 0
|
145
|
-
light = status ? tcp_client["pass"] : tcp_client["fail"]
|
146
|
-
|
147
|
-
begin
|
148
|
-
timeout(5) do
|
149
|
-
attempts += 1
|
150
|
-
client = TCPSocket.new(tcp_client["host"], tcp_client["port"])
|
151
|
-
client.print "GET #{light} HTTP/1.0\n\n"
|
152
|
-
answer = client.gets(nil)
|
153
|
-
STDOUT.puts answer if verbose
|
154
|
-
client.close
|
155
|
-
end
|
156
|
-
rescue Timeout::Error => ex
|
157
|
-
STDERR.puts "Error: #{ex} while trying to send #{light}"
|
158
|
-
retry unless attempts > 2
|
159
|
-
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH => ex
|
160
|
-
STDERR.puts "Error: #{ex} while trying to send #{light}"
|
161
|
-
STDERR.puts "Will wait for 2 seconds and try again..."
|
162
|
-
sleep 2
|
163
|
-
retry unless attempts > 2
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
__END__
|
170
|
-
|
171
|
-
Example payload:
|
172
|
-
{
|
173
|
-
"name":"test",
|
174
|
-
"url":"job/test/",
|
175
|
-
"build":{
|
176
|
-
"full_url":"http://cronus.local:3001/job/test/20/",
|
177
|
-
"number":20,
|
178
|
-
"phase":"FINISHED",
|
179
|
-
"status":"SUCCESS",
|
180
|
-
"url":"job/test/20/"
|
181
|
-
}
|
182
|
-
}
|
183
|
-
|
184
|
-
We're getting this error once in a while:
|
185
|
-
/usr/local/lib/ruby/1.8/timeout.rb:64:in `notify': execution expired (Timeout::Error)
|
186
|
-
from /home/jcmuller/build_notifier/lib/server.rb:102:in `notify'
|
187
|
-
from /home/jcmuller/build_notifier/lib/server.rb:33:in `listen'
|
188
|
-
from bin/server:5
|
189
|
-
|
@@ -1,124 +0,0 @@
|
|
1
|
-
$:.push File.expand_path("lib", __FILE__)
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
require 'build_status_server'
|
5
|
-
|
6
|
-
describe BuildStatusServer::Server, :pending => true do
|
7
|
-
let!(:server) { BuildStatusServer::Server.new(:config => '/dev/null') }
|
8
|
-
|
9
|
-
describe "#load_config_file" do
|
10
|
-
it "should use the supplied argument" do
|
11
|
-
config_file = '/dev/null'
|
12
|
-
File.should_receive(:exists?).and_return(true)
|
13
|
-
server.load_config_file(config_file)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
describe "#load_store" do
|
18
|
-
|
19
|
-
before do
|
20
|
-
server.stub!(:store_file).and_return("/tmp/build")
|
21
|
-
end
|
22
|
-
|
23
|
-
it "initializes an empty hash if store file doesn't exist" do
|
24
|
-
server.load_store
|
25
|
-
server.store.should == {}
|
26
|
-
end
|
27
|
-
|
28
|
-
it "initializes an empty hash if store file is empty" do
|
29
|
-
require "tempfile"
|
30
|
-
f = Tempfile.new("server_spec")
|
31
|
-
server.stub!(:store_file).and_return(f.path)
|
32
|
-
|
33
|
-
server.load_store
|
34
|
-
server.store.should == {}
|
35
|
-
end
|
36
|
-
|
37
|
-
it "initializes a hash with the contents of the store file" do
|
38
|
-
server.stub!(:store_file).and_return("spec/support/build_result.yml")
|
39
|
-
server.load_store
|
40
|
-
|
41
|
-
server.store.should == {"blah" => "SUCCESS", "test" => "SUCCESS"}
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
describe "#notify"
|
46
|
-
describe "#process_all_statuses"
|
47
|
-
describe "#process_job"
|
48
|
-
|
49
|
-
describe "#should_process_build" do
|
50
|
-
context "when mask exists" do
|
51
|
-
before do
|
52
|
-
server.stub!(:mask).and_return(/.*master.*/)
|
53
|
-
end
|
54
|
-
|
55
|
-
context "when policy is include" do
|
56
|
-
before do
|
57
|
-
server.stub!(:mask_policy).and_return("include")
|
58
|
-
end
|
59
|
-
|
60
|
-
it "ignores builds if mask doesn't match build name" do
|
61
|
-
server.should_process_build("blah-development").should be_false
|
62
|
-
end
|
63
|
-
|
64
|
-
it "processes builds if mask matches build name" do
|
65
|
-
server.should_process_build("blah-master").should be_true
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
context "when policy is exclude" do
|
70
|
-
before do
|
71
|
-
server.stub!(:mask_policy).and_return("exclude")
|
72
|
-
end
|
73
|
-
|
74
|
-
it "ignores builds if mask matches build name" do
|
75
|
-
server.should_process_build("blah-master").should be_false
|
76
|
-
end
|
77
|
-
|
78
|
-
it "processes builds if mask doesn't match build name" do
|
79
|
-
server.should_process_build("blah-development").should be_true
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
context "when policy is undefined" do
|
84
|
-
before do
|
85
|
-
server.stub!(:mask_policy).and_return(nil)
|
86
|
-
end
|
87
|
-
|
88
|
-
it "ignores builds if mask matches build name" do
|
89
|
-
server.should_process_build("blah-master").should be_false
|
90
|
-
end
|
91
|
-
|
92
|
-
it "processes builds if mask doesn't match build name" do
|
93
|
-
server.should_process_build("blah-development").should be_true
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
context "when policy is unexpected" do
|
98
|
-
before do
|
99
|
-
server.stub!(:mask_policy).and_return("trash")
|
100
|
-
end
|
101
|
-
|
102
|
-
it "ignores builds if mask matches build name" do
|
103
|
-
server.should_process_build("blah-master").should be_false
|
104
|
-
end
|
105
|
-
|
106
|
-
it "processes builds if mask doesn't match build name" do
|
107
|
-
server.should_process_build("blah-development").should be_true
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
context "when mask doesn't" do
|
113
|
-
before do
|
114
|
-
server.stub!(:mask).and_return(nil)
|
115
|
-
end
|
116
|
-
|
117
|
-
it "should process all jobs" do
|
118
|
-
server.should_process_build("blah-development").should be_true
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
# vim:set foldmethod=syntax foldlevel=1:
|
@@ -1,124 +0,0 @@
|
|
1
|
-
$:.push File.expand_path("lib", __FILE__)
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
require 'build_status_server'
|
5
|
-
|
6
|
-
describe BuildStatusServer::Server do
|
7
|
-
let!(:server) { BuildStatusServer::Server.new(:config => '/dev/null') }
|
8
|
-
|
9
|
-
describe "#load_config_file" do
|
10
|
-
it "should use the supplied argument" do
|
11
|
-
config_file = '/dev/null'
|
12
|
-
File.should_receive(:exists?).and_return(true)
|
13
|
-
server.load_config_file(config_file)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
describe "#load_store" do
|
18
|
-
|
19
|
-
before do
|
20
|
-
server.stub!(:store_file).and_return("/tmp/build")
|
21
|
-
end
|
22
|
-
|
23
|
-
it "initializes an empty hash if store file doesn't exist" do
|
24
|
-
server.load_store
|
25
|
-
server.store.should == {}
|
26
|
-
end
|
27
|
-
|
28
|
-
it "initializes an empty hash if store file is empty" do
|
29
|
-
require "tempfile"
|
30
|
-
f = Tempfile.new("server_spec")
|
31
|
-
server.stub!(:store_file).and_return(f.path)
|
32
|
-
|
33
|
-
server.load_store
|
34
|
-
server.store.should == {}
|
35
|
-
end
|
36
|
-
|
37
|
-
it "initializes a hash with the contents of the store file" do
|
38
|
-
server.stub!(:store_file).and_return("spec/support/build_result.yml")
|
39
|
-
server.load_store
|
40
|
-
|
41
|
-
server.store.should == {"blah" => "SUCCESS", "test" => "SUCCESS"}
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
describe "#notify"
|
46
|
-
describe "#process_all_statuses"
|
47
|
-
describe "#process_job"
|
48
|
-
|
49
|
-
describe "#should_process_build" do
|
50
|
-
context "when mask exists" do
|
51
|
-
before do
|
52
|
-
server.stub!(:mask).and_return(/.*master.*/)
|
53
|
-
end
|
54
|
-
|
55
|
-
context "when policy is include" do
|
56
|
-
before do
|
57
|
-
server.stub!(:mask_policy).and_return("include")
|
58
|
-
end
|
59
|
-
|
60
|
-
it "ignores builds if mask doesn't match build name" do
|
61
|
-
server.should_process_build("blah-development").should be_false
|
62
|
-
end
|
63
|
-
|
64
|
-
it "processes builds if mask matches build name" do
|
65
|
-
server.should_process_build("blah-master").should be_true
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
context "when policy is exclude" do
|
70
|
-
before do
|
71
|
-
server.stub!(:mask_policy).and_return("exclude")
|
72
|
-
end
|
73
|
-
|
74
|
-
it "ignores builds if mask matches build name" do
|
75
|
-
server.should_process_build("blah-master").should be_false
|
76
|
-
end
|
77
|
-
|
78
|
-
it "processes builds if mask doesn't match build name" do
|
79
|
-
server.should_process_build("blah-development").should be_true
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
context "when policy is undefined" do
|
84
|
-
before do
|
85
|
-
server.stub!(:mask_policy).and_return(nil)
|
86
|
-
end
|
87
|
-
|
88
|
-
it "ignores builds if mask matches build name" do
|
89
|
-
server.should_process_build("blah-master").should be_false
|
90
|
-
end
|
91
|
-
|
92
|
-
it "processes builds if mask doesn't match build name" do
|
93
|
-
server.should_process_build("blah-development").should be_true
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
context "when policy is unexpected" do
|
98
|
-
before do
|
99
|
-
server.stub!(:mask_policy).and_return("trash")
|
100
|
-
end
|
101
|
-
|
102
|
-
it "ignores builds if mask matches build name" do
|
103
|
-
server.should_process_build("blah-master").should be_false
|
104
|
-
end
|
105
|
-
|
106
|
-
it "processes builds if mask doesn't match build name" do
|
107
|
-
server.should_process_build("blah-development").should be_true
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
context "when mask doesn't" do
|
113
|
-
before do
|
114
|
-
server.stub!(:mask).and_return(nil)
|
115
|
-
end
|
116
|
-
|
117
|
-
it "should process all jobs" do
|
118
|
-
server.should_process_build("blah-development").should be_true
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
# vim:set foldmethod=syntax foldlevel=1:
|
data/spec/spec_helper.rb~
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
-
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
-
# Require this file using `require "spec_helper.rb"` to ensure that it is only
|
4
|
-
# loaded once.
|
5
|
-
#
|
6
|
-
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
-
|
8
|
-
#require "rspec/core"
|
9
|
-
#require "rspec/mocks"
|
10
|
-
#
|
11
|
-
#RSpec.configure do |config|
|
12
|
-
# config.treat_symbols_as_metadata_keys_with_true_values = true
|
13
|
-
# config.run_all_when_everything_filtered = true
|
14
|
-
# config.filter_run :focus
|
15
|
-
#end
|