buildbox 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.buildbox.toml +2 -0
- data/README.md +1 -1
- data/lib/buildbox.rb +1 -1
- data/lib/buildbox/api.rb +40 -43
- data/lib/buildbox/build.rb +35 -18
- data/lib/buildbox/client.rb +8 -14
- data/lib/buildbox/command.rb +6 -5
- data/lib/buildbox/configuration.rb +1 -1
- data/lib/buildbox/queue.rb +28 -0
- data/lib/buildbox/result.rb +30 -1
- data/lib/buildbox/utf8.rb +17 -0
- data/lib/buildbox/version.rb +1 -1
- data/spec/buildbox/buildbox/build_spec.rb +1 -1
- data/spec/buildbox/buildbox/command_spec.rb +6 -4
- data/spec/buildbox/buildbox/configuration_spec.rb +1 -1
- data/spec/integration/running_a_build_spec.rb +1 -1
- metadata +4 -3
- data/lib/buildbox/worker.rb +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb5d520609a7709bb39f3101371b189843a1529f
|
4
|
+
data.tar.gz: 17c6f844295770f5db6e0ed00b8d96d9780ebbe6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71082a07bf307f1a53d1e294f2a84ebcbb0882a9194a289a35ee13b9ca1ebc69efd04f38428e63f5498008008346725e634b124a228b10f9602beae602814772
|
7
|
+
data.tar.gz: 0e2ad6bca3ac9228d49d3e09d5e012644839281bef5d4c18980e484156ce42aa73a8194f437e088cccffa8542d5dd71e15b853b7b489ce81610dd468cdbb91b9
|
data/.buildbox.toml
ADDED
data/README.md
CHANGED
data/lib/buildbox.rb
CHANGED
data/lib/buildbox/api.rb
CHANGED
@@ -6,6 +6,7 @@ module Buildbox
|
|
6
6
|
|
7
7
|
def initialize(options)
|
8
8
|
@options = options
|
9
|
+
@queue = ::Queue.new
|
9
10
|
end
|
10
11
|
|
11
12
|
def crash(exception, information = {})
|
@@ -20,27 +21,56 @@ module Buildbox
|
|
20
21
|
payload[:meta][:build_uuid] = information[:build] if information[:build]
|
21
22
|
payload[:meta][:client_version] = Buildbox::VERSION
|
22
23
|
|
23
|
-
request(:post, "crashes",
|
24
|
+
request(:post, "crashes", payload)
|
24
25
|
end
|
25
26
|
|
26
27
|
def register(payload)
|
27
|
-
request(:post, "workers",
|
28
|
+
request(:post, "workers", payload)
|
28
29
|
end
|
29
30
|
|
30
31
|
def login
|
31
32
|
request(:get, "user")
|
32
33
|
end
|
33
34
|
|
34
|
-
def update(build, payload)
|
35
|
-
request(:put, "workers/#{worker_uuid}/builds/#{build.uuid}", :build => payload)
|
36
|
-
end
|
37
|
-
|
38
35
|
def builds(options = {})
|
39
36
|
request(:get, "workers/#{worker_uuid}/builds")
|
40
37
|
end
|
41
38
|
|
39
|
+
def update_build_state_async(*args)
|
40
|
+
async :update_build_state, args
|
41
|
+
end
|
42
|
+
|
43
|
+
def update_build_state(build_uuid, state)
|
44
|
+
request(:put, "workers/#{worker_uuid}/builds/#{build_uuid}", :state => state)
|
45
|
+
end
|
46
|
+
|
47
|
+
def update_build_result_async(*args)
|
48
|
+
async :update_build_result, args
|
49
|
+
end
|
50
|
+
|
51
|
+
def update_build_result(build_uuid, result_uuid, attributes)
|
52
|
+
request(:put, "workers/#{worker_uuid}/builds/#{build_uuid}/results/#{result_uuid}", attributes)
|
53
|
+
end
|
54
|
+
|
42
55
|
private
|
43
56
|
|
57
|
+
def async(method, args)
|
58
|
+
@queue << [ method, args ]
|
59
|
+
|
60
|
+
if !@thread || !@thread.alive?
|
61
|
+
@thread = Thread.new do
|
62
|
+
loop do
|
63
|
+
async_call = @queue.pop
|
64
|
+
Thread.exit if async_call.nil?
|
65
|
+
|
66
|
+
self.send(async_call[0], *async_call[1])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
@thread.abort_on_exception = true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
44
74
|
def http(uri)
|
45
75
|
Net::HTTP.new(uri.host, uri.port).tap do |http|
|
46
76
|
http.use_ssl = uri.scheme == "https"
|
@@ -57,15 +87,14 @@ module Buildbox
|
|
57
87
|
end
|
58
88
|
|
59
89
|
uri = URI.parse(endpoint(path))
|
60
|
-
request = klass.new(uri.request_uri
|
90
|
+
request = klass.new(uri.request_uri, 'Content-Type' => 'application/json',
|
91
|
+
'Accept' => 'application/json')
|
61
92
|
|
62
93
|
if payload.nil?
|
63
94
|
Buildbox.logger.debug "#{method.to_s.upcase} #{uri}"
|
64
95
|
else
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
Buildbox.logger.debug "#{method.to_s.upcase} #{uri} #{normalized_payload.inspect}"
|
96
|
+
request.body = JSON.dump(payload)
|
97
|
+
Buildbox.logger.debug "#{method.to_s.upcase} #{uri} #{payload.inspect}"
|
69
98
|
end
|
70
99
|
|
71
100
|
Response.new http(uri).request(request)
|
@@ -79,37 +108,5 @@ module Buildbox
|
|
79
108
|
(Buildbox.configuration.use_ssl ? "https://" : "http://") +
|
80
109
|
"#{Buildbox.configuration.endpoint}/v#{Buildbox.configuration.api_version}/#{path}"
|
81
110
|
end
|
82
|
-
|
83
|
-
# { :foo => { :bar => { :bang => "yolo" } } } => { "foo[bar][bang]" => "yolo" }
|
84
|
-
def normalize_payload(params, key=nil)
|
85
|
-
params = flatten_keys(params) if params.is_a?(Hash)
|
86
|
-
result = {}
|
87
|
-
params.each do |k,v|
|
88
|
-
case v
|
89
|
-
when Hash
|
90
|
-
result[k.to_s] = normalize_params(v)
|
91
|
-
when Array
|
92
|
-
v.each_with_index do |val,i|
|
93
|
-
result["#{k.to_s}[#{i}]"] = val.to_s
|
94
|
-
end
|
95
|
-
else
|
96
|
-
result[k.to_s] = v.to_s
|
97
|
-
end
|
98
|
-
end
|
99
|
-
result
|
100
|
-
end
|
101
|
-
|
102
|
-
def flatten_keys(hash, newhash={}, keys=nil)
|
103
|
-
hash.each do |k, v|
|
104
|
-
k = k.to_s
|
105
|
-
keys2 = keys ? keys+"[#{k}]" : k
|
106
|
-
if v.is_a?(Hash)
|
107
|
-
flatten_keys(v, newhash, keys2)
|
108
|
-
else
|
109
|
-
newhash[keys2] = v
|
110
|
-
end
|
111
|
-
end
|
112
|
-
newhash
|
113
|
-
end
|
114
111
|
end
|
115
112
|
end
|
data/lib/buildbox/build.rb
CHANGED
@@ -6,44 +6,61 @@ module Buildbox
|
|
6
6
|
@uuid = options[:uuid]
|
7
7
|
@repository = options[:repository]
|
8
8
|
@commit = options[:commit]
|
9
|
-
@
|
9
|
+
@config = options[:config]
|
10
10
|
end
|
11
11
|
|
12
12
|
def start(&block)
|
13
|
-
|
14
|
-
update
|
13
|
+
@block = block
|
15
14
|
|
16
|
-
|
17
|
-
|
15
|
+
unless build_path.exist?
|
16
|
+
setup_build_path
|
17
|
+
clone_repository
|
18
18
|
end
|
19
|
+
|
20
|
+
fetch_and_checkout
|
21
|
+
build
|
22
|
+
|
23
|
+
true
|
19
24
|
end
|
20
25
|
|
21
26
|
private
|
22
27
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
28
|
+
def setup_build_path
|
29
|
+
run %{mkdir -p "#{build_path}"}
|
30
|
+
end
|
26
31
|
|
27
|
-
|
28
|
-
|
32
|
+
def clone_repository
|
33
|
+
run %{git clone "#{@repository}" . -q}
|
29
34
|
end
|
30
35
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
34
|
-
|
36
|
+
def fetch_and_checkout
|
37
|
+
run %{git clean -fd}
|
38
|
+
run %{git fetch}
|
39
|
+
run %{git checkout -qf "#{@commit}"}
|
35
40
|
end
|
36
41
|
|
37
|
-
def
|
38
|
-
|
42
|
+
def build
|
43
|
+
@config[:build][:commands].each { |command| run command }
|
39
44
|
end
|
40
45
|
|
41
46
|
def folder_name
|
42
47
|
@repository.gsub(/[^a-zA-Z0-9]/, '-')
|
43
48
|
end
|
44
49
|
|
45
|
-
def
|
46
|
-
Buildbox
|
50
|
+
def build_path
|
51
|
+
Buildbox.root_path.join folder_name
|
52
|
+
end
|
53
|
+
|
54
|
+
def run(command)
|
55
|
+
path = build_path if build_path.exist?
|
56
|
+
|
57
|
+
result = Buildbox::Command.new(path).run(command) do |result, chunk|
|
58
|
+
@block.call(result)
|
59
|
+
end
|
60
|
+
|
61
|
+
@block.call(result)
|
62
|
+
|
63
|
+
result
|
47
64
|
end
|
48
65
|
end
|
49
66
|
end
|
data/lib/buildbox/client.rb
CHANGED
@@ -18,11 +18,14 @@ module Buildbox
|
|
18
18
|
|
19
19
|
loop do
|
20
20
|
reload_configuration
|
21
|
-
|
21
|
+
Buildbox::Queue.new.process
|
22
22
|
wait_for_interval
|
23
23
|
end
|
24
24
|
rescue => e
|
25
|
-
|
25
|
+
Buildbox.logger.error "#{e.class.name}: #{e.message}"
|
26
|
+
e.backtrace.each { |line| Buildbox.logger.error line }
|
27
|
+
|
28
|
+
api.crash(e)
|
26
29
|
ensure
|
27
30
|
pid_file.delete
|
28
31
|
end
|
@@ -51,18 +54,6 @@ module Buildbox
|
|
51
54
|
Buildbox.configuration.update :worker_uuid, response.payload[:uuid]
|
52
55
|
end
|
53
56
|
|
54
|
-
def process_build_queue
|
55
|
-
scheduled = api.builds.payload.first
|
56
|
-
|
57
|
-
if scheduled
|
58
|
-
# store build in an instance variable so we can report on it in
|
59
|
-
# the event of a crash
|
60
|
-
@build = Build.new(scheduled)
|
61
|
-
Buildbox::Worker.new(@build, api).run
|
62
|
-
@build = nil
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
57
|
def reload_configuration
|
67
58
|
Buildbox.configuration.reload
|
68
59
|
end
|
@@ -81,6 +72,9 @@ module Buildbox
|
|
81
72
|
end
|
82
73
|
end
|
83
74
|
|
75
|
+
def worker
|
76
|
+
end
|
77
|
+
|
84
78
|
def api
|
85
79
|
@api ||= Buildbox::API.new(:api_key => Buildbox.configuration.api_key)
|
86
80
|
end
|
data/lib/buildbox/command.rb
CHANGED
@@ -12,8 +12,8 @@ module Buildbox
|
|
12
12
|
def run(command)
|
13
13
|
Buildbox.logger.debug(command)
|
14
14
|
|
15
|
-
output = ""
|
16
15
|
read_io, write_io, pid = nil
|
16
|
+
result = Buildbox::Result.new(command)
|
17
17
|
|
18
18
|
begin
|
19
19
|
dir = File.expand_path(@path)
|
@@ -33,9 +33,9 @@ module Buildbox
|
|
33
33
|
begin
|
34
34
|
chunk = read_io.read_nonblock(10240)
|
35
35
|
if block_given?
|
36
|
-
yield chunk
|
36
|
+
yield result, chunk
|
37
37
|
end
|
38
|
-
output += chunk
|
38
|
+
result.output += chunk
|
39
39
|
rescue Errno::EAGAIN, Errno::EWOULDBLOCK
|
40
40
|
# do select again
|
41
41
|
rescue EOFError, Errno::EIO # EOFError from OSX, EIO is raised by ubuntu
|
@@ -49,9 +49,10 @@ module Buildbox
|
|
49
49
|
Process.waitpid(pid)
|
50
50
|
|
51
51
|
# output may be invalid UTF-8, as it is produced by the build command.
|
52
|
-
|
52
|
+
result.finished = true
|
53
|
+
result.exit_status = $?.exitstatus
|
53
54
|
|
54
|
-
|
55
|
+
result
|
55
56
|
end
|
56
57
|
|
57
58
|
def run!(command)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Buildbox
|
2
|
+
class Queue
|
3
|
+
def process
|
4
|
+
if scheduled = api.builds.payload.first
|
5
|
+
start Build.new(scheduled)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def start(build)
|
12
|
+
api.update_build_state(build.uuid, 'started')
|
13
|
+
|
14
|
+
build.start do |result|
|
15
|
+
json = result.as_json
|
16
|
+
json[:state] = json.delete(:finished) ? 'finished' : 'started'
|
17
|
+
|
18
|
+
api.update_build_result_async(build.uuid, json.delete(:uuid), json)
|
19
|
+
end
|
20
|
+
|
21
|
+
api.update_build_state_async(build.uuid, 'finished')
|
22
|
+
end
|
23
|
+
|
24
|
+
def api
|
25
|
+
@api ||= Buildbox::API.new(:api_key => Buildbox.configuration.api_key)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/buildbox/result.rb
CHANGED
@@ -1,7 +1,36 @@
|
|
1
1
|
module Buildbox
|
2
|
-
class Result
|
2
|
+
class Result
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
attr_reader :uuid, :command
|
6
|
+
attr_accessor :output, :finished, :exit_status
|
7
|
+
|
8
|
+
def initialize(command)
|
9
|
+
@uuid = SecureRandom.uuid
|
10
|
+
@output = ""
|
11
|
+
@finished = false
|
12
|
+
@command = command
|
13
|
+
end
|
14
|
+
|
15
|
+
def finished?
|
16
|
+
finished
|
17
|
+
end
|
18
|
+
|
3
19
|
def success?
|
4
20
|
exit_status == 0
|
5
21
|
end
|
22
|
+
|
23
|
+
def as_json
|
24
|
+
{ :uuid => @uuid,
|
25
|
+
:command => @command,
|
26
|
+
:output => clean_output,
|
27
|
+
:exit_status => @exit_status }
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def clean_output
|
33
|
+
Buildbox::UTF8.clean(@output).chomp
|
34
|
+
end
|
6
35
|
end
|
7
36
|
end
|
data/lib/buildbox/utf8.rb
CHANGED
@@ -46,5 +46,22 @@ module Buildbox
|
|
46
46
|
end
|
47
47
|
raise CannotFindEncoding, 'Cannot find an intermediate encoding for conversion to UTF-8'
|
48
48
|
end
|
49
|
+
|
50
|
+
def self.clean_utf8_iconv
|
51
|
+
unless @iconv_loaded
|
52
|
+
begin
|
53
|
+
require 'iconv'
|
54
|
+
rescue LoadError
|
55
|
+
@iconv = nil
|
56
|
+
else
|
57
|
+
@iconv = Iconv.new('utf-8//translit//ignore', 'utf-8')
|
58
|
+
# On some systems (Linux appears to be vulnerable, FreeBSD not)
|
59
|
+
# iconv chokes on invalid utf-8 with //translit//ignore.
|
60
|
+
@iconv_fallback = Iconv.new('utf-8//ignore', 'utf-8')
|
61
|
+
end
|
62
|
+
@iconv_loaded = true
|
63
|
+
end
|
64
|
+
[@iconv, @iconv_fallback]
|
65
|
+
end
|
49
66
|
end
|
50
67
|
end
|
data/lib/buildbox/version.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Buildbox::Build do
|
4
|
-
let(:build) { Buildbox::Build.new(:uuid => '1234', :
|
4
|
+
let(:build) { Buildbox::Build.new(:uuid => '1234', :repository => "git@github.com:keithpitt/ci-ruby.git", :commit => "67b15b704e0", :command => "rspec") }
|
5
5
|
|
6
6
|
describe "#start" do
|
7
7
|
let(:build_path) { double }
|
@@ -39,7 +39,7 @@ describe Buildbox::Command do
|
|
39
39
|
|
40
40
|
it "can collect output in chunks" do
|
41
41
|
chunked_output = ''
|
42
|
-
result = command.run('echo hello world') do |chunk|
|
42
|
+
result = command.run('echo hello world') do |result, chunk|
|
43
43
|
chunked_output += chunk
|
44
44
|
end
|
45
45
|
|
@@ -52,7 +52,7 @@ describe Buildbox::Command do
|
|
52
52
|
command = Buildbox::Command.new(nil, 0.1)
|
53
53
|
|
54
54
|
chunked_output = ''
|
55
|
-
result = command.run('sleep 0.5; echo hello world') do |chunk|
|
55
|
+
result = command.run('sleep 0.5; echo hello world') do |result, chunk|
|
56
56
|
chunked_output += chunk
|
57
57
|
end
|
58
58
|
|
@@ -61,11 +61,13 @@ describe Buildbox::Command do
|
|
61
61
|
chunked_output.should == "hello world\r\n"
|
62
62
|
end
|
63
63
|
|
64
|
+
it 'passes a result object to the block'
|
65
|
+
|
64
66
|
it "can collect chunks from within a thread" do
|
65
67
|
chunked_output = ''
|
66
68
|
result = nil
|
67
69
|
worker_thread = Thread.new do
|
68
|
-
result = command.run('echo before sleep; sleep 1; echo after sleep') do |chunk|
|
70
|
+
result = command.run('echo before sleep; sleep 1; echo after sleep') do |result, chunk|
|
69
71
|
chunked_output += chunk
|
70
72
|
end
|
71
73
|
end
|
@@ -103,7 +105,7 @@ describe Buildbox::Command do
|
|
103
105
|
|
104
106
|
it "captures color'd output" do
|
105
107
|
chunked_output = ''
|
106
|
-
result = command.run("rspec #{FIXTURES_PATH.join('rspec', 'test_spec.rb')} --color") do |chunk|
|
108
|
+
result = command.run("rspec #{FIXTURES_PATH.join('rspec', 'test_spec.rb')} --color") do |result, chunk|
|
107
109
|
chunked_output += chunk
|
108
110
|
end
|
109
111
|
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe 'running a build' do
|
4
4
|
let(:commit) { "3e0c65433b241ff2c59220f80bcdcd2ebb7e4b96" }
|
5
5
|
let(:command) { "rspec test_spec.rb" }
|
6
|
-
let(:build) { Buildbox::Build.new(:project_id => 1, :build_id => 1, :
|
6
|
+
let(:build) { Buildbox::Build.new(:project_id => 1, :build_id => 1, :repository => FIXTURES_PATH.join("repo.git").to_s, :commit => commit, :command => command) }
|
7
7
|
|
8
8
|
before do
|
9
9
|
Buildbox.stub(:root_path).and_return(TEMP_PATH)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: buildbox
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Keith Pitt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-06-
|
11
|
+
date: 2013-06-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -74,6 +74,7 @@ executables:
|
|
74
74
|
extensions: []
|
75
75
|
extra_rdoc_files: []
|
76
76
|
files:
|
77
|
+
- .buildbox.toml
|
77
78
|
- .gitignore
|
78
79
|
- .rspec
|
79
80
|
- Gemfile
|
@@ -90,11 +91,11 @@ files:
|
|
90
91
|
- lib/buildbox/command.rb
|
91
92
|
- lib/buildbox/configuration.rb
|
92
93
|
- lib/buildbox/pid_file.rb
|
94
|
+
- lib/buildbox/queue.rb
|
93
95
|
- lib/buildbox/response.rb
|
94
96
|
- lib/buildbox/result.rb
|
95
97
|
- lib/buildbox/utf8.rb
|
96
98
|
- lib/buildbox/version.rb
|
97
|
-
- lib/buildbox/worker.rb
|
98
99
|
- spec/buildbox/buildbox/build_spec.rb
|
99
100
|
- spec/buildbox/buildbox/command_spec.rb
|
100
101
|
- spec/buildbox/buildbox/configuration_spec.rb
|
data/lib/buildbox/worker.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
module Buildbox
|
2
|
-
class Worker
|
3
|
-
def initialize(build, api)
|
4
|
-
@build = build
|
5
|
-
@api = api
|
6
|
-
end
|
7
|
-
|
8
|
-
def run
|
9
|
-
update(:started_at => Time.now)
|
10
|
-
|
11
|
-
chunks = ""
|
12
|
-
result = @build.start do |chunk|
|
13
|
-
update(:output => chunks += chunk)
|
14
|
-
end
|
15
|
-
|
16
|
-
update(:exit_status => result.exit_status, :output => result.output, :finished_at => Time.now)
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def update(data)
|
22
|
-
@api.update(@build, data)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|