juici 0.0.0.alpha1
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.
- data/.gitignore +2 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +74 -0
- data/Procfile +1 -0
- data/README.md +138 -0
- data/Rakefile +33 -0
- data/TODO.md +28 -0
- data/bin/juici +7 -0
- data/bin/juicic +54 -0
- data/config/mongoid.yml.sample +25 -0
- data/juici-interface.gemspec +19 -0
- data/juici.gemspec +32 -0
- data/lib/juici.rb +24 -0
- data/lib/juici/app.rb +67 -0
- data/lib/juici/build_environment.rb +38 -0
- data/lib/juici/build_logic.rb +37 -0
- data/lib/juici/build_queue.rb +111 -0
- data/lib/juici/callback.rb +26 -0
- data/lib/juici/config.rb +11 -0
- data/lib/juici/controllers.rb +6 -0
- data/lib/juici/controllers/base.rb +26 -0
- data/lib/juici/controllers/build_queue.rb +14 -0
- data/lib/juici/controllers/builds.rb +74 -0
- data/lib/juici/controllers/index.rb +20 -0
- data/lib/juici/controllers/trigger.rb +85 -0
- data/lib/juici/database.rb +25 -0
- data/lib/juici/exceptions.rb +2 -0
- data/lib/juici/find_logic.rb +11 -0
- data/lib/juici/helpers/form_helpers.rb +11 -0
- data/lib/juici/helpers/html_helpers.rb +4 -0
- data/lib/juici/helpers/url_helpers.rb +38 -0
- data/lib/juici/interface.rb +13 -0
- data/lib/juici/models/build.rb +190 -0
- data/lib/juici/models/build_process.rb +7 -0
- data/lib/juici/models/project.rb +9 -0
- data/lib/juici/server.rb +172 -0
- data/lib/juici/version.rb +8 -0
- data/lib/juici/views/README.markdown +138 -0
- data/lib/juici/views/about.erb +16 -0
- data/lib/juici/views/builds.erb +7 -0
- data/lib/juici/views/builds/edit.erb +23 -0
- data/lib/juici/views/builds/list.erb +27 -0
- data/lib/juici/views/builds/new.erb +43 -0
- data/lib/juici/views/builds/show.erb +4 -0
- data/lib/juici/views/index.erb +30 -0
- data/lib/juici/views/layout.erb +44 -0
- data/lib/juici/views/not_found.erb +3 -0
- data/lib/juici/views/partials/builds/debug.erb +22 -0
- data/lib/juici/views/partials/builds/output.erb +1 -0
- data/lib/juici/views/partials/builds/show.erb +19 -0
- data/lib/juici/views/partials/builds/sidebar.erb +13 -0
- data/lib/juici/views/partials/index/recently_built.erb +19 -0
- data/lib/juici/views/queue/list.erb +6 -0
- data/lib/juici/views/redirect.erb +0 -0
- data/lib/juici/views/support.erb +6 -0
- data/lib/juici/watcher.rb +48 -0
- data/public/favicon.ico +0 -0
- data/public/images/black_denim.png +0 -0
- data/public/styles/builds.css +62 -0
- data/public/styles/juici.css +226 -0
- data/public/vendor/bootstrap.css +6004 -0
- data/public/vendor/bootstrap.js +2036 -0
- data/public/vendor/img/glyphicons-halflings-white.png +0 -0
- data/public/vendor/jquery.js +9440 -0
- data/sample/mongod.conf +5 -0
- data/script/cibuild +10 -0
- data/spec/build_callback_spec.rb +54 -0
- data/spec/build_environment_spec.rb +53 -0
- data/spec/build_process_spec.rb +96 -0
- data/spec/build_queue_spec.rb +63 -0
- data/spec/controllers/builds_spec.rb +68 -0
- data/spec/controllers/index_spec.rb +28 -0
- data/spec/juici_app_spec.rb +8 -0
- data/spec/models/build_spec.rb +54 -0
- data/spec/spec_helper.rb +26 -0
- metadata +290 -0
data/sample/mongod.conf
ADDED
data/script/cibuild
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Juicy::Build::Callback" do
|
4
|
+
|
5
|
+
it "should call #process! on each of it's callbacks" do
|
6
|
+
def mock_callback
|
7
|
+
mock(:payload= => nil).tap do |callback|
|
8
|
+
callback.expects(:process!).once
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
callbacks = ["rawr", "test"]
|
13
|
+
|
14
|
+
callbacks.each do |callback|
|
15
|
+
Juici::Callback.stubs(:new).with(callback).
|
16
|
+
returns(mock_callback)
|
17
|
+
end
|
18
|
+
|
19
|
+
build = Juici::Build.new(:callbacks => callbacks)
|
20
|
+
build.start!
|
21
|
+
build.success!
|
22
|
+
build.destroy
|
23
|
+
end
|
24
|
+
|
25
|
+
# TODO DRY This right up
|
26
|
+
it "should have status success on success" do
|
27
|
+
cb = Juici::Callback.new("test")
|
28
|
+
cb.stubs(:process!)
|
29
|
+
|
30
|
+
Juici::Callback.stubs(:new).with("test").returns(cb)
|
31
|
+
|
32
|
+
build = Juici::Build.new(:callbacks => ["test"])
|
33
|
+
build.start!
|
34
|
+
build.success!
|
35
|
+
|
36
|
+
JSON.load(cb.payload)["status"].should == Juici::BuildStatus::PASS
|
37
|
+
build.destroy
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should have status failed on failure" do
|
41
|
+
cb = Juici::Callback.new("test")
|
42
|
+
cb.stubs(:process!)
|
43
|
+
|
44
|
+
Juici::Callback.stubs(:new).with("test").returns(cb)
|
45
|
+
|
46
|
+
build = Juici::Build.new(:callbacks => ["test"])
|
47
|
+
build.start!
|
48
|
+
build.failure!
|
49
|
+
|
50
|
+
JSON.load(cb.payload)["status"].should == Juici::BuildStatus::FAIL
|
51
|
+
build.destroy
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
# Stub the shit out of ENV.to_hash
|
4
|
+
describe "Juici build environment" do
|
5
|
+
|
6
|
+
it "Should hose sensitive environment variables" do
|
7
|
+
new_env = ENV.to_hash
|
8
|
+
::Juici::BUILD_SENSITIVE_VARIABLES.each do |var|
|
9
|
+
new_env[var] = "Some values!"
|
10
|
+
end
|
11
|
+
ENV.stubs(:to_hash).returns(new_env)
|
12
|
+
|
13
|
+
env = ::Juici::BuildEnvironment.new
|
14
|
+
::Juici::BUILD_SENSITIVE_VARIABLES.each do |var|
|
15
|
+
env[var].should be_nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it "Should merge json hashes" do
|
20
|
+
env = ::Juici::BuildEnvironment.new
|
21
|
+
json = %[{"my_spec_key": "my_spec_value"}]
|
22
|
+
|
23
|
+
env.load_json!(json).should == true
|
24
|
+
env["my_spec_key"].should == "my_spec_value"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "Should fail on valid json that is a string" do
|
28
|
+
env = ::Juici::BuildEnvironment.new
|
29
|
+
json = %["rawr!"]
|
30
|
+
|
31
|
+
env.load_json!(json).should == false
|
32
|
+
end
|
33
|
+
|
34
|
+
it "Should fail on valid json that is an integer" do
|
35
|
+
env = ::Juici::BuildEnvironment.new
|
36
|
+
json = %[4]
|
37
|
+
|
38
|
+
env.load_json!(json).should == false
|
39
|
+
end
|
40
|
+
|
41
|
+
it "Should fail on invalid json" do
|
42
|
+
env = ::Juici::BuildEnvironment.new
|
43
|
+
json = %[{ butts lol]
|
44
|
+
|
45
|
+
env.load_json!(json).should == false
|
46
|
+
end
|
47
|
+
|
48
|
+
it "Should regard an empty string as valid" do
|
49
|
+
env = ::Juici::BuildEnvironment.new
|
50
|
+
json = ""
|
51
|
+
env.load_json!(json).should == true
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Juici build abstraction" do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
@app = Juici::App.new
|
7
|
+
end
|
8
|
+
|
9
|
+
after(:all) do
|
10
|
+
Juici::App.shutdown
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
it "Should run a given command in a subshell" do
|
15
|
+
watcher = Juici::Watcher.instance.start
|
16
|
+
build = Juici::Build.new(parent: "test project",
|
17
|
+
environment: {},
|
18
|
+
command: "/bin/echo 'test build succeeded'")
|
19
|
+
$build_queue << build
|
20
|
+
|
21
|
+
# Wait a reasonable time for build to finish
|
22
|
+
# TODO: This can leverage the hooks system
|
23
|
+
# TODO: Easer will be to have worker.block or something
|
24
|
+
Timeout::timeout(2) do
|
25
|
+
poll_build(build)
|
26
|
+
|
27
|
+
build.reload
|
28
|
+
build.status.should == Juici::BuildStatus::PASS
|
29
|
+
build[:output].chomp.should == "test build succeeded"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "Should catch failed builds" do
|
34
|
+
watcher = Juici::Watcher.instance.start
|
35
|
+
build = Juici::Build.new(parent: "test project",
|
36
|
+
environment: {},
|
37
|
+
command: "exit 3")
|
38
|
+
$build_queue << build
|
39
|
+
|
40
|
+
# Wait a reasonable time for build to finish
|
41
|
+
# TODO: This can leverage the hooks system
|
42
|
+
# TODO: Easer will be to have worker.block or something
|
43
|
+
Timeout::timeout(2) do
|
44
|
+
poll_build(build)
|
45
|
+
|
46
|
+
build.reload
|
47
|
+
build.status.should == Juici::BuildStatus::FAIL
|
48
|
+
build[:output].chomp.should == ""
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it "Should kill builds" do
|
53
|
+
build = Juici::Build.new(parent: "test",
|
54
|
+
command: "sleep 30")
|
55
|
+
$build_queue << build
|
56
|
+
sleep 1
|
57
|
+
build.kill!
|
58
|
+
|
59
|
+
build.reload
|
60
|
+
|
61
|
+
build.status.should == Juici::BuildStatus::FAIL
|
62
|
+
build.warnings.should include("Killed!")
|
63
|
+
end
|
64
|
+
|
65
|
+
it "Can create and fetch new bundles" do
|
66
|
+
watcher = Juici::Watcher.instance.start
|
67
|
+
build = Juici::Build.new(parent: "test project",
|
68
|
+
environment: ::Juici::BuildEnvironment.new.to_hash,
|
69
|
+
command: <<-EOS)
|
70
|
+
#!/bin/sh
|
71
|
+
set -e
|
72
|
+
|
73
|
+
cat > Gemfile <<EOF
|
74
|
+
source :rubygems
|
75
|
+
gem "m2a"
|
76
|
+
EOF
|
77
|
+
|
78
|
+
env
|
79
|
+
|
80
|
+
bundle install
|
81
|
+
bundle list
|
82
|
+
bundle config
|
83
|
+
EOS
|
84
|
+
|
85
|
+
build.save!
|
86
|
+
$build_queue << build
|
87
|
+
|
88
|
+
Timeout::timeout(10) do
|
89
|
+
poll_build(build)
|
90
|
+
|
91
|
+
build.status.should == Juici::BuildStatus::PASS
|
92
|
+
build[:output].chomp.should match /m2a/
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Juici::BuildQueue do
|
4
|
+
|
5
|
+
subject { Juici::BuildQueue.new.tap { |q| q.__builds = @builds } }
|
6
|
+
|
7
|
+
it "Should return 1 as min priority when empty" do
|
8
|
+
@builds = []
|
9
|
+
subject.current_min_priority.should == 1
|
10
|
+
end
|
11
|
+
|
12
|
+
it "Should return the min priority when set" do
|
13
|
+
@builds = builds_with(priority: [ 5, 3, 7 ])
|
14
|
+
subject.current_min_priority.should == 3
|
15
|
+
end
|
16
|
+
|
17
|
+
it "Should deal gracefully with nil" do
|
18
|
+
@builds = builds_with(priority: [ 5, 4, nil, 7 ])
|
19
|
+
subject.current_min_priority.should == 4
|
20
|
+
end
|
21
|
+
|
22
|
+
it "Should remove a given build by pid by pid" do
|
23
|
+
# Build an array of
|
24
|
+
@builds = builds_with(pid: [1, 2, 3, 4, 5, 6])
|
25
|
+
subject.purge(:pid, stub(:pid => 3))
|
26
|
+
@builds.collect(&:pid).should == [1, 2, 4, 5, 6]
|
27
|
+
end
|
28
|
+
|
29
|
+
it "Should silently fail to remove nonexistant pids by pid" do
|
30
|
+
@builds = builds_with(pid: [1, 2, 3, 4, 5, 6])
|
31
|
+
subject.purge(:pid, stub(:pid => 9))
|
32
|
+
@builds.collect(&:pid).should == [1, 2, 3, 4, 5, 6]
|
33
|
+
end
|
34
|
+
|
35
|
+
it "Should remove a given build by id" do
|
36
|
+
@builds = builds_with(_id: [1, 2, 3, 4, 5, 6])
|
37
|
+
subject.delete(3)
|
38
|
+
@builds.collect(&:_id).should == [1, 2, 4, 5, 6]
|
39
|
+
end
|
40
|
+
|
41
|
+
it "Should return a low priority job from #next_child" do
|
42
|
+
@builds = builds_with(priority: [1, 2, 3, 4, 5, 6])
|
43
|
+
subject.next_child.priority.should == 1
|
44
|
+
@builds = builds_with(priority: [6, 5, 4, 3, 2, 1])
|
45
|
+
subject.next_child.priority.should == 1
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
class Juici::BuildQueue #{{{ Test injection
|
51
|
+
def __builds=(builds)
|
52
|
+
@builds = builds
|
53
|
+
end
|
54
|
+
end #}}}
|
55
|
+
|
56
|
+
def builds_with(args)
|
57
|
+
args.map do |k, v|
|
58
|
+
v.map do |i|
|
59
|
+
stub(k => i)
|
60
|
+
end
|
61
|
+
end.flatten
|
62
|
+
end
|
63
|
+
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Juici::Controllers::Builds do
|
4
|
+
|
5
|
+
describe "list" do
|
6
|
+
it "should throw not_found for invalid projects" do
|
7
|
+
params = { :project => "__LolIDon'tExist" }
|
8
|
+
lambda {
|
9
|
+
Juici::Controllers::Builds.new(params).list
|
10
|
+
}.should raise_error(Sinatra::NotFound)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "show" do
|
15
|
+
it "should throw not_found for invalid projects" do
|
16
|
+
params = { :project => "__LolIDon'tExist" }
|
17
|
+
lambda {
|
18
|
+
Juici::Controllers::Builds.new(params).show
|
19
|
+
}.should raise_error(Sinatra::NotFound)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "new" do
|
24
|
+
it "should return the new template" do
|
25
|
+
Juici::Controllers::Builds.new({}).new do |opts, template|
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "edit" do
|
31
|
+
it "should throw not_found for invalid projects" do
|
32
|
+
params = { :project => "__LolIDon'tExist" }
|
33
|
+
lambda {
|
34
|
+
Juici::Controllers::Builds.new(params).show
|
35
|
+
}.should raise_error(Sinatra::NotFound)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "Should update build objects when given data" do
|
39
|
+
# FIXME This is a kludge to work around #38
|
40
|
+
::Juici::Project.find_or_create_by(name: "test project")
|
41
|
+
build = Juici::Build.new(parent: "test project", priority: 1, title: "test build")
|
42
|
+
build.save!
|
43
|
+
|
44
|
+
Juici::Controllers::Builds.new({:priority => 15, :title => "butts lol", :id => build[:_id], :project => "test project"}).update!
|
45
|
+
build.reload
|
46
|
+
|
47
|
+
build[:title].should == "butts lol"
|
48
|
+
build[:priority].should == 15
|
49
|
+
end
|
50
|
+
|
51
|
+
it "Should not let you update a build's ID" do
|
52
|
+
# FIXME This is a kludge to work around #38
|
53
|
+
::Juici::Project.find_or_create_by(name: "test project")
|
54
|
+
build = Juici::Build.new(parent: "test project")
|
55
|
+
build.save!
|
56
|
+
|
57
|
+
updated_build = Juici::Controllers::Builds.new({:_id => "New id lol", :id => build[:_id], :project => "test project"}).update!
|
58
|
+
|
59
|
+
updated_build[:_id].should == build[:_id]
|
60
|
+
end
|
61
|
+
|
62
|
+
it "Should not touch values if given invalid values" do
|
63
|
+
pending("Needs more research on mongoid")
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Juici::Controllers::Index do
|
4
|
+
|
5
|
+
describe "index" do
|
6
|
+
it "should render index" do
|
7
|
+
Juici::Controllers::Index.new().index do |template, opts|
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "about" do
|
13
|
+
it "should add block-header to all h1 tags" do
|
14
|
+
Juici::Controllers::Index.new().about do |template, opts|
|
15
|
+
opts[:content].should match(/class="block-header"/)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "support" do
|
21
|
+
it "should render support" do
|
22
|
+
Juici::Controllers::Index.new().support do |template, opts|
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Juici::Build do
|
4
|
+
|
5
|
+
describe "Time elapsed" do
|
6
|
+
it "Should have the total duration if complete" do
|
7
|
+
|
8
|
+
build = Juici::Build.new
|
9
|
+
Time.stubs(:now).returns(Time.at(5000)).then.returns(Time.at(5005))
|
10
|
+
build.start!
|
11
|
+
build.success!
|
12
|
+
|
13
|
+
build.time_elapsed.to_i.should == 5
|
14
|
+
end
|
15
|
+
|
16
|
+
it "Should have the time running if started" do
|
17
|
+
build = Juici::Build.new
|
18
|
+
Time.stubs(:now).returns(Time.at(5000)).then.returns(Time.at(5006))
|
19
|
+
build.start!
|
20
|
+
|
21
|
+
build.time_elapsed.to_i.should == 6
|
22
|
+
end
|
23
|
+
|
24
|
+
it "Should be nil if not started" do
|
25
|
+
build = Juici::Build.new
|
26
|
+
build.time_elapsed.should be_nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it "Should clone itself with #new_from" do
|
31
|
+
values = {}
|
32
|
+
Juici::Build::CLONABLE_FIELDS.each do |k|
|
33
|
+
case k
|
34
|
+
when :environment
|
35
|
+
values[k] = {:something => "#{k}_value"}
|
36
|
+
else
|
37
|
+
values[k] = "#{k}_value"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
build = Juici::Build.new(values)
|
42
|
+
build[:output] = "Lol, I has an output"
|
43
|
+
build[:buffer] = "/tmp/buffer/lol"
|
44
|
+
new_build = Juici::Build.new_from(build)
|
45
|
+
|
46
|
+
Juici::Build::CLONABLE_FIELDS.each do |k|
|
47
|
+
build[k].should == new_build[k]
|
48
|
+
end
|
49
|
+
|
50
|
+
build[:_id].should_not == new_build[:_id]
|
51
|
+
new_build[:output].should be_nil
|
52
|
+
new_build[:buffer].should be_nil
|
53
|
+
end
|
54
|
+
end
|