buildbox 0.0.2 → 0.0.3
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.
- 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
|