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.
Files changed (69) hide show
  1. data/.gitignore +2 -0
  2. data/Gemfile +4 -1
  3. data/Gemfile.lock +26 -16
  4. data/README.md +45 -6
  5. data/Rakefile +24 -0
  6. data/bin/juicic +54 -0
  7. data/config/mongoid.yml.sample +20 -5
  8. data/juici-interface.gemspec +19 -0
  9. data/juici.gemspec +7 -3
  10. data/lib/juici.rb +8 -3
  11. data/lib/juici/app.rb +19 -23
  12. data/lib/juici/build_environment.rb +1 -1
  13. data/lib/juici/build_logic.rb +10 -1
  14. data/lib/juici/build_queue.rb +30 -3
  15. data/lib/juici/callback.rb +9 -5
  16. data/lib/juici/config.rb +4 -0
  17. data/lib/juici/controllers.rb +6 -0
  18. data/lib/juici/controllers/base.rb +26 -0
  19. data/lib/juici/controllers/build_queue.rb +14 -0
  20. data/lib/juici/controllers/builds.rb +74 -0
  21. data/lib/juici/controllers/index.rb +20 -0
  22. data/lib/juici/controllers/{trigger_controller.rb → trigger.rb} +24 -5
  23. data/lib/juici/database.rb +19 -13
  24. data/lib/juici/exceptions.rb +2 -0
  25. data/lib/juici/find_logic.rb +11 -0
  26. data/lib/juici/helpers/form_helpers.rb +11 -0
  27. data/lib/juici/helpers/html_helpers.rb +4 -0
  28. data/lib/juici/helpers/url_helpers.rb +28 -0
  29. data/lib/juici/interface.rb +13 -0
  30. data/lib/juici/models/build.rb +81 -24
  31. data/lib/juici/models/project.rb +1 -1
  32. data/lib/juici/server.rb +103 -40
  33. data/lib/juici/version.rb +8 -0
  34. data/lib/juici/views/README.markdown +45 -6
  35. data/lib/juici/views/about.erb +5 -5
  36. data/lib/juici/views/builds/edit.erb +23 -0
  37. data/lib/juici/views/builds/list.erb +26 -33
  38. data/lib/juici/views/builds/new.erb +42 -37
  39. data/lib/juici/views/builds/show.erb +4 -28
  40. data/lib/juici/views/index.erb +30 -13
  41. data/lib/juici/views/layout.erb +22 -13
  42. data/lib/juici/views/not_found.erb +3 -0
  43. data/lib/juici/views/partials/builds/debug.erb +12 -18
  44. data/lib/juici/views/partials/builds/output.erb +1 -0
  45. data/lib/juici/views/partials/builds/show.erb +19 -0
  46. data/lib/juici/views/partials/builds/sidebar.erb +13 -0
  47. data/lib/juici/views/partials/index/recently_built.erb +19 -0
  48. data/lib/juici/views/queue/list.erb +6 -0
  49. data/lib/juici/watcher.rb +33 -25
  50. data/public/favicon.ico +0 -0
  51. data/public/images/black_denim.png +0 -0
  52. data/public/styles/builds.css +59 -8
  53. data/public/styles/juici.css +226 -2
  54. data/public/vendor/bootstrap.css +6004 -0
  55. data/public/vendor/bootstrap.js +2036 -0
  56. data/public/vendor/img/glyphicons-halflings-white.png +0 -0
  57. data/public/vendor/jquery.js +9440 -0
  58. data/script/cibuild +10 -0
  59. data/spec/build_callback_spec.rb +46 -1
  60. data/spec/build_process_spec.rb +71 -5
  61. data/spec/build_queue_spec.rb +3 -1
  62. data/spec/controllers/builds_spec.rb +68 -0
  63. data/spec/controllers/index_spec.rb +28 -0
  64. data/spec/juici_app_spec.rb +0 -15
  65. data/spec/models/build_spec.rb +54 -0
  66. data/spec/spec_helper.rb +13 -0
  67. metadata +76 -12
  68. data/lib/juici/controllers/build_controller.rb +0 -0
  69. data/lib/juici/url_helpers.rb +0 -15
