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.
Files changed (76) hide show
  1. data/.gitignore +2 -0
  2. data/Gemfile +5 -0
  3. data/Gemfile.lock +74 -0
  4. data/Procfile +1 -0
  5. data/README.md +138 -0
  6. data/Rakefile +33 -0
  7. data/TODO.md +28 -0
  8. data/bin/juici +7 -0
  9. data/bin/juicic +54 -0
  10. data/config/mongoid.yml.sample +25 -0
  11. data/juici-interface.gemspec +19 -0
  12. data/juici.gemspec +32 -0
  13. data/lib/juici.rb +24 -0
  14. data/lib/juici/app.rb +67 -0
  15. data/lib/juici/build_environment.rb +38 -0
  16. data/lib/juici/build_logic.rb +37 -0
  17. data/lib/juici/build_queue.rb +111 -0
  18. data/lib/juici/callback.rb +26 -0
  19. data/lib/juici/config.rb +11 -0
  20. data/lib/juici/controllers.rb +6 -0
  21. data/lib/juici/controllers/base.rb +26 -0
  22. data/lib/juici/controllers/build_queue.rb +14 -0
  23. data/lib/juici/controllers/builds.rb +74 -0
  24. data/lib/juici/controllers/index.rb +20 -0
  25. data/lib/juici/controllers/trigger.rb +85 -0
  26. data/lib/juici/database.rb +25 -0
  27. data/lib/juici/exceptions.rb +2 -0
  28. data/lib/juici/find_logic.rb +11 -0
  29. data/lib/juici/helpers/form_helpers.rb +11 -0
  30. data/lib/juici/helpers/html_helpers.rb +4 -0
  31. data/lib/juici/helpers/url_helpers.rb +38 -0
  32. data/lib/juici/interface.rb +13 -0
  33. data/lib/juici/models/build.rb +190 -0
  34. data/lib/juici/models/build_process.rb +7 -0
  35. data/lib/juici/models/project.rb +9 -0
  36. data/lib/juici/server.rb +172 -0
  37. data/lib/juici/version.rb +8 -0
  38. data/lib/juici/views/README.markdown +138 -0
  39. data/lib/juici/views/about.erb +16 -0
  40. data/lib/juici/views/builds.erb +7 -0
  41. data/lib/juici/views/builds/edit.erb +23 -0
  42. data/lib/juici/views/builds/list.erb +27 -0
  43. data/lib/juici/views/builds/new.erb +43 -0
  44. data/lib/juici/views/builds/show.erb +4 -0
  45. data/lib/juici/views/index.erb +30 -0
  46. data/lib/juici/views/layout.erb +44 -0
  47. data/lib/juici/views/not_found.erb +3 -0
  48. data/lib/juici/views/partials/builds/debug.erb +22 -0
  49. data/lib/juici/views/partials/builds/output.erb +1 -0
  50. data/lib/juici/views/partials/builds/show.erb +19 -0
  51. data/lib/juici/views/partials/builds/sidebar.erb +13 -0
  52. data/lib/juici/views/partials/index/recently_built.erb +19 -0
  53. data/lib/juici/views/queue/list.erb +6 -0
  54. data/lib/juici/views/redirect.erb +0 -0
  55. data/lib/juici/views/support.erb +6 -0
  56. data/lib/juici/watcher.rb +48 -0
  57. data/public/favicon.ico +0 -0
  58. data/public/images/black_denim.png +0 -0
  59. data/public/styles/builds.css +62 -0
  60. data/public/styles/juici.css +226 -0
  61. data/public/vendor/bootstrap.css +6004 -0
  62. data/public/vendor/bootstrap.js +2036 -0
  63. data/public/vendor/img/glyphicons-halflings-white.png +0 -0
  64. data/public/vendor/jquery.js +9440 -0
  65. data/sample/mongod.conf +5 -0
  66. data/script/cibuild +10 -0
  67. data/spec/build_callback_spec.rb +54 -0
  68. data/spec/build_environment_spec.rb +53 -0
  69. data/spec/build_process_spec.rb +96 -0
  70. data/spec/build_queue_spec.rb +63 -0
  71. data/spec/controllers/builds_spec.rb +68 -0
  72. data/spec/controllers/index_spec.rb +28 -0
  73. data/spec/juici_app_spec.rb +8 -0
  74. data/spec/models/build_spec.rb +54 -0
  75. data/spec/spec_helper.rb +26 -0
  76. metadata +290 -0
@@ -0,0 +1,2 @@
1
+ *.gem
2
+ config/mongoid.yml
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source :rubygems
2
+
3
+ ruby '1.9.3'
4
+
5
+ gemspec :name => "juici"
@@ -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
@@ -0,0 +1 @@
1
+ web: RACK_ENV=heroku bundle exec ruby bin/juici
@@ -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.
@@ -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
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.expand_path("../../lib", __FILE__))
3
+ require 'juici'
4
+
5
+ port = ENV['PORT'] || 9000
6
+
7
+ Juici::Server.start('localhost', port)
@@ -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
+
@@ -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
+
@@ -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