buildbox 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +29 -0
  6. data/Rakefile +1 -0
  7. data/bin/buildbox +31 -0
  8. data/buildbox-ruby.gemspec +25 -0
  9. data/lib/buildbox.rb +37 -0
  10. data/lib/buildbox/api.rb +80 -0
  11. data/lib/buildbox/build.rb +50 -0
  12. data/lib/buildbox/client.rb +78 -0
  13. data/lib/buildbox/command.rb +67 -0
  14. data/lib/buildbox/configuration.rb +56 -0
  15. data/lib/buildbox/pid_file.rb +25 -0
  16. data/lib/buildbox/result.rb +7 -0
  17. data/lib/buildbox/utf8.rb +50 -0
  18. data/lib/buildbox/version.rb +3 -0
  19. data/lib/buildbox/worker.rb +25 -0
  20. data/spec/buildbox/buildbox/build_spec.rb +66 -0
  21. data/spec/buildbox/buildbox/command_spec.rb +115 -0
  22. data/spec/buildbox/buildbox/configuration_spec.rb +9 -0
  23. data/spec/buildbox/buildbox_spec.rb +4 -0
  24. data/spec/fixtures/repo.git/HEAD +1 -0
  25. data/spec/fixtures/repo.git/config +6 -0
  26. data/spec/fixtures/repo.git/description +1 -0
  27. data/spec/fixtures/repo.git/hooks/applypatch-msg.sample +15 -0
  28. data/spec/fixtures/repo.git/hooks/commit-msg.sample +24 -0
  29. data/spec/fixtures/repo.git/hooks/post-update.sample +8 -0
  30. data/spec/fixtures/repo.git/hooks/pre-applypatch.sample +14 -0
  31. data/spec/fixtures/repo.git/hooks/pre-commit.sample +50 -0
  32. data/spec/fixtures/repo.git/hooks/pre-push.sample +53 -0
  33. data/spec/fixtures/repo.git/hooks/pre-rebase.sample +169 -0
  34. data/spec/fixtures/repo.git/hooks/prepare-commit-msg.sample +36 -0
  35. data/spec/fixtures/repo.git/hooks/update.sample +128 -0
  36. data/spec/fixtures/repo.git/info/exclude +6 -0
  37. data/spec/fixtures/repo.git/objects/2d/762cdfd781dc4077c9f27a18969efbd186363c +2 -0
  38. data/spec/fixtures/repo.git/objects/3e/0c65433b241ff2c59220f80bcdcd2ebb7e4b96 +2 -0
  39. data/spec/fixtures/repo.git/objects/95/73fff3f9e2c38ccdd7755674ec87c31ca08270 +0 -0
  40. data/spec/fixtures/repo.git/objects/c4/01f49fe0172add6a09aec8a7808112ce5894dd +0 -0
  41. data/spec/fixtures/repo.git/objects/c9/3cd4edd7e0b6fd4c69e65fc7f25cbf06ac855c +0 -0
  42. data/spec/fixtures/repo.git/objects/e9/8d8a9be514ef53609a52c9e1b820dbcc8e6603 +0 -0
  43. data/spec/fixtures/repo.git/refs/heads/master +1 -0
  44. data/spec/fixtures/rspec/test_spec.rb +5 -0
  45. data/spec/integration/running_a_build_spec.rb +89 -0
  46. data/spec/spec_helper.rb +18 -0
  47. data/spec/support/dotenv.rb +3 -0
  48. data/spec/support/silence_logger.rb +7 -0
  49. metadata +177 -0