@@ -1,28 +1,4 @@
1
- <% if proj = ::Juici::Project.where(name: project).first %>
2
- <h3><em><%= proj.name %></em> - <a href="/builds/new?project=<%= proj.name %>"><em>New Build</em></a></h3>
3
- <% build =::Juici::Build.where(parent: proj.name, _id: id).first %>
4
- <div class="accordion" id="accordion0">
5
- <div class="accordion-heading">
6
- <a class="accordion-toggle <%= build.heading_color %>" data-toggle="collapse" data-parent="#accordion0" href="#collapse_0">
7
- <%= build.create_time %>&nbsp;<% build[:warnings].each do |warning| %><span class="label label-important"><%= warning %></span><% end %>
8
- </a>
9
- </div>
10
- <div id="collapse_0" class="accordion-body collapse in">
11
- <div class="row">
12
- <div class="span8">
13
- <div>
14
- <a href="/builds/<%= proj.name %>/show/<%= build[:_id] %>">Link to this build</a>
15
- </div>
16
- <div class="accordion-inner build-output">
17
- <%= build.output if build.respond_to? :output %>
18
- </div>
19
- </div>
20
- <div class="span4">
21
- <%= erb(:"partials/builds/debug", :locals => { :build => build }) %>
22
- </div>
23
- </div>
24
- </div>
25
- </div>
26
- <% else %>
27
- No such project.
28
- <% end %>
1
+ <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>
2
+ <div class="accordion" id="accordion0">
3
+ <%= erb(:"partials/builds/show", :locals => { :build => build, :idx => 0, :show => true }) %>
4
+ </div>
@@ -1,13 +1,30 @@
1
- <h1>JuiCI</h1>
2
- <p>
3
- JuiCI is a CI server, written at RailsCamp after Jenkins left me feeling a little dead inside. JuiCI aims to solve some of the issues with existing (and outstanding) CI solutions like <a href="http://jenkins-ci.org/">Jenkins</a> and <a href="http://travis-ci.org">Travis</a>, by not trying to be all things to all people.
4
- </p>
5
- <p>
6
- JuiCI has a very thin wrapper around build triggers, is almost entirely API driven, and has a queuing system with a notion of priority, globally shared across all projects.
7
- </p>
8
- <p>
9
- In the past, this instance of Juici has built code for:
10
- <ul><% ::Juici::Project.all.each do |p| %>
11
- <li><a href="<%= build_url_for(p) %>"><%= p.name %></a></li>
12
- <% end %></ul>
13
- </p>
1
+ <div class="row-fluid">
2
+ <div class="span8">
3
+ <h1 class="block-header">JuiCI</h1>
4
+ <p>
5
+ JuiCI is a CI server, written at RailsCamp after Jenkins left me feeling a little dead inside. JuiCI aims to solve some of the issues with existing (and outstanding) CI solutions like <a href="http://jenkins-ci.org/">Jenkins</a> and <a href="http://travis-ci.org">Travis</a>, by not trying to be all things to all people.
6
+ </p>
7
+ <p>
8
+ JuiCI has a very thin wrapper around build triggers, is almost entirely API driven, and has a queuing system with a notion of priority, globally shared across all projects.
9
+ </p>
10
+ <div>
11
+ In the past, this instance of Juici has built code for:
12
+ <ul>
13
+ <% ::Juici::Project.all.each do |p| %>
14
+ <li><a href="<%= build_url_for(p) %>"><%= p.name %></a></li>
15
+ <% end %>
16
+ </ul>
17
+ </div>
18
+ </div>
19
+ <div class="span4">
20
+ <h4 class="block-header builds-header">currently building</h4>
21
+ <ul class="builds">
22
+ <% $build_queue.currently_building.each do |build| %>
23
+ <li>
24
+ <a class="<%= build.heading_color %>" href="<%= build_url_for(build) %>"><%= build.link_title %></a>
25
+ </li>
26
+ <% end %>
27
+ </ul>
28
+ <%= erb(:"partials/index/recently_built") %>
29
+ </div>
30
+ </div>
@@ -1,34 +1,43 @@
1
1
  <!doctype html>
2
2
  <html>
3
3
  <head>
