buildbox 0.0.1 → 0.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9695125aaee3d822bac58ccacfe3acb3e6af8a9c
4
- data.tar.gz: c03eddbac8c47d1022f0efb5dd33e0f4c1b5afd3
3
+ metadata.gz: 50ef87e918e385b0169d8bc601aa29f94c6a7dbf
4
+ data.tar.gz: 8aeb7524a9a4df2b8f74fb80bcff2d6669d4b380
5
5
  SHA512:
6
- metadata.gz: e957e054f341e19ef76811b343de8954fb860709f7afd7723e010c03d87cbc42d6ae5df09de42b5a1ff44143f38f82afc299d255a5e047a0726d07ac91078d8a
7
- data.tar.gz: 52ab5893fe9cc9405a11807e16fc7d36b7e0bc5f94fe2b0a3f4bc5fd6a01e75c2e879434b906ad9ed4d9d2908c9ac5c9b6f77898e3ddf7f7e9c69dfd878b4f19
6
+ metadata.gz: 6c46f041eeb2929692d1f4a90d8a931cc10f9de14dab779b116737e4a2553d9d2f4159e36d71d332253269ee46a4649f6919113529d42fcb750e389c7b9e4de0
7
+ data.tar.gz: a3903b0189b3907b5ccea550ef619cb2f2ba7a40a27a5975c18c62b395c4f82493dc1ecb7cf372c60f11db344ca95cd13a79aaba01802675f57313533d1d4da2
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --profile
3
+ -Ilib
data/README.md CHANGED
@@ -1,24 +1,22 @@
1
- # Ci::Ruby
2
-
3
- TODO: Write a gem description
1
+ # Buildbox
4
2
 
5
3
  ## Installation
6
4
 
7
- Add this line to your application's Gemfile:
5
+ Install the gem
8
6
 
9
- gem 'ci-ruby'
7
+ $ gem install buildbox
10
8
 
11
- And then execute:
9
+ Then authenticate
12
10
 
13
- $ bundle
11
+ $ buildbox auth:login [apikey]
14
12
 
15
- Or install it yourself as:
13
+ Then you can start monitoring for builds like so:
16
14
 
17
- $ gem install ci-ruby
15
+ $ buildbox monitor:start
18
16
 
19
- ## Usage
17
+ For more help with the command line interface
20
18
 
21
- TODO: Write usage instructions here
19
+ $ buildbox --help
22
20
 
23
21
  ## Contributing
24
22
 
@@ -2,30 +2,98 @@
2
2
 
3
3
  dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
4
4
  $LOAD_PATH.unshift(dir) unless $LOAD_PATH.include?(dir)
5
- require 'trigger'
5
+ require 'buildbox'
6
6
  require 'optparse'
7
7
 
8
- COMMANDS = %w(start stop)
8
+ commands = {}
9
+ options = {}
9
10
 
10
- options = {}.tap do |options|
11
- OptionParser.new do |opts|
12
- opts.version = Trigger::VERSION
13
- opts.banner = 'Usage: trigger command [options]'
11
+ help = <<HELP
14
12
 
15
- opts.separator ""
16
- opts.separator "Options:"
13
+ auth # authentication (login, logout)
14
+ monitor # monitor builds (start, stop)
15
+ version # display version
17
16
 
18
- opts.on("-d", "--daemon", "Runs trigger as a daemon") do |user_agent|
19
- options[:daemon] = true
20
- end
17
+ HELP
18
+
19
+ global = OptionParser.new do |opts|
20
+ opts.version = Buildbox::VERSION
21
+ opts.banner = 'Usage: buildbox COMMAND [command-specific-actions]'
22
+
23
+ opts.separator help
24
+ end
25
+
26
+ commands['auth:login'] = OptionParser.new do |opts|
27
+ opts.banner = "Usage: buildbox auth:login API_KEY"
28
+ end
21
29
 
22
- end.parse!
30
+ commands['auth:logout'] = OptionParser.new do |opts|
31
+ opts.banner = "Usage: buildbox auth:logout"
23
32
  end