@@ -0,0 +1,56 @@
1
+ module Buildbox
2
+ class Configuration
3
+ def self.load(*args)
4
+ new(*args).tap &:reload
5
+ end
6
+
7
+ require 'json'
8
+
9
+ attr_accessor :endpoint
10
+ attr_accessor :use_ssl
11
+ attr_accessor :api_version
12
+ attr_accessor :repositories
13
+
14
+ def initialize
15
+ @use_ssl = true
16
+ @endpoint = 'api.buildboxci.com'
17
+ @api_version = 1
18
+ @repositories = []
19
+ end
20
+
21
+ def save
22
+ File.open(path, 'w+') do |file|
23
+ file.write(to_json)
24
+ end
25
+ end
26
+
27
+ def reload
28
+ json = if path.exist?
29
+ read
30
+ else
31
+ save && read
32
+ end
33
+
34
+ json.each_pair do |key, value|
35
+ self.public_send("#{key}=", value)
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def to_json
42
+ JSON.generate(:endpoint => endpoint,
43
+ :use_ssl => use_ssl,
44
+ :api_version => api_version,
45
+ :repositories => repositories)
46
+ end
47
+
48
+ def read
49
+ JSON.parse(path.read)
50
+ end
51
+
52
+ def path
53
+ Buildbox.root_path.join("configuration.json")
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,25 @@
1
+ module Buildbox
2
+ class PidFile
3
+ def exist?
4
+ File.exist?(path)
5
+ end
6
+
7
+ def path
8
+ Buildbox.root_path.join("ci.pid")
9
+ end
10
+
11
+ def pid
12
+ File.readlines(path).first.to_i
13
+ end
14
+
15
+ def save
16
+ File.open(path, 'w+') { |file| file.write(Process.pid.to_s) }
17
+ end
18
+
19
+ def delete
20
+ value = pid
21
+ File.delete(path)
22
+ value
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,7 @@
1
+ module Buildbox
2
+ class Result < Struct.new(:output, :exit_status)
3
+ def success?
4
+ exit_status == 0
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,50 @@
1
+ module Buildbox
2
+ module UTF8
3
+ # Replace or delete invalid UTF-8 characters from text, which is assumed
4
+ # to be in UTF-8.
5
+ #
6
+ # The text is expected to come from external to Integrity sources such as
7
+ # commit messages or build output.
8
+ #
9
+ # On ruby 1.9, invalid UTF-8 characters are replaced with question marks.
10
+ # On ruby 1.8, if iconv extension is present, invalid UTF-8 characters
11
+ # are removed.
12
+ # On ruby 1.8, if iconv extension is not present, the string is unmodified.
13
+ def self.clean(text)
14
+ # http://po-ru.com/diary/fixing-invalid-utf-8-in-ruby-revisited/
15
+ # http://stackoverflow.com/questions/9126782/how-to-change-deprecated-iconv-to-stringencode-for-invalid-utf8-correction
16
+ if text.respond_to?(:encoding)
17
+ # ruby 1.9
18
+ text = text.force_encoding('utf-8').encode(intermediate_encoding, :invalid => :replace, :replace => '?').encode('utf-8')
19
+ else
20
+ # ruby 1.8
21
+ # As no encoding checks are done, any string will be accepted.
22
+ # But delete invalid utf-8 characters anyway for consistency with 1.9.
23
+ iconv, iconv_fallback = clean_utf8_iconv
24
+ if iconv
25
+ begin
26
+ output = iconv.iconv(text)
27
+ rescue Iconv::IllegalSequence
28
+ output = iconv_fallback.iconv(text)
29
+ end
30
+ end
31
+ end
32
+ text
33
+ end
34
+
35
+ # Apparently utf-16 is not available everywhere, in particular not on travis.
36
+ # Try to find a usable encoding.
37
+ def self.intermediate_encoding
38
+ map = {}
39
+ Encoding.list.each do |encoding|
40
+ map[encoding.name.downcase] = true
41
+ end
42
+ %w(utf-16 utf-16be utf-16le utf-7 utf-32 utf-32le utf-32be).each do |candidate|
43
+ if map[candidate]
44
+ return candidate
45
+ end
46
+ end
47
+ raise CannotFindEncoding, 'Cannot find an intermediate encoding for conversion to UTF-8'
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,3 @@
1
+ module Buildbox
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,25 @@
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
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
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") }
5
+
6
+ describe "#start" do
7
+ let(:build_path) { double }
8
+ let(:root_path) { double(:join => build_path) }
9
+ let(:command) { double(:run => true, :run! => true) }
10
+
11
+ before do
12
+ Buildbox.stub(:root_path => root_path)
13
+ Buildbox::Command.stub(:new => command)
14
+ end
15
+
16
+ context "with a new checkout" do
17
+ before do
18
+ build_path.stub(:exist? => false, :mkpath => true)
19
+ end
20
+
21
+ it "creates a directory for the build" do
22
+ build_path.should_receive(:mkpath)
23
+
24
+ build.start
25
+ end
26
+
27
+ it "creates a folder for the build" do
28
+ root_path.should_receive(:join).with('git-github-com-keithpitt-ci-ruby-git')
29
+
30
+ build.start
31
+ end
32
+
33
+ it "clones the repo" do
34
+ command.should_receive(:run!).with(%{git clone "git@github.com:keithpitt/ci-ruby.git" .}).once
35
+
36
+ build.start
37
+ end
38
+ end
39
+
40
+ context "with an existing checkout" do
41
+ before do
42
+ build_path.stub(:exist? => true)
43
+ end
44
+
45
+ it "doesn't checkout the repo again" do
46
+ command.should_not_receive(:run!).with(%{git clone "git@github.com:keithpitt/ci-ruby.git" .})
47
+
48
+ build.start
49
+ end
50
+
51
+ it "cleans, fetches and checks out the commit" do
52
+ command.should_receive(:run!).with(%{git clean -fd}).ordered
53
+ command.should_receive(:run!).with(%{git fetch}).ordered
54
+ command.should_receive(:run!).with(%{git checkout -qf "67b15b704e0"}).ordered
55
+
56
+ build.start
57
+ end
58
+
59
+ it "runs the command" do
60
+ command.should_receive(:run).with(%{rspec})
61
+
62
+ build.start
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,115 @@
1
+ require "spec_helper"
2
+
3
+ describe Buildbox::Command do
4
+ let(:command) { Buildbox::Command.new }
5
+
6
+ describe "#run" do
7
+ it "successfully runs and returns the output from a simple comment" do
8
+ result = command.run('echo hello world')
9
+
10
+ result.should be_success
11
+ result.output.should == 'hello world'
12
+ end
13
+
14
+ it "redirects stdout to stderr" do
15
+ result = command.run('echo hello world 1>&2')
16
+
17
+ result.should be_success
18
+ result.output.should == 'hello world'
19
+ end
20
+
21
+ it "handles commands that fail and returns the correct status" do
22
+ result = command.run('(exit 1)')
23
+
24
+ result.should_not be_success
25
+ result.output.should == ''
26
+ end
27
+
28
+ it "handles running malformed commands" do
29
+ result = command.run('if (')
30
+
31
+ result.should_not be_success
32
+ # bash 3.2.48 prints "syntax error" in lowercase.
33
+ # freebsd 9.1 /bin/sh prints "Syntax error" with capital S.
34
+ # zsh 5.0.2 prints "parse error" which we do not handle.
35
+ # localized systems will print the message in not English which
36
+ # we do not handle either.
37
+ result.output.should =~ /(syntax|parse) error/i
38
+ end
39
+
40
+ it "can collect output in chunks" do
41
+ chunked_output = ''
42
+ result = command.run('echo hello world') do |chunk|
43
+ chunked_output += chunk
44
+ end
45
+
46
+ result.should be_success
47
+ result.output.should == 'hello world'
48
+ chunked_output.should == "hello world\r\n"
49
+ end
50
+
51
+ it "can collect chunks at paticular intervals" do
52
+ command = Buildbox::Command.new(nil, 0.1)
53
+
54
+ chunked_output = ''
55
+ result = command.run('sleep 0.5; echo hello world') do |chunk|
56
+ chunked_output += chunk
57
+ end
58
+
59
+ result.should be_success
60
+ result.output.should == 'hello world'
61
+ chunked_output.should == "hello world\r\n"
62
+ end
63
+
64
+ it "can collect chunks from within a thread" do
65
+ chunked_output = ''
66
+ result = nil
67
+ worker_thread = Thread.new do
68
+ result = command.run('echo before sleep; sleep 1; echo after sleep') do |chunk|
69
+ chunked_output += chunk
70
+ end
71
+ end
72
+
73
+ worker_thread.run
74
+ sleep(0.5)
75
+ result.should be_nil
76
+ chunked_output.should == "before sleep\r\n"
77
+
78
+ worker_thread.join
79
+
80
+ result.should_not be_nil
81
+ result.should be_success
82
+ result.output.should == "before sleep\r\nafter sleep"
83
+ chunked_output.should == "before sleep\r\nafter sleep\r\n"
84
+ end
85
+
86
+ it 'returns a result when running an invalid command in a thread' do
87
+ result = nil
88
+ second_result = nil
89
+ thread = Thread.new do
90
+ result = command.run('sillycommandlololol')
91
+ second_result = command.run('export FOO=bar; doesntexist.rb')
92
+ end
93
+ thread.join
94
+
95
+ result.should_not be_success
96
+ result.output.should =~ /sillycommandlololol.+not found/
97
+
98
+ second_result.should_not be_success
99
+ # osx: `sh: doesntexist.rb: command not found`
100
+ # ubuntu: `sh: 1: doesntexist.rb: not found`
101
+ second_result.output.should =~ /doesntexist.rb:.+not found/
102
+ end
103
+
104
+ it "captures color'd output" do
105
+ chunked_output = ''
106
+ result = command.run("rspec #{FIXTURES_PATH.join('rspec', 'test_spec.rb')} --color") do |chunk|
107
+ chunked_output += chunk
108
+ end
109
+
110
+ result.should be_success
111
+ result.output.should include("32m")
112
+ chunked_output.should include("32m")
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Buildbox::Configuration do
4
+ subject(:configuration) { Buildbox::Configuration.new }
5
+
6
+ it "has a default endpoint" do
7
+ configuration.endpoint.should =~ /buildboxci/
8
+ end
9
+ end
@@ -0,0 +1,4 @@
1
+ require 'spec_helper'
2
+
3
+ describe Buildbox do
4
+ end
@@ -0,0 +1 @@
1
+ ref: refs/heads/master
@@ -0,0 +1,6 @@
1
+ [core]
2
+ repositoryformatversion = 0
3
+ filemode = true
4
+ bare = true
5
+ ignorecase = true
6
+ precomposeunicode = false
@@ -0,0 +1 @@
1
+ Unnamed repository; edit this file 'description' to name the repository.
@@ -0,0 +1,15 @@
1
+ #!/bin/sh
2
+ #
3
+ # An example hook script to check the commit log message taken by
4
+ # applypatch from an e-mail message.
5
+ #
6
+ # The hook should exit with non-zero status after issuing an
7
+ # appropriate message if it wants to stop the commit. The hook is
8
+ # allowed to edit the commit message file.
9
+ #
10
+ # To enable this hook, rename this file to "applypatch-msg".
11
+
12
+ . git-sh-setup
13
+ test -x "$GIT_DIR/hooks/commit-msg" &&
14
+ exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
15
+ :
@@ -0,0 +1,24 @@
1
+ #!/bin/sh
2
+ #
3
+ # An example hook script to check the commit log message.
4
+ # Called by "git commit" with one argument, the name of the file
5
+ # that has the commit message. The hook should exit with non-zero
6
+ # status after issuing an appropriate message if it wants to stop the
7
+ # commit. The hook is allowed to edit the commit message file.
8
+ #
9
+ # To enable this hook, rename this file to "commit-msg".
10
+
11
+ # Uncomment the below to add a Signed-off-by line to the message.
12
+ # Doing this in a hook is a bad idea in general, but the prepare-commit-msg
13
+ # hook is more suited to it.
14
+ #
15
+ # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
16
+ # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
17
+
18
+ # This example catches duplicate Signed-off-by lines.
19
+
20
+ test "" = "$(grep '^Signed-off-by: ' "$1" |
21
+ sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
22
+ echo >&2 Duplicate Signed-off-by lines.
23
+ exit 1
24
+ }
@@ -0,0 +1,8 @@
1
+ #!/bin/sh
2
+ #
3
+ # An example hook script to prepare a packed repository for use over
4
+ # dumb transports.
5
+ #
6
+ # To enable this hook, rename this file to "post-update".
7
+
8
+ exec git update-server-info
@@ -0,0 +1,14 @@
1
+ #!/bin/sh
2
+ #
3
+ # An example hook script to verify what is about to be committed
4
+ # by applypatch from an e-mail message.
5
+ #
6
+ # The hook should exit with non-zero status after issuing an
7
+ # appropriate message if it wants to stop the commit.
8
+ #
9
+ # To enable this hook, rename this file to "pre-applypatch".
10
+
11
+ . git-sh-setup
12
+ test -x "$GIT_DIR/hooks/pre-commit" &&
13
+ exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
14
+ :