4
- <link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.1.1/css/bootstrap.min.css" rel="stylesheet">
5
- <link href="/styles/juici.css" rel="stylesheet">
6
- <% if @page # This will 404 a ton of page XXX %>
7
- <link href="/styles/<%= @page %>.css" rel="stylesheet">
8
- <% end %>
4
+ <meta charset="UTF-8">
5
+ <% if ::Juici.env == "development" %>
6
+ <link href="/vendor/bootstrap.css" rel="stylesheet">
7
+ <script src="/vendor/jquery.js"></script>
8
+ <script src="/vendor/bootstrap.js"></script>
9
+ <% else %>
10
+ <link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/css/bootstrap.min.css" rel="stylesheet">
9
11
  <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
10
- <script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.1.1/js/bootstrap.min.js"></script>
12
+ <script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/js/bootstrap.min.js"></script>
13
+ <% end %>
14
+ <link href="/styles/juici.css" rel="stylesheet">
15
+ <% styles.each do |style| %>
16
+ <link href="/styles/<%= style %>.css" rel="stylesheet">
17
+ <% end rescue nil%>
11
18
  <title>JuiCI</title>
12
19
  <% if @redirect_to %>
13
- <meta http-equiv="Refresh" content="0; url=<%= URI.escape(@redirect_to) %>" />
20
+ <meta http-equiv="Refresh" content="0; url=<%= @redirect_to %>" />
14
21
  <% end %>
15
22
  </head>
16
23
  <body>
17
24
  <div class="navbar navbar-fixed-top">
18
25
  <div class="navbar-inner">
19
- <div class="container">
26
+ <div class="container-fluid">
20
27
  <div class="nav-collapse">
21
28
  <ul class="nav">
22
- <li class="<%= "active" if @page == :index %>"><a class="brand" href="/">JuiCI</a></li>
23
- <li class="<%= "active" if @page == :builds && @action == :new %>"><a href="/builds/new">New Build</a></li>
24
- <li class="<%= "active" if @page == :about %>"><a href="/about">About</a></li>
25
- <li class="<%= "active" if @page == :support %>"><a href="/support">Support</a></li>
29
+ <% active rescue active = nil %>
30
+ <li class="main <%= "active" if active == :index %>"><a href="/">JuiCI</a></li>
31
+ <li class="<%= "active" if active == :new_build %>"><a href="/builds/new">New Build</a></li>
32
+ <li class="<%= "active" if active == :about %>"><a href="/about">About</a></li>
33
+ <li class="<%= "active" if active == :support %>"><a href="/support">Support</a></li>
34
+ <li class="<%= "active" if active == :queue %>"><a href="/queue">Build Queue</a></li>
26
35
  </ul>
27
36
  </div><!--/.nav-collapse -->
28
37
  </div>
29
38
  </div>
30
39
  </div>
31
- <div class="container">
40
+ <div class="container-fluid">
32
41
  <%= yield %>
33
42
  </div>
34
43
  </body>
@@ -0,0 +1,3 @@
1
+ <h3>Page not found!</h3>
2
+
3
+ <pre>[Insert witty 404 joke]</pre>
@@ -1,28 +1,22 @@
1
- <% if build[:environment] %>
2
- <h3>Environment:</h3>
3
- <pre class="prettyprint">
4
- <% build[:environment].each do |k, v| %>
5
- <%= "#{k}=#{v}" %>
6
- <% end %>
7
- </pre>
8
- <% end %>
1
+ <div class="debug-output">
9
2
  <% if build[:priority] %>
10
- <h3>Priority:</h3>
11
- <pre class="prettyprint">
3
+ <h3 class="block-header">priority:</h3>
12
4
  <%= build[:priority] %>
13
- </pre>
14
5
  <% end %>
15
6
  <% if build[:command] %>
16
- <h3>Command:</h3>
17
- <pre class="prettyprint">
7
+ <h3 class="block-header">command:</h3>
18
8
  <%= build[:command] %>
19
- </pre>
9
+ <% end %>
10
+ <% if build[:environment] %>
11
+ <h3 class="block-header">environment:</h3>
12
+ <% build[:environment].reject{|k, v| v.nil?}.each do |k, v| %>
13
+ <p><strong><%="#{k} = " %></strong><%="#{v}" %></p>
14
+ <% end %>
20
15
  <% end %>
