ginst 2.0.1 → 2009.11.23

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 (41) hide show
  1. data/Rakefile +20 -21
  2. data/VERSION +1 -1
  3. data/app/controllers/application_controller.rb +1 -1
  4. data/app/controllers/status_controller.rb +10 -0
  5. data/app/controllers/tasks_controller.rb +9 -7
  6. data/app/helpers/projects_helper.rb +9 -4
  7. data/app/models/project.rb +38 -15
  8. data/app/models/task.rb +15 -3
  9. data/app/views/grit/commits/_commit.html.erb +3 -12
  10. data/app/views/layouts/projects.html.erb +1 -1
  11. data/app/views/status/show.html.erb +6 -1
  12. data/app/views/tasks/_task.html.erb +43 -26
  13. data/app/views/tasks/_task_item.html.erb +22 -0
  14. data/app/views/tasks/index.html.erb +3 -2
  15. data/app/views/tasks/show.html.erb +1 -46
  16. data/config/database.yml +4 -1
  17. data/config/environment.rb +4 -4
  18. data/config/routes.rb +1 -5
  19. data/ginst.gemspec +17 -18
  20. data/lib/ginst.rb +5 -2
  21. data/lib/ginst/builder.rb +2 -0
  22. data/lib/ginst/cli.rb +21 -44
  23. data/lib/ginst/console.rb +9 -0
  24. data/lib/ginst/ginst_template/webserver.ymlt +2 -0
  25. data/lib/ginst/logger.rb +31 -0
  26. data/lib/ginst/template.rb +1 -0
  27. data/lib/ginst/web_server.rb +28 -6
  28. data/log/development.log +49 -0
  29. data/public/stylesheets/screen.css +73 -38
  30. data/script/builder.rb +6 -1
  31. data/{features/step_definitions/ginst_steps.rb → tmp/.gittouch} +0 -0
  32. data/tmp/cache/.gittouch +0 -0
  33. data/tmp/pids/.gittouch +0 -0
  34. data/tmp/sessions/.gittouch +0 -0
  35. data/tmp/sockets/.gittouch +0 -0
  36. metadata +19 -17
  37. data/features/ginst.feature +0 -9
  38. data/features/support/env.rb +0 -4
  39. data/spec/ginst_spec.rb +0 -7
  40. data/spec/spec.opts +0 -1
  41. data/spec/spec_helper.rb +0 -9
data/Rakefile CHANGED
@@ -17,9 +17,10 @@ begin
17
17
  gem.homepage = "http://guillermo.github.com/ginst"
18
18
  gem.authors = ["Guillermo Álvarez Fernández"]
19
19
  gem.rubyforge_project = "ginst"
20
- gem.add_development_dependency "rspec", ">= 1.2.9"
21
- gem.add_development_dependency "cucumber", ">= 0"
22
20
  gem.add_dependency "sys-proctable"
21
+ gem.add_dependency "mime-types"
22
+ gem.add_dependency "mongrel"
23
+
23
24
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
24
25
  end
25
26
  Jeweler::GemcutterTasks.new
@@ -30,32 +31,30 @@ rescue LoadError
30
31
  puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
31
32
  end
32
33
 
33
- require 'spec/rake/spectask'
34
- Spec::Rake::SpecTask.new(:spec) do |spec|
35
- spec.libs << 'lib' << 'spec'
36
- spec.spec_files = FileList['spec/**/*_spec.rb']
37
- end
38
-
39
- Spec::Rake::SpecTask.new(:rcov) do |spec|
40
- spec.libs << 'lib' << 'spec'
41
- spec.pattern = 'spec/**/*_spec.rb'
42
- spec.rcov = true
34
+ require 'rake/testtask'
35
+ Rake::TestTask.new(:test) do |test|
36
+ test.libs << 'lib' << 'test' << 'app/*'
37
+ test.pattern = 'test/**/test_*.rb'
38
+ test.verbose = true
43
39
  end
44
40
 
45
- task :spec => :check_dependencies
46
-
47
41
  begin