24
33
 
25
- client = Trigger::Client.new(options)
34
+ commands['monitor:start'] = OptionParser.new do |opts|
35
+ opts.banner = "Usage: buildbox monitor:start"
26
36
 
27
- if command = ARGV[0]
28
- client.public_send(command)
37
+ opts.on("-d", "--daemon", "Runs as a daemon") do
38
+ options[:daemon] = true
39
+ end
40
+
41
+ opts.on("--help", "You're looking at it.") do
42
+ puts commands['monitor:start']
43
+ exit
44
+ end
45
+ end
46
+
47
+ commands['monitor:stop'] = OptionParser.new do |opts|
48
+ opts.banner = "Usage: buildbox monitor:stop"
49
+ end
50
+
51
+ commands['version'] = OptionParser.new do |opts|
52
+ opts.banner = "Usage: buildbox version"
53
+ end
54
+
55
+ global.order!
56
+
57
+ args = ARGV
58
+
59
+ if command = args.shift
60
+ if commands.has_key?(command)
61
+ commands[command].parse!
62
+ else
63
+ puts "`#{command}` is an unknown command"
64
+ exit 1
65
+ end
66
+
67
+ if command == "version"
68
+ puts Buildbox::VERSION
69
+ exit
70
+ end
71
+
72
+ if command =~ /^monitor/
73
+ client = Buildbox::Client.new(options)
74
+ if command == "monitor:start"
75
+ client.start
76
+ elsif command == "monitor:stop"
77
+ client.stop
78
+ end
79
+ end
80
+
81
+ if command =~ /^auth/
82
+ auth = Buildbox::Auth.new
83
+
84
+ if command == "auth:login"
85
+ api_key = args[0]
86
+
87
+ unless api_key
88
+ puts commands['auth:login']
89
+ exit
90
+ end
91
+
92
+ auth.login(:api_key => args[0])
93
+ elsif command == "auth:logout"
94
+ auth.logout
95
+ end
96
+ end
29
97
  else
30
- abort 'No command specified'
98
+ puts global.help
31
99
  end
@@ -8,6 +8,8 @@ require "buildbox/api"
8
8
  require "buildbox/worker"
9
9
  require "buildbox/pid_file"
10
10
  require "buildbox/configuration"
11
+ require "buildbox/auth"
12
+ require "buildbox/response"
11
13
 
12
14
  module Buildbox
13
15
  require 'fileutils'
@@ -4,24 +4,39 @@ module Buildbox
4
4
  require 'openssl'
5
5
  require 'json'
6
6
 
7
- def update(build, data)
8
- put("repos/#{build.repository_uuid}/builds/#{build.uuid}", normalize_data('build' => data))
7
+ def initialize(options)
8
+ @options = options
9
9
  end
10
10
 
11
- def scheduled(options = {})
12
- builds = []
11
+ def crash(exception, information = {})
12
+ payload = {
13
+ :exception => exception.class.name,
14
+ :message => exception.message,
15
+ :backtrace => exception.backtrace,
16
+ :meta => {}
17
+ }
13
18
 
14
- options[:repositories].each do |repository|
15
- response = get("repos/#{repository}/builds/scheduled")
16
- json = JSON.parse(response.body)
19
+ payload[:meta][:worker_uuid] = worker_uuid if worker_uuid
20
+ payload[:meta][:build_uuid] = information[:build] if information[:build]
21
+ payload[:meta][:client_version] = Buildbox::VERSION
17
22
 
18
- json['response']['builds'].map do |build|
19
- # really smelly way of converting keys to symbols
20
- builds << Build.new(symbolize_keys(build).merge(:repository_uuid => repository))
21
- end
22
- end
23
+ request(:post, "crashes", :crash => payload)
24
+ end
25
+
26
+ def register(payload)
27
+ request(:post, "workers", :worker => payload)
28
+ end
23
29
 