21
16
  <% if build[:callbacks] %>
22
- <h3>Callbacks:</h3>
23
- <pre class="prettyprint">
17
+ <h3 class="block-header">callbacks:</h3>
24
18
  <% build[:callbacks].each do |callback| %>
25
- <%= callback %>
19
+ <p><%= callback %><p>
26
20
  <% end %>
27
- </pre>
28
21
  <% end %>
22
+ </div>
@@ -0,0 +1 @@
1
+ <div class="build-output"><%= ansi_escaped(escaped(build.output)) if build.respond_to? :output %></div>
@@ -0,0 +1,19 @@
1
+ <div class="row-fluid">
2
+ <div class="accordion-heading span10">
3
+ <a class="set-url accordion-toggle <%= build.heading_color %>" data-toggle="collapse" data-parent="#accordion<%= idx+1 %>" href="#collapse_<%= idx %>" data-url="<%= build_url_for(build) %>">
4
+ <%= display_title rescue build.display_title %>&nbsp;<% build[:warnings].each do |warning| %><span class="label label-important"><%= warning %></span><% end %>
5
+ <% if build[:status] == :started %>
6
+ <div class="bowlG pull-right"><div class="bowl_ringG"><div class="ball_holderG"><div class="ballG"></div></div></div></div>
7
+ <% end %>
8
+ </a>
9
+ </div>
10
+ <a class="btn span2" href="<%= build_url_for(build) %>">Build Link</a>
11
+ </div>
12
+ <div id="collapse_<%= idx %>" class="accordion-body collapse <%= "in" if show rescue nil %>">
13
+ <div class="row-fluid" style="padding-top:20px">
14
+ <div class="span8">
15
+ <%= erb(:"partials/builds/output", :locals => { :build => build }) %>
16
+ </div>
17
+ <div class="span4"><%= erb(:"partials/builds/sidebar", :locals => { :build => build }) %><%= erb(:"partials/builds/debug", :locals => { :build => build }) %></div>
18
+ </div>
19
+ </div>
@@ -0,0 +1,13 @@
1
+ <div class="build-sidebar">
2
+ <span><%= form_at(rebuild_url_for(build), {}, :submit => "Rebuild") %></span>
3
+ <% if build.status == Juici::BuildStatus::START %>
4
+ <span><%= form_at(kill_url_for(build),
5
+ {:project => build[:parent], :id => build[:_id]},
6
+ :submit => "Kill!") %></span>
7
+ <% elsif build.status == Juici::BuildStatus::WAIT %>
8
+ <span><%= form_at(cancel_url_for(build),
9
+ {:project => build[:parent], :id => build[:_id]},
10
+ :submit => "Cancel") %></span>
11
+ <% end %>
12
+ <span><a class="btn" href="<%= edit_url_for(build) %>">Edit</a></span>
13
+ </div>
@@ -0,0 +1,19 @@
1
+ <h1 class="block-header builds-header builds-header--failure">fail!<span class="pull-right table-flipper">(╯°□°)╯︵ ┻━┻</span></h1>
2
+ <ul class="builds">
3
+ <% ::Juici::Build.get_recent(5, :status.in => [::Juici::BuildStatus::FAIL]).each do |build| %>
4
+ <li>
5
+ <a class="recently-built--fail" href="<%= build_url_for(build) %>"><%= build.link_title %></a>
6
+ </li>
7
+ <% end %>
8
+ </ul>
9
+
10
+ <hr>
11
+
12
+ <h1 class="block-header builds-header builds-header--success">pass!<span class="pull-right table-flipper">┬─┬ノ( º _ ºノ)</span></h1>
13
+ <ul class="builds">
14
+ <% ::Juici::Build.get_recent(5, :status.in => [::Juici::BuildStatus::PASS]).each do |build| %>
15
+ <li>
16
+ <a class="recently-built--pass" href="<%= build_url_for(build) %>"><%= build.link_title %></a>
17
+ </li>
18
+ <% end %>
19
+ </ul>
@@ -0,0 +1,6 @@
1
+ <% builds.each_with_index do |build, idx| %>
2
+ <div class="accordion" id="accordion<%= idx %>">
3
+ <%= erb(:"partials/builds/show",
4
+ :locals => { :build => build, :idx => idx, :display_title => "#{build[:parent]} :: #{build.display_title}"}) %>
5
+ </div>
6
+ <% end %>
@@ -1,39 +1,47 @@
1
1
  module Juici
