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.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/bin/buildbox +31 -0
- data/buildbox-ruby.gemspec +25 -0
- data/lib/buildbox.rb +37 -0
- data/lib/buildbox/api.rb +80 -0
- data/lib/buildbox/build.rb +50 -0
- data/lib/buildbox/client.rb +78 -0
- data/lib/buildbox/command.rb +67 -0
- data/lib/buildbox/configuration.rb +56 -0
- data/lib/buildbox/pid_file.rb +25 -0
- data/lib/buildbox/result.rb +7 -0
- data/lib/buildbox/utf8.rb +50 -0
- data/lib/buildbox/version.rb +3 -0
- data/lib/buildbox/worker.rb +25 -0
- data/spec/buildbox/buildbox/build_spec.rb +66 -0
- data/spec/buildbox/buildbox/command_spec.rb +115 -0
- data/spec/buildbox/buildbox/configuration_spec.rb +9 -0
- data/spec/buildbox/buildbox_spec.rb +4 -0
- data/spec/fixtures/repo.git/HEAD +1 -0
- data/spec/fixtures/repo.git/config +6 -0
- data/spec/fixtures/repo.git/description +1 -0
- data/spec/fixtures/repo.git/hooks/applypatch-msg.sample +15 -0
- data/spec/fixtures/repo.git/hooks/commit-msg.sample +24 -0
- data/spec/fixtures/repo.git/hooks/post-update.sample +8 -0
- data/spec/fixtures/repo.git/hooks/pre-applypatch.sample +14 -0
- data/spec/fixtures/repo.git/hooks/pre-commit.sample +50 -0
- data/spec/fixtures/repo.git/hooks/pre-push.sample +53 -0
- data/spec/fixtures/repo.git/hooks/pre-rebase.sample +169 -0
- data/spec/fixtures/repo.git/hooks/prepare-commit-msg.sample +36 -0
- data/spec/fixtures/repo.git/hooks/update.sample +128 -0
- data/spec/fixtures/repo.git/info/exclude +6 -0
- data/spec/fixtures/repo.git/objects/2d/762cdfd781dc4077c9f27a18969efbd186363c +2 -0
- data/spec/fixtures/repo.git/objects/3e/0c65433b241ff2c59220f80bcdcd2ebb7e4b96 +2 -0
- data/spec/fixtures/repo.git/objects/95/73fff3f9e2c38ccdd7755674ec87c31ca08270 +0 -0
- data/spec/fixtures/repo.git/objects/c4/01f49fe0172add6a09aec8a7808112ce5894dd +0 -0
- data/spec/fixtures/repo.git/objects/c9/3cd4edd7e0b6fd4c69e65fc7f25cbf06ac855c +0 -0
- data/spec/fixtures/repo.git/objects/e9/8d8a9be514ef53609a52c9e1b820dbcc8e6603 +0 -0
- data/spec/fixtures/repo.git/refs/heads/master +1 -0
- data/spec/fixtures/rspec/test_spec.rb +5 -0
- data/spec/integration/running_a_build_spec.rb +89 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/dotenv.rb +3 -0
- data/spec/support/silence_logger.rb +7 -0
- 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,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,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 @@
|
|
1
|
+
ref: refs/heads/master
|
@@ -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,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
|
+
:
|