24
- builds
30
+ def login
31
+ request(:get, "user")
32
+ end
33
+
34
+ def update(build, payload)
35
+ request(:put, "workers/#{worker_uuid}/builds/#{build.uuid}", :build => payload)
36
+ end
37
+
38
+ def builds(options = {})
39
+ request(:get, "workers/#{worker_uuid}/builds")
25
40
  end
26
41
 
27
42
  private
@@ -33,25 +48,31 @@ module Buildbox
33
48
  end
34
49
  end
35
50
 
36
- def get(path)
37
- uri = URI.parse(endpoint(path))
38
- request = Net::HTTP::Get.new(uri.request_uri)
51
+ def request(method, path, payload = nil)
52
+ klass = case method
53
+ when :get then Net::HTTP::Get
54
+ when :put then Net::HTTP::Put
55
+ when :post then Net::HTTP::Post
56
+ else raise "No request class defined for `#{method}`"
57
+ end
39
58
 
40
- Buildbox.logger.debug "GET #{uri}"
59
+ uri = URI.parse(endpoint(path))
60
+ request = klass.new(uri.request_uri)
41
61
 
42
- http(uri).request(request)
43
- end
62
+ if payload.nil?
63
+ Buildbox.logger.debug "#{method.to_s.upcase} #{uri}"
64
+ else
65
+ normalized_payload = normalize_payload(payload)
66
+ request.set_form_data normalized_payload
44
67
 
45
- def put(path, data)
46
- uri = URI.parse(endpoint(path))
47
- request = Net::HTTP::Put.new(uri.request_uri)
48
- request.set_form_data data
68
+ Buildbox.logger.debug "#{method.to_s.upcase} #{uri} #{normalized_payload.inspect}"
69
+ end
49
70
 
50
- Buildbox.logger.debug "PUT #{uri}"
71
+ Response.new http(uri).request(request)
72
+ end
51
73
 
52
- response = http(uri).request(request)
53
- raise response.body unless response.code.to_i == 200
54
- response
74
+ def worker_uuid
75
+ Buildbox.configuration.worker_uuid
55
76
  end
56
77
 
57
78
  def endpoint(path)
@@ -59,22 +80,36 @@ module Buildbox
59
80
  "#{Buildbox.configuration.endpoint}/v#{Buildbox.configuration.api_version}/#{path}"
60
81
  end
61
82
 
62
- def symbolize_keys(hash)
63
- Hash[hash.map{ |k, v| [k.to_sym, v] }]
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
64
100
  end
65
101
 
66
- def normalize_data(hash)
67
- hash.inject({}) do |target, member|
68
- key, value = member
69
-
70
- if value.kind_of?(Hash)
71
- value.each { |key2, value2| target["#{key}[#{key2}]"] = value2.to_s }
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)
72
108
  else
73
- target[key] = value.to_s
109
+ newhash[keys2] = v
74
110
  end
75
-
76
- target
77
111
  end
112
+ newhash
78
113
  end
79
114
  end
80
115
  end
@@ -0,0 +1,37 @@
1
+ module Buildbox
2
+ class Auth
3
+ def login(options)
4
+ if Buildbox.configuration.api_key
5
+ error "You have already authentication. To unauthenticate, run `buildbox auth:logout`"
6
+ end
7
+
8
+ key = options[:api_key]
9
+ api = Buildbox::API.new(:api_key => key)
10
+
11
+ if api.login.success?
12
+ Buildbox.configuration.update :api_key, key
13
+ info "Authentication successful"
14
+ end
15
+ end
16
+
17
+ def logout
18
+ if Buildbox.configuration.api_key.nil?
19
+ error "You are currently not logged in. To authenticate, run: `buildbox auth:login`"
20
+ end
21
+
22
+ Buildbox.configuration.update :api_key, nil
23
+ info "You have successfuly logged out"
24
+ end
25
+
26
+ private
27
+
28
+ def info(message)
29
+ Buildbox.logger.info message
30
+ end
31
+
32
+ def error(message)
33
+ Buildbox.logger.error message
34
+ exit 1
35
+ end
36
+ end
37
+ end
@@ -1,13 +1,12 @@
1
1
  module Buildbox
