juici 0.0.0 → 0.0.1.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/Gemfile +4 -1
- data/Gemfile.lock +26 -16
- data/README.md +45 -6
- data/Rakefile +24 -0
- data/bin/juicic +54 -0
- data/config/mongoid.yml.sample +20 -5
- data/juici-interface.gemspec +19 -0
- data/juici.gemspec +7 -3
- data/lib/juici.rb +8 -3
- data/lib/juici/app.rb +19 -23
- data/lib/juici/build_environment.rb +1 -1
- data/lib/juici/build_logic.rb +10 -1
- data/lib/juici/build_queue.rb +30 -3
- data/lib/juici/callback.rb +9 -5
- data/lib/juici/config.rb +4 -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_controller.rb → trigger.rb} +24 -5
- data/lib/juici/database.rb +19 -13
- 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 +28 -0
- data/lib/juici/interface.rb +13 -0
- data/lib/juici/models/build.rb +81 -24
- data/lib/juici/models/project.rb +1 -1
- data/lib/juici/server.rb +103 -40
- data/lib/juici/version.rb +8 -0
- data/lib/juici/views/README.markdown +45 -6
- data/lib/juici/views/about.erb +5 -5
- data/lib/juici/views/builds/edit.erb +23 -0
- data/lib/juici/views/builds/list.erb +26 -33
- data/lib/juici/views/builds/new.erb +42 -37
- data/lib/juici/views/builds/show.erb +4 -28
- data/lib/juici/views/index.erb +30 -13
- data/lib/juici/views/layout.erb +22 -13
- data/lib/juici/views/not_found.erb +3 -0
- data/lib/juici/views/partials/builds/debug.erb +12 -18
- 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/watcher.rb +33 -25
- data/public/favicon.ico +0 -0
- data/public/images/black_denim.png +0 -0
- data/public/styles/builds.css +59 -8
- data/public/styles/juici.css +226 -2
- 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/script/cibuild +10 -0
- data/spec/build_callback_spec.rb +46 -1
- data/spec/build_process_spec.rb +71 -5
- data/spec/build_queue_spec.rb +3 -1
- data/spec/controllers/builds_spec.rb +68 -0
- data/spec/controllers/index_spec.rb +28 -0
- data/spec/juici_app_spec.rb +0 -15
- data/spec/models/build_spec.rb +54 -0
- data/spec/spec_helper.rb +13 -0
- metadata +76 -12
- data/lib/juici/controllers/build_controller.rb +0 -0
- data/lib/juici/url_helpers.rb +0 -15
data/lib/juici/models/project.rb
CHANGED
data/lib/juici/server.rb
CHANGED
@@ -11,6 +11,8 @@ module Juici
|
|
11
11
|
end
|
12
12
|
|
13
13
|
helpers do
|
14
|
+
include Ansible
|
15
|
+
|
14
16
|
Dir[File.dirname(__FILE__) + "/helpers/**/*.rb"].each do |file|
|
15
17
|
load file
|
16
18
|
end
|
@@ -19,9 +21,16 @@ module Juici
|
|
19
21
|
dir = File.dirname(File.expand_path(__FILE__))
|
20
22
|
|
21
23
|
def self.start(host, port)
|
22
|
-
|
23
|
-
|
24
|
-
|
24
|
+
@@juici = App.new
|
25
|
+
Juici::Server.run!(:host => host, :port => port) do |server|
|
26
|
+
[:INT, :TERM].each do |sig|
|
27
|
+
trap(sig) do
|
28
|
+
$stderr.puts "Shutting down JuiCI"
|
29
|
+
App.shutdown
|
30
|
+
server.respond_to?(:stop!) ? server.stop! : server.stop
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
25
34
|
end
|
26
35
|
|
27
36
|
def self.rack_start(project_path)
|
@@ -33,76 +42,130 @@ module Juici
|
|
33
42
|
set :static, true
|
34
43
|
|
35
44
|
get '/' do
|
36
|
-
|
37
|
-
|
45
|
+
Controllers::Index.new.index do |template, opts|
|
46
|
+
erb(template, {}, opts)
|
47
|
+
end
|
38
48
|
end
|
39
49
|
|
40
50
|
get '/about' do
|
41
|
-
|
42
|
-
|
43
|
-
|
51
|
+
Controllers::Index.new.about do |template, opts|
|
52
|
+
erb(template, {}, opts)
|
53
|
+
end
|
44
54
|
end
|
45
55
|
|
46
56
|
get '/builds' do
|
47
|
-
|
48
|
-
|
49
|
-
|
57
|
+
Controllers::Index.new.builds do |template, opts|
|
58
|
+
erb(template, {}, opts)
|
59
|
+
end
|
50
60
|
end
|
51
61
|
|
52
|
-
get '/
|
53
|
-
|
54
|
-
|
55
|
-
|
62
|
+
get '/support' do
|
63
|
+
Controllers::Index.new.support do |template, opts|
|
64
|
+
erb(template, {}, opts)
|
65
|
+
end
|
56
66
|
end
|
57
67
|
|
58
68
|
post '/builds/new' do
|
59
|
-
|
60
|
-
@redirect_to = build_url_for(
|
61
|
-
erb(:redirect, {},
|
69
|
+
build = Controllers::Trigger.new(params[:project], params).build!
|
70
|
+
@redirect_to = build_url_for(build)
|
71
|
+
erb(:redirect, {}, {})
|
72
|
+
end
|
73
|
+
|
74
|
+
post '/builds/:project/rebuild/:id' do
|
75
|
+
build = Controllers::Trigger.new(params[:project], params).rebuild!
|
76
|
+
@redirect_to = build_url_for(build)
|
77
|
+
erb(:redirect, {}, {})
|
78
|
+
end
|
79
|
+
|
80
|
+
post '/builds/:user/:project/rebuild/:id' do
|
81
|
+
params[:project] = "#{params[:user]}/#{params[:project]}"
|
82
|
+
build = Controllers::Trigger.new(params[:project], params).rebuild!
|
83
|
+
@redirect_to = build_url_for(build)
|
84
|
+
erb(:redirect, {}, {})
|
62
85
|
end
|
63
86
|
|
64
|
-
|
65
|
-
|
87
|
+
post '/builds/kill' do
|
88
|
+
build = Controllers::Builds.new(params).kill
|
89
|
+
@redirect_to = build_url_for(build)
|
90
|
+
erb(:redirect, {}, {})
|
91
|
+
end
|
92
|
+
|
93
|
+
post '/builds/cancel' do
|
94
|
+
build = Controllers::Builds.new(params).cancel
|
95
|
+
@redirect_to = build_url_for(build)
|
96
|
+
erb(:redirect, {}, {})
|
97
|
+
end
|
98
|
+
|
99
|
+
get ::Juici::Routes::NEW_BUILD do
|
100
|
+
Controllers::Builds.new(params).new do |template, opts|
|
101
|
+
erb(template, {}, opts)
|
102
|
+
end
|
66
103
|
end
|
67
104
|
|
68
105
|
get '/builds/:project/list' do
|
69
|
-
|
70
|
-
|
71
|
-
|
106
|
+
Controllers::Builds.new(params).list do |template, opts|
|
107
|
+
erb(template, {}, opts)
|
108
|
+
end
|
72
109
|
end
|
73
110
|
|
74
111
|
get '/builds/:user/:project/list' do
|
75
|
-
@page = :builds
|
76
|
-
@action = :list
|
77
112
|
params[:project] = "#{params[:user]}/#{params[:project]}"
|
78
|
-
|
113
|
+
Controllers::Builds.new(params).list do |template, opts|
|
114
|
+
erb(template, {}, opts)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
get '/builds/:project/edit/:id' do
|
119
|
+
Controllers::Builds.new(params).edit do |template, opts|
|
120
|
+
erb(template, {}, opts)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
get '/builds/:user/:project/edit/:id' do
|
125
|
+
params[:project] = "#{params[:user]}/#{params[:project]}"
|
126
|
+
Controllers::Builds.new(params).edit do |template, opts|
|
127
|
+
erb(template, {}, opts)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
post '/builds/:project/edit/:id' do
|
132
|
+
build = Controllers::Builds.new(params).update!
|
133
|
+
@redirect_to = build_url_for(build)
|
134
|
+
erb(:redirect, {}, {})
|
79
135
|
end
|
80
136
|
|
81
|
-
|
82
|
-
|
137
|
+
post '/builds/:user/:project/edit/:id' do
|
138
|
+
params[:project] = "#{params[:user]}/#{params[:project]}"
|
139
|
+
build = Controllers::Builds.new(params).update!
|
140
|
+
@redirect_to = build_url_for(build)
|
141
|
+
erb(:redirect, {}, {})
|
83
142
|
end
|
84
143
|
|
85
144
|
get '/builds/:project/show/:id' do
|
86
|
-
|
87
|
-
|
88
|
-
|
145
|
+
Controllers::Builds.new(params).show do |template, opts|
|
146
|
+
erb(template, {}, opts)
|
147
|
+
end
|
89
148
|
end
|
90
149
|
|
91
150
|
get '/builds/:user/:project/show/:id' do
|
92
|
-
@page = :builds
|
93
|
-
@action = :show
|
94
151
|
params[:project] = "#{params[:user]}/#{params[:project]}"
|
95
|
-
|
152
|
+
Controllers::Builds.new(params).show do |template, opts|
|
153
|
+
erb(template, {}, opts)
|
154
|
+
end
|
96
155
|
end
|
97
156
|
|
98
|
-
|
99
|
-
|
100
|
-
@action = :show
|
101
|
-
erb(:"support", {})
|
157
|
+
post '/trigger/:project' do
|
158
|
+
Controllers::Trigger.new(params[:project], params).build!
|
102
159
|
end
|
103
160
|
|
104
|
-
|
105
|
-
|
161
|
+
get '/queue' do
|
162
|
+
Controllers::BuildQueue.new(params).list do |template, opts|
|
163
|
+
erb(template, {}, opts)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
not_found do
|
168
|
+
erb(:not_found, {}, {})
|
106
169
|
end
|
107
170
|
|
108
171
|
end
|
@@ -13,8 +13,8 @@ It's designed to work well with [agent99](https://github.com/99designs/agent99)
|
|
13
13
|
|
14
14
|
## Important but Miscellaneous
|
15
15
|
|
16
|
-
If you create child
|
17
|
-
disinterest or JuiCI will think they're builds and that would be bad
|
16
|
+
If you create child processes in modules/plugins then you need to register your
|
17
|
+
disinterest or JuiCI will think they're builds and that would be bad.
|
18
18
|
|
19
19
|
## Setup
|
20
20
|
|
@@ -27,12 +27,26 @@ bundle exec bin/juici
|
|
27
27
|
|
28
28
|
is all you need to have a working instance (provided that you have mongo installed)
|
29
29
|
|
30
|
+
### Gotchas
|
31
|
+
|
32
|
+
Make sure you don't do something innocuous like
|
33
|
+
|
34
|
+
```bash
|
35
|
+
bundle install --path .bundle
|
36
|
+
```
|
37
|
+
|
38
|
+
this might look sane (and it is, kinda) but owing to a quick in bundler, it
|
39
|
+
will break any ruby code you try to build.
|
40
|
+
|
41
|
+
I'm working on a workaround, but in the meantime the fix is to not do it!
|
42
|
+
|
30
43
|
## Usage
|
31
44
|
|
32
|
-
JuiCI
|
33
|
-
|
34
|
-
|
35
|
-
|
45
|
+
JuiCI is very focused on minimal configuration; meaning that beyond starting
|
46
|
+
the server and pointing it at a mongoDB instance, you do not need to do
|
47
|
+
anything special to build a new project. Just request a build; however this
|
48
|
+
means that on your first build you will need to send the commands to create
|
49
|
+
your test environment)
|
36
50
|
|
37
51
|
Example:
|
38
52
|
|
@@ -67,6 +81,20 @@ called with an (as yet unformalised) json body as the body if/when the build
|
|
67
81
|
reaches that state. Alternately you may specify "any" as the callback state and
|
68
82
|
it will be called on all state changes.
|
69
83
|
|
84
|
+
## Integration
|
85
|
+
|
86
|
+
Apps written in ruby wanting to interact with Juici can include the
|
87
|
+
`juici-interface` gem, which presently exposes a few constants to line up with
|
88
|
+
JuiCI's internal state.
|
89
|
+
Over time this will be expanded, but for now they are:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
Juici::BuildStatus::PASS
|
93
|
+
Juici::BuildStatus::FAIL
|
94
|
+
Juici::BuildStatus::START
|
95
|
+
Juici::BuildStatus::WAIT
|
96
|
+
```
|
97
|
+
|
70
98
|
## Security
|
71
99
|
|
72
100
|
JuiCI poses some interesting security conecerns. First off, it will allow
|
@@ -92,8 +120,19 @@ specifically implement it, your process won't see any of the signal handling
|
|
92
120
|
madness. The shell(`/bin/sh`) will see everything, and if killed, your
|
93
121
|
processes will become orphaned, but carry on.
|
94
122
|
|
123
|
+
## Authors
|
124
|
+
|
125
|
+
* [Richo Healey](https://github.com/rcho)
|
126
|
+
* [Alec Sloman](https://github.com/alecsloman)
|
127
|
+
|
95
128
|
## Contact
|
96
129
|
|
97
130
|
JuiCI's code lives on [Github](https://github.com/richo/juici)
|
98
131
|
and the [author](mailto:richo@psych0tik.net) can be contacted on
|
99
132
|
[Twitter](https://twitter.com/rich0H)
|
133
|
+
|
134
|
+
## Legalese
|
135
|
+
|
136
|
+
(c) Richo Healey 2012, richo@psych0tik.net
|
137
|
+
|
138
|
+
Released under the terms of the MIT license.
|
data/lib/juici/views/about.erb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
<div class="row">
|
2
|
-
<div class="
|
1
|
+
<div class="row-fluid">
|
2
|
+
<div class="span9">
|
3
3
|
<div>
|
4
|
-
<%=
|
4
|
+
<%= content %>
|
5
5
|
</div>
|
6
6
|
</div>
|
7
|
-
<div class="
|
7
|
+
<div class="span2 offset1">
|
8
8
|
<div>
|
9
|
-
<
|
9
|
+
<h3 class="block-header builds-header--failure">quick links</h3>
|
10
10
|
<ul>
|
11
11
|
<li>richo on <a href="https://twitter.com/rich0H">twitter</a></li>
|
12
12
|
<li>richo on <a href="https://github.com/richo">github</a></li>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<h4 class="accordion-toggle ">
|
2
|
+
<%= display_title rescue build.display_title %> <% build[:warnings].each do |warning| %><span class="label label-important"><%= warning %></span><% end %>
|
3
|
+
</h4>
|
4
|
+
<div class="row-fluid" style="padding-top:20px">
|
5
|
+
<div><p>Note that any changes made here will only take effect when this project starts building. Running builds will not inherit these changes, but you'll likey confuse the hell out of anyone trying to debug it.</p></div>
|
6
|
+
<form action="<%= edit_url_for(build) %>" method="post" class="submit-build">
|
7
|
+
<div class="span4">
|
8
|
+
<h3>Misc</h3>
|
9
|
+
<% ::Juici::Build::EDITABLE_ATTRIBUTES[:string].each do |attr| %>
|
10
|
+
<%# TODO css transform to make this pretty %>
|
11
|
+
<label><%= attr %></label>
|
12
|
+
<input type="text" name="<%= attr %>" value="<%= build[attr] %>">
|
13
|
+
<% end %>
|
14
|
+
<button type="submit" class="btn">Submit</button>
|
15
|
+
</div>
|
16
|
+
<div class="span4">
|
17
|
+
<h3>Environment</h3>
|
18
|
+
</div>
|
19
|
+
<div class="span4">
|
20
|
+
<h3>Callbacks</h3>
|
21
|
+
</div>
|
22
|
+
</form>
|
23
|
+
</div>
|
@@ -1,34 +1,27 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
$(
|
4
|
-
$(
|
5
|
-
})
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
1
|
+
<script>
|
2
|
+
$(function() {
|
3
|
+
$('.accordion-set-url').on('click', '.set-url', function() {
|
4
|
+
history.pushState(null, null, $(this).data('url'));
|
5
|
+
})
|
6
|
+
});
|
7
|
+
</script>
|
8
|
+
|
9
|
+
<div class="row-fluid">
|
10
|
+
<!-- Pagination -->
|
11
|
+
<div class="span1">
|
12
|
+
<% pages.times.with_index do |page| %>
|
13
|
+
<a href="?page=<%= page %>" class="bloop<%= " active" if page == params[:page] %>"><%= page %></a>
|
14
|
+
<% end %>
|
15
|
+
</div>
|
16
|
+
<!-- Main contents -->
|
17
|
+
<div class="span11">
|
18
|
+
<!-- Create a new build -->
|
19
|
+
<h3 class="project-title"><a href="<%= build_url_for(project) %>"><%= project.name %></a><a class="btn pull-right" href="/builds/new?project=<%= project.name %>">New Build</a></h3>
|
20
|
+
<!-- Build history -->
|
21
|
+
<% builds.each_with_index do |build, idx| %>
|
22
|
+
<div class="accordion accordion-set-url" id="accordion<%= idx %>">
|
23
|
+
<%= erb(:"partials/builds/show", :locals => { :build => build, :idx => idx }) %>
|
14
24
|
</div>
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
<div>
|
19
|
-
<a href="<%= build_url_for(build) %>">Link to this build</a>
|
20
|
-
</div>
|
21
|
-
<div class="accordion-inner build-output">
|
22
|
-
<%= build.output if build.respond_to? :output %>
|
23
|
-
</div>
|
24
|
-
</div>
|
25
|
-
<div class="span4">
|
26
|
-
<%= erb(:"partials/builds/debug", :locals => { :build => build }) %>
|
27
|
-
</div>
|
28
|
-
</div>
|
29
|
-
</div>
|
30
|
-
</div>
|
31
|
-
<% end %>
|
32
|
-
<% else %>
|
33
|
-
No such project.
|
34
|
-
<% end %>
|
25
|
+
<% end %>
|
26
|
+
</div>
|
27
|
+
</div>
|
@@ -1,38 +1,43 @@
|
|
1
|
-
<div class="row">
|
2
|
-
|
3
|
-
<
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
<
|
10
|
-
<
|
11
|
-
<
|
12
|
-
<
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
<
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
</
|
1
|
+
<div class="row-fluid">
|
2
|
+
|
3
|
+
<div class="span4">
|
4
|
+
<%= erb(:"partials/index/recently_built") %>
|
5
|
+
</div>
|
6
|
+
|
7
|
+
<div class="span8">
|
8
|
+
|
9
|
+
<h1 class="block-header">new</h1>
|
10
|
+
<p>From here you can create a new build</p>
|
11
|
+
<p>Name is a unique identifier for the project. It doesn't need to already exist, but builds for a like project need to be named identically</p>
|
12
|
+
<p>Environment is a json hash of KEY - VALUE pairs, to be passed into the child environment</p>
|
13
|
+
<p>Command is where the magic happens. Often they'll be longwinded, but a reasonable boilerplate might look <a href="#">something like this.</a><p>
|
14
|
+
<hr>
|
15
|
+
|
16
|
+
<form action="/builds/new" method="post" class="submit-build">
|
17
|
+
|
18
|
+
<div class="row-fluid">
|
19
|
+
|
20
|
+
<!-- First Column -->
|
21
|
+
<div class="span3">
|
22
|
+
<label>Project Name</label>
|
23
|
+
<input type="text" name="project" value="<%= params[:project] %>" <%= "readonly" if params[:project] %>>
|
24
|
+
<label>Environment</label>
|
25
|
+
<input type="text" name="environment" placeholder="expects a valid json hash" value="">
|
26
|
+
<label>Priority</label>
|
27
|
+
<input type="text" name="priority" value="1">
|
28
|
+
</div><!-- End first column -->
|
29
|
+
|
30
|
+
<div class="span8 offset1">
|
31
|
+
<label>Command</label>
|
32
|
+
<textarea name="command" class="input-xxlarge" rows="4"></textarea>
|
33
|
+
<%# TODO -> When callbacks get implemented %>
|
34
|
+
<%# <label>Callback URL</label> %>
|
35
|
+
<%# <input type="text" name="callback" class="span3"> %>
|
36
|
+
<!-- <label><%# HACK %></label> -->
|
37
|
+
<button type="submit">START BUILD</button>
|
38
|
+
</div>
|
39
|
+
|
40
|
+
</div>
|
41
|
+
</form>
|
42
|
+
</div>
|
38
43
|
</div>
|