2
- class Watcher < Thread
2
+ class Watcher
3
3
 
4
- def self.start!
5
- new do
6
- self.mainloop
7
- end
4
+ def self.instance
5
+ @@instance ||= self.new
6
+ end
7
+
8
+ def initialize
9
+ @active = true
8
10
  end
9
11
 
10
- def self.mainloop
11
- #XXX No classvariables ever!
12
- loop do
12
+ def register_handler
13
+ Signal.trap("CHLD") do
13
14
  begin
14
- pid, status = catch_child
15
+ if @active
16
+ pid, status = Process.wait2(-1)
17
+ $build_queue.purge(:pid, OpenStruct.new(:pid => pid))
18
+ ::Juici.dbgp "Trying to find pid: #{pid}"
19
+ handle(pid, status)
20
+ end
15
21
  rescue Errno::ECHILD
16
- # No children available, sleep for a while until some might exist
17
- # TODO: It'd be a nice optimisation to actually die here, and
18
- # optionally start a worker if we're the first child
19
- sleep 5
20
- next
22
+ nil
21
23
  end
22
- next unless pid
23
- build = $build_queue.get_build_by_pid(pid)
24
+ $build_queue.bump! if $build_queue && @active
25
+ end
26
+ end
24
27
 
25
- if status == 0
26
- build.success!
27
- else
28
- build.failure!
29
- end
30
- $build_queue.bump! if $build_queue
28
+ def handle(pid, status)
29
+ build = $build_queue.get_build_by_pid(pid)
30
+
31
+ if status == 0
32
+ build.success!
33
+ else
34
+ build.failure!
31
35
  end
36
+ $build_queue.bump! if $build_queue
37
+ end
38
+
39
+ def shutdown!
40
+ @active = false
32
41
  end
33
42
 
34
- # Hook for testing
35
- def self.catch_child
36
- Process.wait2(-1, Process::WNOHANG)
43
+ def start
44
+ register_handler
37
45
  end
38
46
 
39
47
  end