48
- require 'cucumber/rake/task'
49
- Cucumber::Rake::Task.new(:features)
50
-
51
- task :features => :check_dependencies
42
+ require 'rcov/rcovtask'
43
+ Rcov::RcovTask.new do |test|
44
+ test.libs << 'lib' << 'test' << 'app/*'
45
+ test.pattern = 'test/**/test_*.rb'
46
+ test.verbose = true
47
+ end
52
48
  rescue LoadError
53
- task :features do
54
- abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
49
+ task :rcov do
50
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
55
51
  end
56
52
  end
57
53
 
58
- task :default => :spec
54
+ task :test => :check_dependencies
55
+ task :'test:integration' => [:install] # Integration test need the gem to be installed
56
+
57
+ task :default => :test
59
58
 
60
59
  require 'rake/rdoctask'
61
60
  Rake::RDocTask.new do |rdoc|
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.1
1
+ 2009.11.23
@@ -1,6 +1,6 @@
1
1
  # Filters added to this controller apply to all controllers in the application.
2
2
  # Likewise, all the methods added will be available for all controllers.
3
-
3
+
4
4
  class ApplicationController < ActionController::Base
5
5
  helper :all
6
6
  helper_method :current_user_session, :current_user
@@ -5,4 +5,14 @@ class StatusController < ApplicationController
5
5
  @environment = Rails.env
6
6
  end
7
7
 
8
+
9
+ def update
10
+ case params['what']
11
+ when 'restart_builder'
12
+ Ginst::Builder.restart
13
+ else
14
+ end
15
+
16
+ redirect_to :back
17
+ end
8
18
  end
@@ -4,13 +4,16 @@ class TasksController < ApplicationController
4
4
 
5
5
 
6
6
  def index
7
- scope = @project.tasks
8
- if params[:scope] && Task.valid_scopes.include?(params[:scope].to_sym)
9
- scope = scope.send(params[:scope].to_sym)
10
- end
11
-
12
- @tasks = scope.all(:order => 'ended_at DESC')
7
+ if Task.valid_scopes.include?(params[:scope].try(:to_sym))
8
+ scope = @project.tasks
9
+ if params[:scope] && Task.valid_scopes.include?(params[:scope].to_sym)
10
+ scope = scope.send(params[:scope].to_sym)
11
+ end
13
12
 
13
+ @tasks = scope.all(:order => 'ended_at DESC')
14
+ else
15
+ redirect_to :scope => 'prepared'
16
+ end
14
17
  end
15
18
 
16
19
  def update
@@ -24,7 +27,6 @@ class TasksController < ApplicationController
24
27
  end
25
28
 
26
29
  def show
27
- Task.first
28
30
  @task = @project.tasks.find(params[:id])
29
31
 
30
32
  respond_to do |wants|
@@ -1,7 +1,7 @@
1
1
  module ProjectsHelper
2
2
 
3
3
  def show_branch_links