2
2
  class Build
3
- attr_reader :uuid, :repository_uuid
3
+ attr_reader :uuid
4
4
 
5
5
  def initialize(options)
6
- @uuid = options[:uuid]
7
- @repo = options[:repo]
8
- @commit = options[:commit]
9
- @repository_uuid = options[:repository_uuid]
10
- @command = options[:command] || "bundle && rspec"
6
+ @uuid = options[:uuid]
7
+ @repository = options[:repository]
8
+ @commit = options[:commit]
9
+ @command = options[:command] || "bundle && rspec"
11
10
  end
12
11
 
13
12
  def start(&block)
@@ -25,7 +24,7 @@ module Buildbox
25
24
  unless build_path.exist?
26
25
  build_path.mkpath
27
26
 
28
- command.run! %{git clone "#{@repo}" .}
27
+ command.run! %{git clone "#{@repository}" .}
29
28
  end
30
29
  end
31
30
 
@@ -40,7 +39,7 @@ module Buildbox
40
39
  end
41
40
 
42
41
  def folder_name
43
- @repo.gsub(/[^a-zA-Z0-9]/, '-')
42
+ @repository.gsub(/[^a-zA-Z0-9]/, '-')
44
43
  end
45
44
 
46
45
  def command
@@ -10,6 +10,8 @@ module Buildbox
10
10
 
11
11
  Buildbox.logger.info "Starting client..."
12
12
 
13
+ register_client
14
+
13
15
  begin
14
16
  daemonize if @options[:daemon]
15
17
  pid_file.save
@@ -19,6 +21,8 @@ module Buildbox
19
21
  process_build_queue
20
22
  wait_for_interval
21
23
  end
24
+ rescue => e
25
+ api.crash(e, :build => @build)
22
26
  ensure
23
27
  pid_file.delete
24
28
  end
@@ -36,25 +40,35 @@ module Buildbox
36
40
  if @options[:daemon]
37
41
  Process.daemon
38
42
 
39
- Buildbox.logger = Logger.new(Buildbox.root_path.join("ci.log"))
43
+ Buildbox.logger = Logger.new(Buildbox.root_path.join("buildbox.log"))
40
44
  end
41
45
  end
42
46
 
47
+ def register_client
48
+ worker_uuid = Buildbox.configuration.worker_uuid
49
+ response = api.register(:uuid => worker_uuid, :hostname => `hostname`.chomp)
43
50
 
44
- def process_build_queue
45
- build = api.scheduled(:repositories => Buildbox.configuration.repositories).first
51
+ Buildbox.configuration.update :worker_uuid, response.payload[:uuid]
52
+ end
46
53
 
47
- Buildbox::Worker.new(build, api).run if build
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
48
64
  end
49
65
 
50
66
  def reload_configuration
51
- Buildbox.logger.info "Reloading configuration"
52
-
53
67
  Buildbox.configuration.reload
54
68
  end
55
69
 
56
70
  def wait_for_interval
57
- Buildbox.logger.info "Sleeping for #{@interval} seconds"
71
+ Buildbox.logger.debug "Sleeping for #{@interval} seconds"
58
72
 
59
73
  sleep(@interval)
60
74
  end
@@ -68,7 +82,7 @@ module Buildbox
68
82
  end
69
83
 
70
84
  def api
71
- @api ||= Buildbox::API.new
85
+ @api ||= Buildbox::API.new(:api_key => Buildbox.configuration.api_key)
72
86
  end
73
87
 
74
88
  def pid_file
@@ -6,22 +6,28 @@ module Buildbox
6
6
 
7
7
  require 'json'
8
8
 
9
+ attr_accessor :worker_uuid
10
+ attr_accessor :api_key
9
11
  attr_accessor :endpoint
10
12
  attr_accessor :use_ssl
11
13
  attr_accessor :api_version
12
- attr_accessor :repositories
13
14
 
14
15
  def initialize
