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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 50ef87e918e385b0169d8bc601aa29f94c6a7dbf
4
- data.tar.gz: 8aeb7524a9a4df2b8f74fb80bcff2d6669d4b380
3
+ metadata.gz: eb5d520609a7709bb39f3101371b189843a1529f
4
+ data.tar.gz: 17c6f844295770f5db6e0ed00b8d96d9780ebbe6
5
5
  SHA512:
6
- metadata.gz: 6c46f041eeb2929692d1f4a90d8a931cc10f9de14dab779b116737e4a2553d9d2f4159e36d71d332253269ee46a4649f6919113529d42fcb750e389c7b9e4de0
7
- data.tar.gz: a3903b0189b3907b5ccea550ef619cb2f2ba7a40a27a5975c18c62b395c4f82493dc1ecb7cf372c60f11db344ca95cd13a79aaba01802675f57313533d1d4da2
6
+ metadata.gz: 71082a07bf307f1a53d1e294f2a84ebcbb0882a9194a289a35ee13b9ca1ebc69efd04f38428e63f5498008008346725e634b124a228b10f9602beae602814772
7
+ data.tar.gz: 0e2ad6bca3ac9228d49d3e09d5e012644839281bef5d4c18980e484156ce42aa73a8194f437e088cccffa8542d5dd71e15b853b7b489ce81610dd468cdbb91b9
@@ -0,0 +1,2 @@
1
+ [build]
2
+ commands = [ "bundle", "rspec" ]
data/README.md CHANGED
@@ -12,7 +12,7 @@ Then authenticate
12
12
 
13
13
  Then you can start monitoring for builds like so:
14
14
 
15
- $ buildbox monitor:start
15
+ $ buildbox monitor:start --daemon
16
16
 
17
17
  For more help with the command line interface
18
18
 
@@ -5,7 +5,7 @@ require "buildbox/build"
5
5
  require "buildbox/version"
6
6
  require "buildbox/client"
7
7
  require "buildbox/api"
8
- require "buildbox/worker"
8
+ require "buildbox/queue"
9
9
  require "buildbox/pid_file"
10
10
  require "buildbox/configuration"
11
11
  require "buildbox/auth"
@@ -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", :crash => payload)
24
+ request(:post, "crashes", payload)
24
25
  end
25
26
 
26
27
  def register(payload)
27
- request(:post, "workers", :worker => payload)
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
- normalized_payload = normalize_payload(payload)
66
- request.set_form_data normalized_payload
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
@@ -6,44 +6,61 @@ module Buildbox
6
6
  @uuid = options[:uuid]
7
7
  @repository = options[:repository]
8
8
  @commit = options[:commit]
9
- @command = options[:command] || "bundle && rspec"
9
+ @config = options[:config]
10
10
  end
11
11
 
12
12
  def start(&block)
13
- checkout
14
- update
13
+ @block = block
15
14
 
16
- @result = command.run(@command) do |chunk|
17
- yield(chunk) if block_given?
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 checkout
24
- unless build_path.exist?
25
- build_path.mkpath
28
+ def setup_build_path
29
+ run %{mkdir -p "#{build_path}"}
30
+ end
26
31
 
27
- command.run! %{git clone "#{@repository}" .}
28
- end
32
+ def clone_repository
33
+ run %{git clone "#{@repository}" . -q}
29
34
  end
30
35
 
31
- def update
32
- command.run! %{git clean -fd}
33
- command.run! %{git fetch}
34
- command.run! %{git checkout -qf "#{@commit}"}
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 build_path
38
- Buildbox.root_path.join folder_name
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 command
46
- Buildbox::Command.new(build_path)
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
@@ -18,11 +18,14 @@ module Buildbox
18
18
 
19
19
  loop do
20
20
  reload_configuration
21
- process_build_queue
21
+ Buildbox::Queue.new.process
22
22
  wait_for_interval
23
23
  end
24
24
  rescue => e
25
- api.crash(e, :build => @build)
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
@@ -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
- output = Buildbox::UTF8.clean(output)
52
+ result.finished = true
53
+ result.exit_status = $?.exitstatus
53
54
 
54
- Buildbox::Result.new(output.chomp, $?.exitstatus)
55
+ result
55
56
  end
56
57
 
57
58
  def run!(command)
@@ -13,7 +13,7 @@ module Buildbox
13
13
  attr_accessor :api_version
14
14
 
15
15
  def initialize
16
- @use_ssl = false
16
+ @use_ssl = true
17
17
  @endpoint = 'api.buildbox.io'
18
18
  @api_version = 1
19
19
  end
@@ -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
@@ -1,7 +1,36 @@
1
1
  module Buildbox
2
- class Result < Struct.new(:output, :exit_status)
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Buildbox
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -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', :repo => "git@github.com:keithpitt/ci-ruby.git", :commit => "67b15b704e0", :command => "rspec") }
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
 
@@ -4,6 +4,6 @@ describe Buildbox::Configuration do
4
4
  subject(:configuration) { Buildbox::Configuration.new }
5
5
 
6
6
  it "has a default endpoint" do
7
- configuration.endpoint.should =~ /buildboxci/
7
+ configuration.endpoint.should =~ /buildbox/
8
8
  end
9
9
  end
@@ -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, :repo => FIXTURES_PATH.join("repo.git").to_s, :commit => commit, :command => command) }
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.2
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 00:00:00.000000000 Z
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
@@ -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