buildbox 0.0.1 → 0.0.2

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: 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