15
- @use_ssl = true
16
- @endpoint = 'api.buildboxci.com'
16
+ @use_ssl = false
17
+ @endpoint = 'api.buildbox.io'
17
18
  @api_version = 1
18
- @repositories = []
19
+ end
20
+
21
+ def update(key, value)
22
+ self.public_send("#{key}=", value)
23
+ save
19
24
  end
20
25
 
21
26
  def save
22
27
  File.open(path, 'w+') do |file|
23
28
  file.write(to_json)
24
29
  end
30
+ Buildbox.logger.debug "Configuration saved to `#{path}`"
25
31
  end
26
32
 
27
33
  def reload
@@ -39,13 +45,16 @@ module Buildbox
39
45
  private
40
46
 
41
47
  def to_json
42
- JSON.generate(:endpoint => endpoint,
43
- :use_ssl => use_ssl,
44
- :api_version => api_version,
45
- :repositories => repositories)
48
+ JSON.pretty_generate(:endpoint => endpoint,
49
+ :use_ssl => use_ssl,
50
+ :api_version => api_version,
51
+ :api_key => api_key,
52
+ :worker_uuid => worker_uuid)
46
53
  end
47
54
 
48
55
  def read
56
+ Buildbox.logger.debug "Reading configuration `#{path}`"
57
+
49
58
  JSON.parse(path.read)
50
59
  end
51
60
 
@@ -5,7 +5,7 @@ module Buildbox
5
5
  end
6
6
 
7
7
  def path
8
- Buildbox.root_path.join("ci.pid")
8
+ Buildbox.root_path.join("buildbox.pid")
9
9
  end
10
10
 
11
11
  def pid
@@ -0,0 +1,49 @@
1
+ module Buildbox
2
+ class Response
3
+ attr_reader :payload
4
+
5
+ def initialize(response)
6
+ @response = response
7
+
8
+ unless success?
9
+ raise "API Error: #{@response.code} #{@response.body}"
10
+ end
11
+
12
+ if json?
13
+ json = JSON.parse(@response.body)
14
+
15
+ if json.kind_of?(Array)
16
+ @payload = json.map { |item| symbolize_keys(item) }
17
+ else
18
+ @payload = symbolize_keys(json)
19
+ end
20
+ end
21
+ end
22
+
23
+ def success?
24
+ @response.code.to_i == 200
25
+ end
26
+
27
+ private
28
+
29
+ def json?
30
+ @response['content-type'] =~ /json/
31
+ end
32
+
33
+ def symbolize_keys(hash)
34
+ hash.inject({}) do |result, item|
35
+ key, value = item
36
+ new_key = case key
37
+ when String then key.to_sym
38
+ else key
39
+ end
40
+ new_value = case value
41
+ when Hash then symbolize_keys(value)
42
+ else value
43
+ end
44
+ result[new_key] = new_value
45
+ result
46
+ end
47
+ end
48
+ end
49
+ end
@@ -1,3 +1,3 @@
1
1
  module Buildbox
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
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.1
4
+ version: 0.0.2
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-07 00:00:00.000000000 Z
11
+ date: 2013-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -75,6 +75,7 @@ extensions: []
75
75
  extra_rdoc_files: []
76
76
  files:
77
77
  - .gitignore
78
+ - .rspec
78
79
  - Gemfile
79
80
  - LICENSE.txt
80
81
  - README.md
@@ -83,11 +84,13 @@ files:
83
84
  - buildbox-ruby.gemspec
84
85
  - lib/buildbox.rb
85
86
  - lib/buildbox/api.rb
87
+ - lib/buildbox/auth.rb
86
88
  - lib/buildbox/build.rb
87
89
  - lib/buildbox/client.rb
88
90
  - lib/buildbox/command.rb
89
91
  - lib/buildbox/configuration.rb
90
92
  - lib/buildbox/pid_file.rb
93
+ - lib/buildbox/response.rb
91
94
  - lib/buildbox/result.rb
92
95
  - lib/buildbox/utf8.rb
93
96
  - lib/buildbox/version.rb