4
- content_tag :ul, :id => 'heads', :class => 'tabs' do
4
+ content_tag :ul, :class => 'submenu' do
5
5
  @project.branchs.map{ |h|
6
6
  content_tag :li do
7
7
  link_to_unless params["branch"]==h.name, h.name,"?branch=#{h.name}"
@@ -10,9 +10,14 @@ module ProjectsHelper
10
10
  end
11
11
  end
12
12
 
13
- def gravatar_url_for(commit, size = 30)
14
- md5 = Digest::MD5.hexdigest(commit.author.email.strip.downcase)
15
- "http://www.gravatar.com/avatar/#{md5}?s=30"
13
+ def gravatar_img_for(commit,size = 30)
14
+ "<img class=\"avatar_#{size}\" src=\"#{gravatar_url_for(commit, size)}\" alt=\"#{commit.author.name}\" />"
15
+ end
16
+
17
+ def gravatar_url_for(email, size = 30)
18
+ email = email.author.email.strip.downcase unless email.kind_of? String
19
+ md5 = Digest::MD5.hexdigest(email)
20
+ "http://www.gravatar.com/avatar/#{md5}?s=#{size}"
16
21
  end
17
22
 
18
23
  def show_time(time)
@@ -1,10 +1,10 @@
1
1
  class Project < ActiveRecord::Base
2
2
  include Slugify
3
3
 
4
- has_many :tasks, :order => 'system desc, priority ASC'
4
+ has_many :tasks, :order => 'updated_at desc, system desc, priority ASC'
5
5
  delegate :prepared, :to => :tasks, :prefix => :tasks
6
6
 
7
- after_create :create_setup_task
7
+ after_create :log_create, :create_setup_task
8
8
  before_create :create_secret
9
9
 
10
10
  validates_presence_of :name, :repo
@@ -24,8 +24,12 @@ class Project < ActiveRecord::Base
24
24
  status == 'building'
25
25
  end
26
26
 
27
+ def log_create
28
+ logger "#{name} project created for #{repo}"
29
+ end
27
30
 
28
31
  def run_tasks!
32
+ logger "Running tasks"
29
33
  if !:locked_at || !tasks_prepared.empty?
30
34
  update_attribute(:locked_at, Time.current)
31
35
 
@@ -40,13 +44,16 @@ class Project < ActiveRecord::Base
40
44
 
41
45
 
42
46
  def create_fetch_task
47
+ logger "creating fetch task"
43
48
  current_fetch_task = tasks.unfinished.fetch_tasks.first
49
+
50
+ # Don't create another if one exists
44
51
  return current_fetch_task if current_fetch_task
45
52
 
46
- Rails.logger.debug "[#{Time.current.to_s(:small)}] * Creating repo for #{name}"
53
+ Rails.logger.debug "[#{Time.current.to_s(:small)}] * Creating fetch task for #{name}"
47
54
 
48
55
  command = "cd #{local_repo_dir}
49
- git fetch -t
56
+ git fetch -fv origin
50
57
  "
51
58
 
52
59
  tasks.create(
@@ -57,20 +64,17 @@ class Project < ActiveRecord::Base
57
64
  )
58
65
  end
59
66
 
67
+ # call after fetch, to check if new commits exists
60
68
  def process_new_heads
61
- preferences.branch_status ||= {}
62
69
  #look for changes in heads
63
70
  branchs.each do |branch|
64
- unless (branch.commit.id == preferences.branch_status[branch.name])
71
+ unless tasks.find_by_commit_sha1(branch.commit.id)
65
72
  process_new_head(branch)
66
- preferences.branch_status[branch.name] = branch.commit.id
67
- save
68
73
  end
69
74
  end
70
75
  end
71
76
 
72
77
  def process_new_head(branch)
73
- logger.info("Processing new commit for #{branch.name}, #{branch.commit.id} ")
74
78
  plugins.each do |plugin|
75
79
  plugin.on_commit(self, branch) if plugin.respond_to? :on_commit
76
80
  end
@@ -81,8 +85,7 @@ class Project < ActiveRecord::Base
81
85
  end
82
86
 
83
87
  def create_setup_task
84
- Rails.logger.debug "[#{Time.current.to_s(:small)}] * Creating repo for #{name}"
85
-
88
+ logger "created setup task"
86
89
  command = "
87
90
  echo removing previous repo
88
91
  rm -Rf #{local_repo_dir}
@@ -101,13 +104,13 @@ class Project < ActiveRecord::Base
101
104
  :on_success => "p = Project.find(#{id}) ; p.set_default_tracking_branchs ; p.update_attribute(:status, 'prepared') "
102
105
  )
103
106
  end
104
-
105
-
106
-
107
+
108
+
107
109
  def to_param
108
110
  slug
109
111
  end
110
-
112
+
113
+
111
114
  def grit_repo
112
115
  @grit_repo || Grit::Repo.new(local_repo_dir)
113
116
  rescue Grit::NoSuchPathError
@@ -162,6 +165,10 @@ class Project < ActiveRecord::Base
162
165
  grit_repo.commits(*args)
163
166
  end
164
167
 
168
+ def commit(sha1)
169
+ grit_repo.commit(sha1)
170
+ end
171
+
165
172
  def commits_between_dates(from,to)
166
173
  grit_repo.commits_between_dates(from,to)
167
174
  end
@@ -192,5 +199,21 @@ class Project < ActiveRecord::Base
192
199
  Array.new(8).map{ keys[rand(keys.size)] }.join
193
200
  end
194
201
 
202
+
203
+ def log_file
204
+ Ginst.data_dir+"/log/#{slug}.log"
205
+ end
206
+
207
+
208
+ def logger(msg = nil)
209
+ if msg
210
+ logger.log(msg, caller)
211
+ else
212
+ @logger ||= Ginst::Logger.new(log_file)
213
+ end
214
+ end
215
+
216
+
217
+
195
218
  end
