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/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
juici (0.0.0)
|
5
|
+
ansible
|
6
|
+
bson_ext
|
7
|
+
github-markdown
|
8
|
+
json
|
9
|
+
mongoid
|
10
|
+
sinatra
|
11
|
+
thin
|
12
|
+
|
13
|
+
GEM
|
14
|
+
remote: http://rubygems.org/
|
15
|
+
specs:
|
16
|
+
activemodel (3.2.8)
|
17
|
+
activesupport (= 3.2.8)
|
18
|
+
builder (~> 3.0.0)
|
19
|
+
activesupport (3.2.8)
|
20
|
+
i18n (~> 0.6)
|
21
|
+
multi_json (~> 1.0)
|
22
|
+
ansible (0.2.0)
|
23
|
+
bson (1.7.0)
|
24
|
+
bson_ext (1.7.0)
|
25
|
+
bson (~> 1.7.0)
|
26
|
+
builder (3.0.4)
|
27
|
+
daemons (1.1.9)
|
28
|
+
diff-lcs (1.1.3)
|
29
|
+
eventmachine (1.0.0)
|
30
|
+
github-markdown (0.5.3)
|
31
|
+
i18n (0.6.1)
|
32
|
+
json (1.7.5)
|
33
|
+
metaclass (0.0.1)
|
34
|
+
mocha (0.11.4)
|
35
|
+
metaclass (~> 0.0.1)
|
36
|
+
mongoid (3.0.10)
|
37
|
+
activemodel (~> 3.1)
|
38
|
+
moped (~> 1.1)
|
39
|
+
origin (~> 1.0)
|
40
|
+
tzinfo (~> 0.3.22)
|
41
|
+
moped (1.2.7)
|
42
|
+
multi_json (1.3.7)
|
43
|
+
origin (1.0.10)
|
44
|
+
rack (1.4.1)
|
45
|
+
rack-protection (1.2.0)
|
46
|
+
rack
|
47
|
+
rake (0.9.2.2)
|
48
|
+
rspec (2.10.0)
|
49
|
+
rspec-core (~> 2.10.0)
|
50
|
+
rspec-expectations (~> 2.10.0)
|
51
|
+
rspec-mocks (~> 2.10.0)
|
52
|
+
rspec-core (2.10.1)
|
53
|
+
rspec-expectations (2.10.0)
|
54
|
+
diff-lcs (~> 1.1.3)
|
55
|
+
rspec-mocks (2.10.1)
|
56
|
+
sinatra (1.3.3)
|
57
|
+
rack (~> 1.3, >= 1.3.6)
|
58
|
+
rack-protection (~> 1.2)
|
59
|
+
tilt (~> 1.3, >= 1.3.3)
|
60
|
+
thin (1.5.0)
|
61
|
+
daemons (>= 1.0.9)
|
62
|
+
eventmachine (>= 0.12.6)
|
63
|
+
rack (>= 1.0.0)
|
64
|
+
tilt (1.3.3)
|
65
|
+
tzinfo (0.3.34)
|
66
|
+
|
67
|
+
PLATFORMS
|
68
|
+
ruby
|
69
|
+
|
70
|
+
DEPENDENCIES
|
71
|
+
juici!
|
72
|
+
mocha
|
73
|
+
rake
|
74
|
+
rspec
|
data/Procfile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
web: RACK_ENV=heroku bundle exec ruby bin/juici
|
data/README.md
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
## JuiCI
|
2
|
+
|
3
|
+
JuiCI is a CI server that has a notion of queuing and priority.
|
4
|
+
|
5
|
+
It's designed to work well with [agent99](https://github.com/99designs/agent99) but will play nicely with most frontends to CI.
|
6
|
+
|
7
|
+
## Features
|
8
|
+
|
9
|
+
* callbacks are created as builds are requested
|
10
|
+
* Builds are executed sequentially in a series of parallel queues.
|
11
|
+
* Queues can be dynamically created
|
12
|
+
* Build status visualised
|
13
|
+
|
14
|
+
## Important but Miscellaneous
|
15
|
+
|
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
|
+
|
19
|
+
## Setup
|
20
|
+
|
21
|
+
JuiCI is deliberately very light on the setup front.
|
22
|
+
|
23
|
+
```bash
|
24
|
+
bundle install
|
25
|
+
bundle exec bin/juici
|
26
|
+
```
|
27
|
+
|
28
|
+
is all you need to have a working instance (provided that you have mongo installed)
|
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
|
+
|
43
|
+
## Usage
|
44
|
+
|
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)
|
50
|
+
|
51
|
+
Example:
|
52
|
+
|
53
|
+
```bash
|
54
|
+
curl --data-ascii @/dev/stdin <<EOF
|
55
|
+
payload={"environment":{
|
56
|
+
"SHA1":"e8b179f75bbc8717c948af052353424d458af981"},
|
57
|
+
"command":"[ -d .git ] || (git init .; git remote add origin git://github.com/richo/twat.git); git fetch; git checkout $SHA1; bundle install; bundle exec rake spec"
|
58
|
+
EOF
|
59
|
+
```
|
60
|
+
|
61
|
+
Using a convention like `script/cibuild` as in janky/hubot etc is advisable,
|
62
|
+
although bear in mind that the logic to checkout the repo will need to be
|
63
|
+
seperate.
|
64
|
+
|
65
|
+
## Priority
|
66
|
+
|
67
|
+
JuiCI supports the notion of priority. Builds given without a priority will be
|
68
|
+
assigned priority 1 (to allow for marking a build as less important with
|
69
|
+
priority 0).
|
70
|
+
|
71
|
+
If juici recieves a new build with priority higher than any currently
|
72
|
+
unfinished, it will pause whatever it's doing and build the new project. If
|
73
|
+
there is a tie for priority, a FIFO queue is assumed.
|
74
|
+
|
75
|
+
JuiCI uses `SIGSTOP` and `SIGCONT` internally for job control.
|
76
|
+
|
77
|
+
## Hooks
|
78
|
+
|
79
|
+
You may specify one or more callbacks when you request a build. They will be
|
80
|
+
called with an (as yet unformalised) json body as the body if/when the build
|
81
|
+
reaches that state. Alternately you may specify "any" as the callback state and
|
82
|
+
it will be called on all state changes.
|
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
|
+
|
98
|
+
## Security
|
99
|
+
|
100
|
+
JuiCI poses some interesting security conecerns. First off, it will allow
|
101
|
+
anyone with access to run arbitrary commands on your server. I have
|
102
|
+
deliberately not implemented any kind of security inside JuiCI, it plays nicely
|
103
|
+
as a Rack application, and middlewares are much better suited to this task.
|
104
|
+
|
105
|
+
It should go without saying, but any builds started by JuiCI will inherit its
|
106
|
+
environment. This means that if you run it in dev mode and forward your ssh
|
107
|
+
agent, builds can ssh to other machines as you!
|
108
|
+
|
109
|
+
When running in production you should take steps to ensure that the user JuiCI
|
110
|
+
runs as is no more privileged than it needs to be, and sanitise its
|
111
|
+
environment before execution.
|
112
|
+
|
113
|
+
## A note on subprocesses
|
114
|
+
|
115
|
+
JuiCI by default invokes everything in a subshell- indeed this is the only way
|
116
|
+
to approach this if you want to execute more than one command.
|
117
|
+
|
118
|
+
What this means to you as the user though is that unless you go to lengths to
|
119
|
+
specifically implement it, your process won't see any of the signal handling
|
120
|
+
madness. The shell(`/bin/sh`) will see everything, and if killed, your
|
121
|
+
processes will become orphaned, but carry on.
|
122
|
+
|
123
|
+
## Authors
|
124
|
+
|
125
|
+
* [Richo Healey](https://github.com/rcho)
|
126
|
+
* [Alec Sloman](https://github.com/alecsloman)
|
127
|
+
|
128
|
+
## Contact
|
129
|
+
|
130
|
+
JuiCI's code lives on [Github](https://github.com/richo/juici)
|
131
|
+
and the [author](mailto:richo@psych0tik.net) can be contacted on
|
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/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
|
4
|
+
require 'juici/database'
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
7
|
+
t.pattern = "spec/**/*_spec.rb"
|
8
|
+
end
|
9
|
+
|
10
|
+
desc 'Default: run specs'
|
11
|
+
task :default => :spec
|
12
|
+
|
13
|
+
namespace :db do
|
14
|
+
desc "Destroy the test db specified in mongoid.yml"
|
15
|
+
task :destroy do
|
16
|
+
Juici::Database.initialize!
|
17
|
+
Mongoid.purge!
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "Build all gems"
|
22
|
+
task :gems do
|
23
|
+
%w[juici juici-interface].each do |gem|
|
24
|
+
`gem build #{gem}.gemspec`
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Delete all built gems"
|
29
|
+
task :clean do
|
30
|
+
Dir["juici-*.gem"].each do |gem|
|
31
|
+
File.unlink(gem)
|
32
|
+
end
|
33
|
+
end
|
data/TODO.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
* This awkward `::Juici::Model` kludge inside views is a pain.
|
2
|
+
|
3
|
+
* RSS feed for build status (spyware)
|
4
|
+
|
5
|
+
* Paginated builds to avoid blocking up a connection
|
6
|
+
|
7
|
+
* Proper callback support
|
8
|
+
|
9
|
+
* Graphing of build times and statuses
|
10
|
+
|
11
|
+
* Search
|
12
|
+
|
13
|
+
## Only child model
|
14
|
+
|
15
|
+
`lib/juici/watcher +21`
|
16
|
+
|
17
|
+
Basically, it would be nice to have a watcher get started with the first build
|
18
|
+
out of the ranks, and then die on ECHILD
|
19
|
+
|
20
|
+
This means that with lots of builds passing through we can retain a single
|
21
|
+
watcher, and when we're idle we let him die and then spawn a new one when we
|
22
|
+
need
|
23
|
+
|
24
|
+
* Neat method of either resuming or starting a build
|
25
|
+
|
26
|
+
* RSS api
|
27
|
+
|
28
|
+
* Import of Xdefaults/itermcolors.plist for output colors
|
data/bin/juici
ADDED
data/bin/juicic
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'net/http'
|
3
|
+
require 'json'
|
4
|
+
require 'juici/interface'
|
5
|
+
|
6
|
+
# TODO Refactor
|
7
|
+
def main(args)
|
8
|
+
action = args.shift.to_sym
|
9
|
+
options = {}
|
10
|
+
|
11
|
+
until args.empty?
|
12
|
+
case args.shift
|
13
|
+
when "--command"
|
14
|
+
command = args.shift
|
15
|
+
options[:command] = if command == "-"
|
16
|
+
File.read(command)
|
17
|
+
else
|
18
|
+
command
|
19
|
+
end
|
20
|
+
when "--host"
|
21
|
+
options[:host] = args.shift
|
22
|
+
when "--title"
|
23
|
+
options[:title] = args.shift
|
24
|
+
when "--project"
|
25
|
+
options[:project] = args.shift
|
26
|
+
when "--priority"
|
27
|
+
options[:priority] = args.shift
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
send(action, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def build(opts)
|
35
|
+
host = URI(opts[:host])
|
36
|
+
Net::HTTP.start(host.host, host.port) do |h|
|
37
|
+
req = Net::HTTP::Post.new(Juici::Routes::NEW_BUILD)
|
38
|
+
req.body = _create_payload(opts)
|
39
|
+
h.request req
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def _create_payload(opts)
|
44
|
+
URI.encode_www_form({
|
45
|
+
"project" => opts[:project],
|
46
|
+
"environment" => (opts[:environment] || {}).to_json,
|
47
|
+
"command" => opts[:command],
|
48
|
+
"priority" => opts[:priority] || 1,
|
49
|
+
"callbacks" => (opts[:callbacks] || []).to_json,
|
50
|
+
"title" => opts[:title]
|
51
|
+
})
|
52
|
+
end
|
53
|
+
|
54
|
+
main(ARGV.dup)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
development:
|
2
|
+
sessions:
|
3
|
+
default:
|
4
|
+
database: juici
|
5
|
+
hosts:
|
6
|
+
- localhost:27017
|
7
|
+
|
8
|
+
production:
|
9
|
+
sessions:
|
10
|
+
default:
|
11
|
+
database: juici
|
12
|
+
hosts:
|
13
|
+
- localhost:27017
|
14
|
+
|
15
|
+
test:
|
16
|
+
sessions:
|
17
|
+
default:
|
18
|
+
database: juici-test
|
19
|
+
hosts:
|
20
|
+
- localhost:27017
|
21
|
+
|
22
|
+
heroku:
|
23
|
+
sessions:
|
24
|
+
default:
|
25
|
+
uri: <%= ENV['MONGOLAB_URI'] %>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# vim: ft=ruby
|
2
|
+
#
|
3
|
+
require File.expand_path("../lib/juici/version", __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "juici-interface"
|
7
|
+
s.version = Juici::VERSION
|
8
|
+
s.authors = ["Richo Healey"]
|
9
|
+
s.email = ["richo@psych0tik.net"]
|
10
|
+
s.homepage = "http://github.com/richo/juici"
|
11
|
+
s.summary = "Interface definition for JuiCI callbacks and API"
|
12
|
+
s.description = s.summary
|
13
|
+
|
14
|
+
s.files = "lib/juici/interface.rb"
|
15
|
+
s.require_paths = ["lib"]
|
16
|
+
s.executables = "juicic"
|
17
|
+
end
|
18
|
+
|
19
|
+
|
data/juici.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# vim: ft=ruby
|
2
|
+
#
|
3
|
+
require File.expand_path("../lib/juici/version", __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "juici"
|
7
|
+
s.version = Juici::VERSION
|
8
|
+
s.authors = ["Richo Healey"]
|
9
|
+
s.email = ["richo@psych0tik.net"]
|
10
|
+
s.homepage = "http://github.com/richo/juici"
|
11
|
+
s.summary = "Minimal CI server with some support for dynamic"
|
12
|
+
s.description = s.summary
|
13
|
+
|
14
|
+
s.add_dependency "sinatra"
|
15
|
+
s.add_dependency "thin"
|
16
|
+
s.add_dependency "json"
|
17
|
+
s.add_dependency "mongoid"
|
18
|
+
s.add_dependency "bson_ext"
|
19
|
+
s.add_dependency "github-markdown"
|
20
|
+
s.add_dependency "ansible"
|
21
|
+
|
22
|
+
s.add_development_dependency "rake"
|
23
|
+
s.add_development_dependency "mocha"
|
24
|
+
s.add_development_dependency "rspec"
|
25
|
+
|
26
|
+
s.files = `git ls-files`.split("\n")
|
27
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
28
|
+
s.executables = 'juici'
|
29
|
+
s.require_paths = ["lib"]
|
30
|
+
end
|
31
|
+
|
32
|
+
|
data/lib/juici.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'github/markdown'
|
2
|
+
require 'ansible'
|
3
|
+
|
4
|
+
ENV['RACK_ENV'] ||= "development"
|
5
|
+
|
6
|
+
module Juici
|
7
|
+
def self.dbgp(*args)
|
8
|
+
if ENV['JUICI_DEBUG'] || env == "development"
|
9
|
+
$stderr.puts(args)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.env
|
14
|
+
ENV['JUICI_ENV'] || ENV['RACK_ENV']
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Load juici core, followed by extras
|
19
|
+
["", "controllers", "models"].each do |el|
|
20
|
+
Dir[File.dirname(__FILE__) + "/juici/#{el}/*.rb"].each do |file|
|
21
|
+
Juici.dbgp "Loading #{file}"
|
22
|
+
require file
|
23
|
+
end
|
24
|
+
end
|