buildbox 0.0.1

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