196
219
 
@@ -23,6 +23,8 @@ class Task < ActiveRecord::Base
23
23
 
24
24
  named_scope :fetch_tasks, {:conditions => {:name => 'fetch.sh'}}
25
25
 
26
+ delegate :logger, :to => :project
27
+
26
28
  def self.valid_scopes
27
29
  [:prepared,:building,:fail,:success,:finished]
28
30
  end
@@ -54,6 +56,7 @@ class Task < ActiveRecord::Base
54
56
 
55
57
 
56
58
  def kill(signal = 'INT')
59
+ logger "Recived kill signal #{signal} for task #{id}"
57
60
  return nil unless pid
58
61
  pids = Sys::ProcTable.ps.find_all{|p| p.ppid == pid || p.pid == pid }.map{|p| p.pid}
59
62
  pids.each{ |pid| Process.kill(signal,pid) }
@@ -73,7 +76,9 @@ class Task < ActiveRecord::Base
73
76
  end
74
77
  end
75
78
 
76
-
79
+ def commit
80
+ project.commit(commit_sha1)
81
+ end
77
82
 
78
83
  class TaskAlreadyRunningException < Exception ; end
79
84
  class TaskAlreadyExecuteException < Exception ; end
@@ -81,6 +86,7 @@ class Task < ActiveRecord::Base
81
86
  def execute
82
87
  raise TaskAlreadyExecuteException if reload[:ended_at]
83
88
  raise TaskAlreadyRunningException if reload[:started_at]
89
+ logger "Executing task #{id} named #{name}"
84
90
  status = 'fail'
85
91
 