Binary file
@@ -1,11 +1,62 @@
1
- .build-heading-pending { color: #111 }
2
- :hover .build-heading-pending { color: #111 }
1
+ .build-output {
2
+ font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
3
+ white-space: pre-wrap;
4
+ word-wrap: break-word;
5
+ text-align: left;
6
+ }
3
7
 
4
- .build-heading-failed { color: #C10 }
5
- :hover .build-heading-failed { color: #C10 }
8
+ .dark-background {
9
+ background-color: black !important;
10
+ }
6
11
 
7
- .build-heading-success { color: #4A0 }
8
- :hover .build-heading-success { color: #4A0 }
12
+ .build-output .ansible_none {
13
+ color: #a8a8a8;
14
+ }
15
+
16
+ .build-output .ansible_1 {
17
+ color: #ffffff;
18
+ }
19
+
20
+ .build-output .ansible_30 {
21
+ color: #222222;
22
+ }
23
+ .build-output .ansible_31 {
24
+ color: #a80000;
25
+ }
26
+ .build-output .ansible_32 {
27
+ color: #00a800;
28
+ }
29
+ .build-output .ansible_33 {
30
+ color: #a8a800;
31
+ }
32
+ .build-output .ansible_34 {
33
+ color: #3333a8;
34
+ }
35
+ .build-output .ansible_35 {
36
+ color: #a800a8;
37
+ }
38
+ .build-output .ansible_36 {
39
+ color: #00a8a8;
40
+ }
41
+ .build-output .ansible_30.ansible_1 {
42
+ color: #545454;
43
+ }
44
+ .build-output .ansible_31.ansible_1 {
45
+ color: #ff0054;
46
+ }
47
+ .build-output .ansible_32.ansible_1 {
48
+ color: #00ff54;
49
+ }
50
+ .build-output .ansible_33.ansible_1 {
51
+ color: #ffff54;
52
+ }
53
+ .build-output .ansible_34.ansible_1 {
54
+ color: #5555ff;
55
+ }
56
+ .build-output .ansible_35.ansible_1 {
57
+ color: #ff00ff;
58
+ }
59
+ .build-output .ansible_36.ansible_1 {
60
+ color: #00ffff;
61
+ }
9
62
 
10
- .build-heading-started { color: #CC0 }
11
- :hover .build-heading-started { color: #CC0 }
@@ -1,2 +1,226 @@
1
- body { padding-top: 60px; }
2
- .build-output { white-space: pre-wrap; }
1
+ body { background: black url(../images/black_denim.png); color: #a8a8a8; font-family: "Monaco"; font-size: 12px; padding-top: 60px; }
2
+
3
+ a { color: white; font-weight: bold; }
4
+ a:hover { color: red; text-decoration: none; }
5
+
6
+ .navbar, .navbar-inner, .nav { background: #2dffff !important; border-bottom-color: #00dfe0; }
7
+ .nav > li > a { color: black !important; text-shadow: none !important; }
8
+ .nav > li:hover { background-color: #00dfe0; }
9
+ .nav > li.active > a { background-color: #00dfe0 !important; }
10
+ .accordion-inner {border-top: none;}
11
+ .currently-building { white-space: pre-wrap; word-wrap: break-word; }
12
+ .block-header { font-size: 12px; line-height: 24px; color: black; background-color: #2dffff; font-weight: bold; padding: 0 3px 0 3px; }
13
+ .builds-header { margin: 10px 0 0 0; }
14
+ .builds-header--failure { color: #fff; background-color: #c800c9; }
15
+ .builds-header--success { background-color: #7e9eff; }
16
+ .project-title { margin-top: 0; }
17
+
18
+ /* Builds */
19
+ .table-flipper { color: white; }
20
+ .builds { list-style: none; overflow: auto; padding: 0; margin: 0; }
21
+ .builds a { display: block; font-weight: normal; text-decoration: none; padding: 34px 3px 3px 3px; border-top: 2px solid #000; }
22
+
23
+ .recently-built--fail {
24
+ color: black;
25
+ background-color: #ff7bff;
26
+ padding: 16px 3px 3px 3px !important;
27
+ }
28
+
29
+ .recently-built--fail:hover {
30
+ color: white;
31
+ background-color: #e100e1;
32
+ }
33
+
34
+ .recently-built--pass {
35
+ color: white;
36
+ background-color: #595959;
37
+ padding: 16px 3px 3px 3px !important;
38
+ }
39
+
40
+ .recently-built-pass:hover {
41
+ color: white;
42
+ background-color: #3f3f3f;
43
+ }
44
+
45
+ .submit-build label {
46
+ font-size: 12px;
47
+ color: #8091ff;
48
+ }
49
+
50
+ button, .btn {
51
+ font-size: 12px;
52
+ font-weight: normal;
53
+ border: 2px solid rgba(255,255,255,0.25);
54
+ padding: 5px;
55
+ border-radius: 0;
56
+ background: #8091ff !important;
57
+ text-shadow: none !important;
58
+ color: #000 !important;
59
+ font-family: "Monaco";
60
+ }
61
+
62
+ button:hover, .btn:hover {
63
+ background: red !important;
64
+ color: #FFF;
65
+ }
66
+
67
+ button:before, button:after, .btn:before, .btn:after {
68
+ content: " ** ";
69
+ }
70
+
71
+ .build-heading-started {
72
+ color: #DFFF80;
73
+ border: 2px dashed #DFFF80;
74
+ }
75
+ .build-heading-started:hover {
76
+ color: #ccff33;
77
+ border: 2px dashed #ccff33;
78
+ }
79
+
80
+ .build-heading-failed {
81
+ color: black;
82
+ background-color: #ff7bff;
83
+ }
84
+ .build-heading-failed:hover {
85
+ color: white;
86
+ background-color: #e100e1;
87
+ }
88
+
89
+ .build-heading-success {
90
+ color: white;
91
+ background: #8091ff;
92
+ }
93
+ .build-heading-success:hover {
94
+ color: white;
95
+ background-color: #334fff;
96
+ }
97
+
98
+ .build-heading-pending { color: gray; }
99
+ .build-heading-pending:hover { color: gray; }
100
+
101
+ .build-sidebar {
102
+ height: 48px;
103
+ }
104
+
105
+ .build-sidebar > span {
106
+ float: left;
107
+ }
108
+
109
+ .build-sidebar > span > form > button {
110
+ margin-left: 10px;
111
+ margin-right: 10px;
112
+ }
113
+
114
+ .build-output {
115
+ padding: 20px;
116
+ background-color: black;
117
+ border: 1px solid #2dffff;
118
+ box-shadow: 0 0 2px #2dffff;
119
+ border-radius: 3px;
120
+ }
121
+
122
+ .bloop {
123
+ color: transparent;
124
+ text-decoration: none;
125
+ font-size: 12px;
126
+ font-weight: normal;
127
+ display: block;
128
+ height: 79px;
129
+ width: 20px;
130
+ margin: 3px auto 0 auto;
131
+ background-color: #ff7bff;
132
+ }
133
+ .bloop:hover, .bloop.active { color: transparent; background-color: #c800c9; }
134
+
135
+ .disclosure { display: inline-block; width: 20px; }
136
+ .link-to-build { padding: 10px 0 10px 0; }
137
+
138
+ .bowlG{
139
+ margin-top: 2px;
140
+ position:relative;
141
+ width:14px;
142
+ height:14px;
143
+ }
144
+
145
+ .bowl_ringG{
146
+ position:absolute;
147
+ width:14px;
148
+ height:14px;
149
+ border:1px solid #DFFF80;
150
+ -moz-border-radius:14px;
151
+ -webkit-border-radius:14px;
152
+ border-radius:14px;
153
+ box-shadow: 0 0 3px #DFFF80;
154
+ }
155
+
156
+ .ball_holderG{
157
+ position:absolute;
158
+ width:4px;
159
+ height:14px;
160
+ left:5px;
161
+ top:0px;
162
+ -webkit-animation-name:ball_moveG;
163
+ -webkit-animation-duration:1.8s;
164
+ -webkit-animation-iteration-count:infinite;
165
+ -webkit-animation-timing-function:linear;
166
+ -moz-animation-name:ball_moveG;
167
+ -moz-animation-duration:1.8s;
168
+ -moz-animation-iteration-count:infinite;
169
+ -moz-animation-timing-function:linear;
170
+ -o-animation-name:ball_moveG;
171
+ -o-animation-duration:1.8s;
172
+ -o-animation-iteration-count:infinite;
173
+ -o-animation-timing-function:linear;
174
+ -ms-animation-name:ball_moveG;
175
+ -ms-animation-duration:1.8s;
176
+ -ms-animation-iteration-count:infinite;
177
+ -ms-animation-timing-function:linear;
178
+ }
179
+
180
+ .ballG{
181
+ position:absolute;
182
+ left:0px;
183
+ top:-3px;
184
+ width:6px;
185
+ height:6px;
186
+ background:#DFFF80;
187
+ -moz-border-radius:5px;
188
+ -webkit-border-radius:5px;
189
+ border-radius:5px;
190
+ box-shadow: 0 0 3px #DFFF80;
191
+ }
192
+
193
+ @-webkit-keyframes ball_moveG{
194
+ 0%{
195
+ -webkit-transform:rotate(0deg)}
196
+
197
+ 100%{
198
+ -webkit-transform:rotate(360deg)}
199
+
200
+ }
201
+
202
+ @-moz-keyframes ball_moveG{
203
+ 0%{
204
+ -moz-transform:rotate(0deg)}
205
+
206
+ 100%{
207
+ -moz-transform:rotate(360deg)}
208
+
209
+ }
210
+
211
+ @-o-keyframes ball_moveG{
212
+ 0%{
213
+ -o-transform:rotate(0deg)}
214
+
215
+ 100%{
216
+ -o-transform:rotate(360deg)}
217
+
218
+ }
219
+
220
+ @-ms-keyframes ball_moveG{
221
+ 0%{
222
+ -ms-transform:rotate(0deg)}
223
+
224
+ 100%{
225
+ -ms-transform:rotate(360deg)}
226
+ }