86
92
  ObjectSpace.define_finalizer(self,lambda{
@@ -148,16 +154,22 @@ class Task < ActiveRecord::Base
148
154
  while ( !inputs.empty? ) do
149
155
  select(inputs).first.each do |input|
150
156
  begin
151
- buff +=input.read_nonblock(9999999)
157
+ read = input.read_nonblock(99999999)
158
+ logger read
159
+ buff += read
152
160
  self.update_attribute(:output, buff) if output != buff && Time.now.to_i > updated_at.to_i + DATABASE_UPDATE_INTERVAL
153
161
  rescue EOFError
162
+ logger "EOFError. removing #{stdout == input ? 'stdout' : 'stderr'} input"
154
163
  inputs.delete input
155
164
  end
156
165
  end
157
166
  end
158
167
 
159
168
  end
160
- process_status.exitstatus
169
+ logger "Finished popen"
170
+ Process.wait
171
+ logger "Really finished command"
172
+ process_status.exitstatus
161
173
  ensure
162
174
  self.update_attributes(:output => buff,:pid => nil, :exit_code => (process_status && process_status.exitstatus))
163
175
  end
@@ -1,18 +1,9 @@
1
1
  <% content_tag_for :li, commit do %>
2
2
  <pre><%= link_to commit.short_message, project_commit_path(@project, commit.id) %></pre>
3
- <img src="<%= gravatar_url_for(commit) %>">
4
- <p>
3
+ <%= gravatar_img_for(commit) %>
4
+
5
5
  <strong><%= commit.author.name %></strong> (author)<br/>
6
- <em><%= commit.committed_date.to_s(:short) %></em>
7
-
8
- <% task = @project.tasks.for_commit(commit.id).first %>
9
- <% if task %>
10
- <%= link_to "Build status: #{task.status}", project_task_path(@project,task), :class => task.status %>
11
- <% else %>
12
- <% form_for commit, :url => project_commit_path(@project, commit) do |f| %>
13
- <button type="submit">build!</button>
14
- <% end %>
15
- <% end %>
6
+ <em><%= commit.committed_date.to_s(:short) %></em>
16
7
  </p>
17
8
  <dl>
18
9
  <dt>sha1</dt>
@@ -26,10 +26,10 @@
26
26
  <div id="header">
27
27
  <h1><%= @project.slug %></h1>
28
28
  <ul class="tabs">
29
+ <%= plugins_links %>
29
30
  <li><%= link_to 'Commits', project_commits_path(@project) %></li>
30
31
  <li><%= link_to 'Tasks', project_tasks_path(@project) %></li>
31
32
  <li><%= link_to 'Admin', edit_project_path(@project) %></li>
32
- <%= plugins_links %>
33
33
  </ul>
34
34
  </div>
35
35
 
@@ -1,5 +1,10 @@
1
1
  <h1>Builder Daemon Status</h1>
2
- <p><%= @builder_daemon_status %></p>
2
+ <p><%= @builder_daemon_status %>
3
+ <% form_tag('/status', :method => :put) do %>
4
+ <%= hidden_field_tag('what', 'restart_builder') %>
5
+ <%= submit_tag 'restart' %>
6
+ <% end %>
7
+ </p>
3
8
 
4
9
  <h1>Current plugins</h1>
5
10
  <ul>
@@ -1,29 +1,46 @@
1
- <% content_tag_for(:li, task, :class=> task.status) do %>
2
- <pre><%= link_to task.name, project_task_path(@project, task) %></pre>
3
- <p><%= simple_format(h(truncate(task.output))) %></p>
4
- <dl>
5
- <dt>Command:</dt>
6
- <dd><%= truncate(task.code,:length => 35) %></dd>
1
+ <% content_tag_for :div, task, :class=> "#{task.status} task" do %>
2
+ <h1><%= task.name %></h1>
3
+ <p class="status <%= task.status %>">Build status: <%= task.status %></p>
7
4
 
8
- <dt>created_at</dt>
9
- <dd><%= show_time(task.created_at) %></dd>
10
- <dt>started_at</dt>
11
- <dd><%= show_time(task.started_at) %></dd>
5
+ <div class='task_description'>
6
+ <% if task.prepared? %>
7
+ <p>This tasks is prepared to be run. Please wait other tasks finish.</p>
8
+ <pre id="output"><%= task.parsed_output %></pre>
12
9
 
13
- <dt>ended_At</dt>
14
- <dd><%= show_time(task.ended_at) %></dd>
15
-
16
- <dt>status</dt>
17
- <dd><%= task.status %></dd>
18
-
19
- <!-- <dt>output</dt>
20
- <dd><pre><code><%= simple_format(task.output) %></code></pre></dd>
21
-
22
- <dt>exit_code</dt>
23
- <dd><%= task.exit_code %></dd> -->
24
- </dl>
25
- <!-- <% form_for task, :url => project_task_path(task.project, task) do |f| %>
26
- <%= f.submit 'ReRun' %>
27
- <% end %> -->
28
- <% end %>
10
+ <% else %>
11
+ <dl>
12
+ <dt>Started at:</dt>
13
+ <dd><%= show_time(task.started_at) %></dd>
14
+
15
+ <% if task.building? %>
16
+ <dt>Elapsed time</dt>
17
+ <dd class="elapsed_time"><%= show_elapsed(task.build_duration) %></dd>
18
+ <dt>Pid</dt>
19
+ <dd><%= task.pid %>
20
+ <% form_for task, :url => project_task_path(@project,task) do |f| %>
21
+ <%= hidden_field_tag :kill, 'INT' %>
22
+ <%= f.submit 'INT' %>
23
+ <% end %>
24
+
25
+ <% form_for task, :url => project_task_path(@project,task) do |f| %>
26
+ <%= hidden_field_tag :kill, 'QUIT' %>
27
+ <%= f.submit 'QUIT' %>
28
+ <% end %>
29
+
30
+ <% form_for task, :url => project_task_path(@project,task) do |f| %>
31
+ <%= hidden_field_tag :kill, 'KILL' %>
32
+ <%= f.submit 'QUIT' %>
33
+ <% end %>
29
34
 
35
+ </dd>
36
+ <% else %>
37
+ <dt>Ended at:</dt>
38
+ <dd><%= show_time(task.ended_at) %>
39
+ <span><%= show_elapsed(task.build_duration) %></span></dd>
40
+ <% end %>
41
+ <%= link_to 'raw', project_task_path(@project,task, 'text'), :class => 'raw_link' %>
42
+ </dl>
43
+ <pre id="output"><%= task.parsed_output %></pre>
44
+ <% end %>
45
+ </div>
46
+ <% end %>