damagecontrol 0.5.0 → 0.5.0.1391
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/README +67 -11
- data/Rakefile +15 -6
- data/app/controllers/admin_controller.rb +0 -3
- data/app/controllers/application.rb +42 -163
- data/app/controllers/build_controller.rb +33 -0
- data/app/controllers/files_controller.rb +1 -1
- data/app/controllers/project_controller.rb +23 -65
- data/app/controllers/rails_ext.rb +247 -0
- data/app/controllers/rscm_ext.rb +52 -0
- data/app/helpers/build_helper.rb +2 -0
- data/app/views/build/email.rhtml +18 -0
- data/app/views/build/status.rhtml +20 -0
- data/app/views/build/tests.rhtml +2 -0
- data/app/views/layouts/{rscm.rhtml → default.rhtml} +10 -4
- data/app/views/project/_changesets_list.rhtml +2 -2
- data/app/views/project/_cvs.rhtml +4 -5
- data/app/views/project/_project.rhtml +9 -9
- data/app/views/project/_select_pane.rhtml +26 -0
- data/app/views/project/_tab_pane.rhtml +23 -0
- data/app/views/project/changeset.rhtml +35 -0
- data/app/views/project/view.rhtml +18 -32
- data/app/views/setup/welcome.rhtml +118 -0
- data/config/database.yml +20 -20
- data/config/environment.rb +66 -60
- data/config/environments/development.rb +3 -2
- data/config/environments/production.rb +3 -2
- data/config/environments/test.rb +3 -2
- data/config/routes.rb +15 -0
- data/lib/damagecontrol/app.rb +11 -40
- data/lib/damagecontrol/build.rb +50 -8
- data/lib/damagecontrol/directories.rb +7 -6
- data/lib/damagecontrol/poller.rb +11 -20
- data/lib/damagecontrol/project.rb +83 -16
- data/lib/damagecontrol/publisher/ambient_orb.rb +16 -0
- data/lib/damagecontrol/publisher/archive.rb +16 -0
- data/lib/damagecontrol/publisher/base.rb +25 -0
- data/lib/damagecontrol/publisher/build_duration.rb +16 -0
- data/lib/damagecontrol/publisher/email.rb +59 -0
- data/lib/damagecontrol/publisher/execute.rb +49 -0
- data/lib/damagecontrol/publisher/ftp.rb +16 -0
- data/lib/damagecontrol/publisher/growl.rb +44 -0
- data/lib/damagecontrol/publisher/irc.rb +31 -0
- data/lib/damagecontrol/publisher/jabber.rb +68 -0
- data/lib/damagecontrol/publisher/scp.rb +16 -0
- data/lib/damagecontrol/publisher/x10cm11a.rb +17 -0
- data/lib/damagecontrol/publisher/x10cm17a.rb +17 -0
- data/lib/damagecontrol/publisher/yahoo.rb +16 -0
- data/lib/damagecontrol/standard_persister.rb +2 -2
- data/lib/damagecontrol/tracker.rb +48 -6
- data/lib/damagecontrol/visitor/rss_writer.rb +1 -1
- data/lib/damagecontrol/visitor/yaml_persister.rb +10 -1
- data/public/404.html +5 -5
- data/public/500.html +5 -5
- data/public/dispatch.cgi +2 -2
- data/public/dispatch.fcgi +1 -1
- data/public/dispatch.rb +2 -2
- data/public/images/growlicon.png +0 -0
- data/public/images/megaphone.png +0 -0
- data/public/images/monotone-logo.png +0 -0
- data/public/images/publisher/ambient_orb.png +0 -0
- data/public/images/publisher/build_duration.png +0 -0
- data/public/images/publisher/email.png +0 -0
- data/public/images/publisher/execute.png +0 -0
- data/public/images/publisher/growl.png +0 -0
- data/public/images/publisher/irc.png +0 -0
- data/public/images/publisher/jabber.png +0 -0
- data/public/images/publisher/x10cm11a.png +0 -0
- data/public/images/publisher/x10cm17a.png +0 -0
- data/public/images/publisher/yahoo.png +0 -0
- data/public/index.html +70 -1
- data/public/javascripts/dateFormat.js +283 -0
- data/public/javascripts/jscalendar/ChangeLog +500 -0
- data/public/javascripts/jscalendar/README +33 -0
- data/public/javascripts/jscalendar/bugtest-hidden-selects.html +108 -0
- data/public/javascripts/jscalendar/calendar-blue.css +231 -0
- data/public/javascripts/jscalendar/calendar-blue2.css +235 -0
- data/public/javascripts/jscalendar/calendar-brown.css +224 -0
- data/public/javascripts/jscalendar/calendar-green.css +228 -0
- data/public/javascripts/jscalendar/calendar-setup.js +181 -0
- data/public/javascripts/jscalendar/calendar-setup_stripped.js +21 -0
- data/public/javascripts/jscalendar/calendar-system.css +250 -0
- data/public/javascripts/jscalendar/calendar-tas.css +238 -0
- data/public/javascripts/jscalendar/calendar-win2k-1.css +270 -0
- data/public/javascripts/jscalendar/calendar-win2k-2.css +270 -0
- data/public/javascripts/jscalendar/calendar-win2k-cold-1.css +264 -0
- data/public/javascripts/jscalendar/calendar-win2k-cold-2.css +270 -0
- data/public/javascripts/jscalendar/calendar.js +1715 -0
- data/public/javascripts/jscalendar/calendar.php +119 -0
- data/public/javascripts/jscalendar/calendar_stripped.js +12 -0
- data/public/javascripts/jscalendar/doc/html/reference-Z-S.css +0 -0
- data/public/javascripts/jscalendar/doc/html/reference.css +34 -0
- data/public/javascripts/jscalendar/doc/html/reference.html +1316 -0
- data/public/javascripts/jscalendar/doc/reference.pdf +0 -0
- data/public/javascripts/jscalendar/img.gif +0 -0
- data/public/javascripts/jscalendar/index.html +333 -0
- data/public/javascripts/jscalendar/lang/calendar-af.js +39 -0
- data/public/javascripts/jscalendar/lang/calendar-br.js +45 -0
- data/public/javascripts/jscalendar/lang/calendar-ca.js +45 -0
- data/public/javascripts/jscalendar/lang/calendar-cs-win.js +34 -0
- data/public/javascripts/jscalendar/lang/calendar-da.js +63 -0
- data/public/javascripts/jscalendar/lang/calendar-de.js +100 -0
- data/public/javascripts/jscalendar/lang/calendar-du.js +45 -0
- data/public/javascripts/jscalendar/lang/calendar-el.js +89 -0
- data/public/javascripts/jscalendar/lang/calendar-en.js +123 -0
- data/public/javascripts/jscalendar/lang/calendar-es.js +114 -0
- data/public/javascripts/jscalendar/lang/calendar-fi.js +98 -0
- data/public/javascripts/jscalendar/lang/calendar-fr.js +86 -0
- data/public/javascripts/jscalendar/lang/calendar-hr-utf8.js +49 -0
- data/public/javascripts/jscalendar/lang/calendar-hr.js +0 -0
- data/public/javascripts/jscalendar/lang/calendar-hu.js +45 -0
- data/public/javascripts/jscalendar/lang/calendar-it.js +79 -0
- data/public/javascripts/jscalendar/lang/calendar-jp.js +45 -0
- data/public/javascripts/jscalendar/lang/calendar-ko-utf8.js +120 -0
- data/public/javascripts/jscalendar/lang/calendar-ko.js +120 -0
- data/public/javascripts/jscalendar/lang/calendar-lt-utf8.js +114 -0
- data/public/javascripts/jscalendar/lang/calendar-lt.js +114 -0
- data/public/javascripts/jscalendar/lang/calendar-nl.js +45 -0
- data/public/javascripts/jscalendar/lang/calendar-no.js +45 -0
- data/public/javascripts/jscalendar/lang/calendar-pl-utf8.js +93 -0
- data/public/javascripts/jscalendar/lang/calendar-pl.js +56 -0
- data/public/javascripts/jscalendar/lang/calendar-pt.js +45 -0
- data/public/javascripts/jscalendar/lang/calendar-ro.js +66 -0
- data/public/javascripts/jscalendar/lang/calendar-ru.js +45 -0
- data/public/javascripts/jscalendar/lang/calendar-si.js +94 -0
- data/public/javascripts/jscalendar/lang/calendar-sk.js +99 -0
- data/public/javascripts/jscalendar/lang/calendar-sp.js +63 -0
- data/public/javascripts/jscalendar/lang/calendar-sv.js +93 -0
- data/public/javascripts/jscalendar/lang/calendar-tr.js +58 -0
- data/public/javascripts/jscalendar/lang/calendar-zh.js +45 -0
- data/public/javascripts/jscalendar/menuarrow.gif +0 -0
- data/public/javascripts/jscalendar/menuarrow2.gif +0 -0
- data/public/javascripts/jscalendar/release-notes.html +334 -0
- data/public/javascripts/jscalendar/simple-1.html +244 -0
- data/public/javascripts/jscalendar/simple-2.html +108 -0
- data/public/javascripts/jscalendar/simple-3.html +130 -0
- data/public/javascripts/jscalendar/test-position.html +40 -0
- data/public/javascripts/jscalendar/test.php +116 -0
- data/public/javascripts/toggle_div.js +18 -0
- data/public/stylesheets/niceones.txt +1 -0
- data/public/stylesheets/style.css +8 -1
- data/script/breakpointer +4 -5
- data/script/console +19 -27
- data/script/console_sandbox.rb +7 -0
- data/script/destroy +5 -0
- data/script/generate +3 -68
- data/script/server +6 -16
- data/test/damagecontrol/build_test.rb +8 -8
- data/test/damagecontrol/poller_test.rb +10 -18
- data/test/damagecontrol/project_test.rb +49 -13
- data/test/damagecontrol/publisher/base_test.rb +26 -0
- data/test/damagecontrol/publisher/build/email.rhtml +0 -0
- data/test/damagecontrol/publisher/email_test.rb +26 -0
- data/test/damagecontrol/publisher/fixture.rb +34 -0
- data/test/damagecontrol/publisher/growl_test.rb +15 -0
- data/test/damagecontrol/publisher/jabber_test.rb +15 -0
- data/test/damagecontrol/scm_web_test.rb +1 -1
- data/test/damagecontrol/visitor/changesets.rss +1 -1
- data/test/damagecontrol/visitor/diff_persister_test.rb +4 -4
- data/test/functional/build_controller_test.rb +17 -0
- data/test/test_helper.rb +13 -13
- metadata +185 -24
- data/app/views/project/_bugzilla.rhtml +0 -13
- data/app/views/project/_jira.rhtml +0 -19
- data/app/views/project/_mooky.rhtml +0 -23
- data/app/views/project/_rubyforge.rhtml +0 -19
- data/app/views/project/_scarab.rhtml +0 -19
- data/app/views/project/_scms.rhtml +0 -15
- data/app/views/project/_sourceforge.rhtml +0 -19
- data/app/views/project/_starteam.rhtml +0 -43
- data/app/views/project/_svn.rhtml +0 -22
- data/app/views/project/_trac.rhtml +0 -13
- data/app/views/project/_trackers.rhtml +0 -18
- data/app/views/project/changesets.rhtml +0 -31
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
Dependencies.mechanism = :require
|
|
2
|
-
ActionController::Base.consider_all_requests_local = false
|
|
1
|
+
Dependencies.mechanism = :require
|
|
2
|
+
ActionController::Base.consider_all_requests_local = false
|
|
3
|
+
ActionController::Base.perform_caching = true
|
data/config/environments/test.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
Dependencies.mechanism = :require
|
|
2
|
-
ActionController::Base.consider_all_requests_local = true
|
|
1
|
+
Dependencies.mechanism = :require
|
|
2
|
+
ActionController::Base.consider_all_requests_local = true
|
|
3
|
+
ActionController::Base.perform_caching = false
|
|
3
4
|
ActionMailer::Base.delivery_method = :test
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ActionController::Routing::Routes.draw do |map|
|
|
2
|
+
# Add your own custom routes here.
|
|
3
|
+
# The priority is based upon order of creation: first created -> highest priority.
|
|
4
|
+
|
|
5
|
+
# Here's a sample route:
|
|
6
|
+
# map.connect 'products/:id', :controller => 'catalog', :action => 'view'
|
|
7
|
+
# Keep in mind you can assign values other than :controller and :action
|
|
8
|
+
|
|
9
|
+
# Allow downloading Web Service WSDL as a file with an extension
|
|
10
|
+
# instead of a file named 'wsdl'
|
|
11
|
+
map.connect ':controller/service.wsdl', :action => 'wsdl'
|
|
12
|
+
|
|
13
|
+
# Install the default route as the lowest priority.
|
|
14
|
+
map.connect ':controller/:action/:id'
|
|
15
|
+
end
|
data/lib/damagecontrol/app.rb
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
require 'drb'
|
|
2
1
|
require 'rubygems'
|
|
3
2
|
require 'needle'
|
|
4
|
-
|
|
3
|
+
require 'rscm'
|
|
5
4
|
require 'damagecontrol/poller'
|
|
6
5
|
require 'damagecontrol/standard_persister'
|
|
6
|
+
require 'damagecontrol/publisher/base'
|
|
7
7
|
|
|
8
8
|
# Wire up the whole DamageControl app with Needle's nice block based DI framework.
|
|
9
9
|
# I wonder - is BDI (Block Dependency Injection) a new flavour of DI?
|
|
@@ -15,59 +15,30 @@ REGISTRY = Needle::Registry.define do |b|
|
|
|
15
15
|
b.poller do
|
|
16
16
|
DamageControl::Poller.new do |project, changesets|
|
|
17
17
|
b.persister.save_changesets(project, changesets)
|
|
18
|
-
b.persister.save_diffs(project, changesets)
|
|
19
18
|
b.persister.save_rss(project)
|
|
20
19
|
changeset = changesets.latest
|
|
21
|
-
project.
|
|
20
|
+
project.execute_build(changeset.identifier, "Detected changes by polling #{project.scm.name}") do |build|
|
|
21
|
+
# TODO: we want to reuse this in other places (Execute publisher)
|
|
22
22
|
env = {
|
|
23
23
|
'PKG_BUILD' => changeset.identifier.to_s, # Rake standard
|
|
24
|
-
'DAMAGECONTROL_BUILD_LABEL' => changeset.identifier.to_s # For others
|
|
24
|
+
'DAMAGECONTROL_BUILD_LABEL' => changeset.identifier.to_s, # For others
|
|
25
|
+
'DAMAGECONTROL_CHANGED_FILES' => changeset.changes.collect{|change| change.path}.join(",")
|
|
25
26
|
}
|
|
26
27
|
build.execute(project.build_command, env)
|
|
28
|
+
project.publish(build)
|
|
27
29
|
end
|
|
30
|
+
# TODO: do this in a publisher that can be turned off if an other SCMWeb is used.
|
|
31
|
+
# This may take a while, so we do it after the build.
|
|
32
|
+
b.persister.save_diffs(project, changesets)
|
|
28
33
|
end
|
|
29
34
|
end
|
|
30
|
-
|
|
31
|
-
b.drb_server do
|
|
32
|
-
DamageControl::DrbServer.new('druby://localhost:9000')
|
|
33
|
-
end
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
module DamageControl
|
|
37
38
|
|
|
38
39
|
class App
|
|
39
40
|
def run
|
|
40
|
-
REGISTRY.poller.start
|
|
41
|
-
REGISTRY.drb_server.start
|
|
42
|
-
|
|
43
|
-
DRb.thread.join # Block forever
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# Drb top-level object that can be accessed by the web app.
|
|
48
|
-
# The webapp should use this for any operations that are
|
|
49
|
-
# lengthy.
|
|
50
|
-
#
|
|
51
|
-
class DrbServer
|
|
52
|
-
def initialize(drb_url)
|
|
53
|
-
@drb_url = drb_url
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def start
|
|
57
|
-
DRb.start_service(@drb_url, self)
|
|
58
|
-
Log.info "DamageControl server running on #{@drb_url}"
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def save_project(project)
|
|
62
|
-
project.save
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def delete_project(project)
|
|
66
|
-
project.delete
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def checkout_project(project)
|
|
70
|
-
project.checkout
|
|
41
|
+
REGISTRY.poller.start.join
|
|
71
42
|
end
|
|
72
43
|
end
|
|
73
44
|
|
data/lib/damagecontrol/build.rb
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
require 'rscm/path_converter'
|
|
2
2
|
require 'damagecontrol/directories'
|
|
3
|
+
require 'damagecontrol/project'
|
|
3
4
|
|
|
4
5
|
module DamageControl
|
|
5
|
-
#
|
|
6
|
+
# Represents build-related data organised in the following file structure:
|
|
7
|
+
#
|
|
8
|
+
# File structure:
|
|
6
9
|
#
|
|
7
10
|
# .damagecontrol/
|
|
8
11
|
# SomeProject/
|
|
@@ -19,16 +22,32 @@ module DamageControl
|
|
|
19
22
|
# artifacts/
|
|
20
23
|
#
|
|
21
24
|
class Build
|
|
25
|
+
|
|
26
|
+
# TODO: we want to store the following additional info for a build (time related)
|
|
27
|
+
# * Total Duration
|
|
28
|
+
# * Duration of checkpoints (compile, test, javadocs...) - should be configurable in project.
|
|
29
|
+
# *
|
|
30
|
+
|
|
22
31
|
attr_reader :time
|
|
23
32
|
|
|
24
33
|
# Creates a new Build for a +project+'s +changeset+, created at +time+.
|
|
25
|
-
def initialize(project_name, changeset_identifier, time)
|
|
26
|
-
@project_name, @changeset_identifier, @time = project_name, changeset_identifier, time
|
|
34
|
+
def initialize(project_name, changeset_identifier, time, build_reason)
|
|
35
|
+
@project_name, @changeset_identifier, @time, @build_reason = project_name, changeset_identifier, time, build_reason
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Our unique id within the changeset
|
|
39
|
+
def identifier
|
|
40
|
+
time.ymdHMS
|
|
27
41
|
end
|
|
28
42
|
|
|
29
|
-
#
|
|
43
|
+
# Our associated project
|
|
44
|
+
def project
|
|
45
|
+
Project.load(@project_name)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Our associated changeset
|
|
30
49
|
def changeset
|
|
31
|
-
|
|
50
|
+
project.changeset(@changeset_identifier)
|
|
32
51
|
end
|
|
33
52
|
|
|
34
53
|
# Executes +command+ with the environment variables +env+ and persists the command for future reference.
|
|
@@ -40,20 +59,22 @@ module DamageControl
|
|
|
40
59
|
File.open(command_file, "w") do |io|
|
|
41
60
|
io.write(command)
|
|
42
61
|
end
|
|
43
|
-
stderr = Directories.stderr(@project_name, @changeset_identifier, @time)
|
|
44
|
-
stdout = Directories.stdout(@project_name, @changeset_identifier, @time)
|
|
45
62
|
command_line = "#{command} > #{stdout} 2> #{stderr}"
|
|
46
63
|
|
|
47
64
|
begin
|
|
48
65
|
with_working_dir(checkout_dir) do
|
|
49
66
|
env.each {|k,v| ENV[k]=v}
|
|
67
|
+
Log.info "Executing '#{command_line}'"
|
|
68
|
+
Log.info "Execution environment:"
|
|
69
|
+
ENV.each {|k,v| Log.info("#{k}=#{v}")}
|
|
50
70
|
IO.popen(command_line) do |io|
|
|
51
71
|
File.open(pid_file, "w") do |pid_io|
|
|
52
72
|
pid_io.write(pid)
|
|
53
73
|
end
|
|
54
74
|
|
|
55
75
|
# there is nothing to read, since we're redirecting to file,
|
|
56
|
-
# but we still need to read in order to block
|
|
76
|
+
# but we still need to read in order to block until the process is done.
|
|
77
|
+
# TODO: don't redirect stdout - we want to intercept checkpoints
|
|
57
78
|
io.read
|
|
58
79
|
end
|
|
59
80
|
end
|
|
@@ -73,6 +94,14 @@ module DamageControl
|
|
|
73
94
|
nil
|
|
74
95
|
end
|
|
75
96
|
end
|
|
97
|
+
|
|
98
|
+
def successful?
|
|
99
|
+
exit_code == 0
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def status_message
|
|
103
|
+
successful? ? "Successful" : "Failed"
|
|
104
|
+
end
|
|
76
105
|
|
|
77
106
|
# Returns the pid of the build process
|
|
78
107
|
def pid
|
|
@@ -82,7 +111,20 @@ module DamageControl
|
|
|
82
111
|
def kill
|
|
83
112
|
Process.kill("SIGHUP", pid)
|
|
84
113
|
end
|
|
114
|
+
|
|
115
|
+
def stdout
|
|
116
|
+
Directories.stdout(@project_name, @changeset_identifier, @time)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def stderr
|
|
120
|
+
Directories.stderr(@project_name, @changeset_identifier, @time)
|
|
121
|
+
end
|
|
85
122
|
|
|
123
|
+
# The directory of the build
|
|
124
|
+
def dir
|
|
125
|
+
Directories.build_dir(@project_name, @changeset_identifier, @time)
|
|
126
|
+
end
|
|
127
|
+
|
|
86
128
|
private
|
|
87
129
|
|
|
88
130
|
def checkout_dir
|
|
@@ -6,17 +6,23 @@ module DamageControl
|
|
|
6
6
|
|
|
7
7
|
# This class knows about locations of various files and directories.
|
|
8
8
|
#
|
|
9
|
+
# TODO: Add templates, logs(global)
|
|
9
10
|
module Directories
|
|
10
11
|
include FileUtils
|
|
11
12
|
|
|
12
13
|
def project_names
|
|
13
|
-
result = Dir["#{basedir}/*/project.yaml"].collect do |f|
|
|
14
|
+
result = Dir["#{basedir}/projects/*/project.yaml"].collect do |f|
|
|
14
15
|
File.basename(File.dirname(f))
|
|
15
16
|
end
|
|
16
17
|
result.sort
|
|
17
18
|
end
|
|
18
19
|
module_function :project_names
|
|
19
20
|
|
|
21
|
+
def project_dir(project_name)
|
|
22
|
+
"#{basedir}/projects/#{project_name}"
|
|
23
|
+
end
|
|
24
|
+
module_function :project_dir
|
|
25
|
+
|
|
20
26
|
def checkout_dir(project_name)
|
|
21
27
|
"#{project_dir(project_name)}/checkout"
|
|
22
28
|
end
|
|
@@ -105,11 +111,6 @@ module DamageControl
|
|
|
105
111
|
end
|
|
106
112
|
module_function :project_config_file
|
|
107
113
|
|
|
108
|
-
def project_dir(project_name)
|
|
109
|
-
"#{basedir}/#{project_name}"
|
|
110
|
-
end
|
|
111
|
-
module_function :project_dir
|
|
112
|
-
|
|
113
114
|
def basedir
|
|
114
115
|
if(ENV['DAMAGECONTROL_HOME'])
|
|
115
116
|
ENV['DAMAGECONTROL_HOME']
|
data/lib/damagecontrol/poller.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require 'rscm/logging'
|
|
2
2
|
require 'rscm/time_ext'
|
|
3
3
|
require 'damagecontrol/project'
|
|
4
|
+
require 'damagecontrol/publisher/base'
|
|
4
5
|
|
|
5
6
|
module DamageControl
|
|
6
7
|
# Polls all projects in intervals.
|
|
@@ -11,25 +12,20 @@ module DamageControl
|
|
|
11
12
|
# receive |project, changesets| each time new
|
|
12
13
|
# +changesets+ are found in a polled +project+
|
|
13
14
|
def initialize(sleeptime=60, &proc)
|
|
14
|
-
@projects = []
|
|
15
15
|
@sleeptime = sleeptime
|
|
16
16
|
@proc = proc
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
# Adds a project to poll. If the project is already added it is replaced
|
|
20
|
-
# with then new one, otherwise appended to the end.
|
|
21
|
-
def add_project(project)
|
|
22
|
-
index = @projects.index(project) || @projects.length
|
|
23
|
-
@projects[index] = project
|
|
24
|
-
end
|
|
25
|
-
|
|
26
19
|
# Polls all registered projects and persists RSS, changesets and diffs to disk.
|
|
27
20
|
# If a block is passed, the project and the changesets will be yielded to the block
|
|
28
21
|
# for each new changesets object.
|
|
29
22
|
def poll
|
|
30
|
-
|
|
23
|
+
Log.info "Starting polling cycle"
|
|
24
|
+
Project.find_all.each do |project|
|
|
25
|
+
Log.info "Polling #{project.name}"
|
|
31
26
|
begin
|
|
32
27
|
if(project.scm_exists?)
|
|
28
|
+
Log.info "Polling #{project.name}"
|
|
33
29
|
project.poll do |changesets|
|
|
34
30
|
if(changesets.empty?)
|
|
35
31
|
Log.info "No changesets for #{project.name}"
|
|
@@ -37,36 +33,31 @@ module DamageControl
|
|
|
37
33
|
@proc.call(project, changesets)
|
|
38
34
|
end
|
|
39
35
|
end
|
|
36
|
+
else
|
|
37
|
+
Log.info "Not polling #{project.name} since its scm doesn't exist"
|
|
40
38
|
end
|
|
41
39
|
rescue => e
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
Log.error "Error polling #{project.name}"
|
|
41
|
+
Log.error e.message
|
|
42
|
+
Log.error " " + e.backtrace.join(" \n")
|
|
45
43
|
end
|
|
46
44
|
end
|
|
47
45
|
end
|
|
48
46
|
|
|
49
47
|
# Runs +poll+ in a separate thread.
|
|
50
48
|
def start
|
|
51
|
-
add_all_projects
|
|
52
49
|
@t = Thread.new do
|
|
53
50
|
while(true)
|
|
54
51
|
poll
|
|
55
52
|
sleep(@sleeptime)
|
|
56
53
|
end
|
|
57
54
|
end
|
|
55
|
+
@t
|
|
58
56
|
end
|
|
59
57
|
|
|
60
58
|
# Stops thread after a +start+.
|
|
61
59
|
def stop
|
|
62
60
|
@t.kill if @t && @t.alive?
|
|
63
61
|
end
|
|
64
|
-
|
|
65
|
-
# Adds all projects
|
|
66
|
-
def add_all_projects
|
|
67
|
-
Project.find_all.each do |project|
|
|
68
|
-
add_project(project)
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
62
|
end
|
|
72
63
|
end
|
|
@@ -10,14 +10,29 @@ require 'damagecontrol/tracker'
|
|
|
10
10
|
require 'damagecontrol/visitor/yaml_persister'
|
|
11
11
|
require 'damagecontrol/visitor/diff_persister'
|
|
12
12
|
require 'damagecontrol/visitor/rss_writer'
|
|
13
|
+
require 'damagecontrol/publisher/base'
|
|
14
|
+
|
|
15
|
+
module ObjectTemplate
|
|
16
|
+
def dupe(variables)
|
|
17
|
+
template_yaml = YAML::dump(self)
|
|
18
|
+
b = binding
|
|
19
|
+
variables.each { |key, value| eval "#{key} = variables[\"#{key}\"]", b }
|
|
20
|
+
new_yaml = eval(template_yaml.dump.gsub(/\\#/, "#"), b)
|
|
21
|
+
YAML::load(new_yaml)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
13
24
|
|
|
14
25
|
module DamageControl
|
|
15
26
|
# Represents a project with associated SCM, Tracker and SCMWeb
|
|
16
27
|
class Project
|
|
28
|
+
include ObjectTemplate
|
|
29
|
+
|
|
30
|
+
# TODO: move to scms? not sure....
|
|
31
|
+
DEFAULT_QUIET_PERIOD = 10 unless defined? DEFAULT_QUIET_PERIOD
|
|
17
32
|
|
|
18
33
|
attr_accessor :name
|
|
19
|
-
attr_accessor :description
|
|
20
34
|
attr_accessor :home_page
|
|
35
|
+
attr_accessor :start_time
|
|
21
36
|
|
|
22
37
|
attr_accessor :scm
|
|
23
38
|
attr_accessor :tracker
|
|
@@ -27,14 +42,26 @@ module DamageControl
|
|
|
27
42
|
attr_accessor :quiet_period
|
|
28
43
|
|
|
29
44
|
attr_accessor :build_command
|
|
45
|
+
attr_accessor :publishers
|
|
30
46
|
|
|
31
47
|
# Loads the project with the given +name+.
|
|
32
48
|
def Project.load(name)
|
|
33
49
|
config_file = Directories.project_config_file(name)
|
|
34
50
|
Log.info "Loading project from #{config_file}"
|
|
35
|
-
File.open(config_file) do |io|
|
|
51
|
+
project = File.open(config_file) do |io|
|
|
36
52
|
YAML::load(io)
|
|
37
53
|
end
|
|
54
|
+
|
|
55
|
+
# Add new publishers that may have be defined after the project was YAMLed.
|
|
56
|
+
project.publishers = [] if project.publishers.nil?
|
|
57
|
+
Publisher::Base.classes.collect{|cls| cls.new}.each do |publisher|
|
|
58
|
+
publisher_of_same_type = project.publishers.find do |p|
|
|
59
|
+
p.class == publisher.class
|
|
60
|
+
end
|
|
61
|
+
project.publishers << publisher unless publisher_of_same_type
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
project
|
|
38
65
|
end
|
|
39
66
|
|
|
40
67
|
# Loads all projects
|
|
@@ -43,12 +70,39 @@ module DamageControl
|
|
|
43
70
|
Project.load(name)
|
|
44
71
|
end
|
|
45
72
|
end
|
|
46
|
-
|
|
47
|
-
def
|
|
73
|
+
|
|
74
|
+
def start_time=(t)
|
|
75
|
+
t = Time.parse_ymdHMS(t) if t.is_a? String
|
|
76
|
+
@start_time = t
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def initialize(name="")
|
|
48
80
|
@name = name
|
|
81
|
+
@publishers = Publisher::Base.classes.collect{|cls| cls.new}
|
|
49
82
|
@scm = nil
|
|
50
83
|
@tracker = Tracker::Null.new
|
|
51
|
-
@scm_web = SCMWeb::Null.new
|
|
84
|
+
@scm_web = SCMWeb::Null.new
|
|
85
|
+
# Default start time is 2 weeks ago
|
|
86
|
+
@start_time = Time.now.utc - (3600*24*14)
|
|
87
|
+
@quiet_period = DEFAULT_QUIET_PERIOD
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Tells all publishers to publish a build
|
|
91
|
+
def publish(build)
|
|
92
|
+
@publishers.each do |publisher|
|
|
93
|
+
begin
|
|
94
|
+
if(publisher.enabled)
|
|
95
|
+
Log.info("Publishing #{publisher.name} for #{@name}")
|
|
96
|
+
publisher.publish(build)
|
|
97
|
+
else
|
|
98
|
+
Log.info("Skipping disabled publisher #{publisher.name} for #{@name}")
|
|
99
|
+
end
|
|
100
|
+
rescue => e
|
|
101
|
+
Log.error "Error running publisher #{publisher.name} for project #{name}"
|
|
102
|
+
Log.error e.message
|
|
103
|
+
Log.error " " + e.backtrace.join(" \n")
|
|
104
|
+
end
|
|
105
|
+
end
|
|
52
106
|
end
|
|
53
107
|
|
|
54
108
|
# Saves the state of this project to persistent store (YAML)
|
|
@@ -58,8 +112,6 @@ module DamageControl
|
|
|
58
112
|
File.open(f, "w") do |io|
|
|
59
113
|
YAML::dump(self, io)
|
|
60
114
|
end
|
|
61
|
-
|
|
62
|
-
REGISTRY.poller.add_project(self) if REGISTRY
|
|
63
115
|
end
|
|
64
116
|
|
|
65
117
|
# Path to file containing pathnames of latest checked out files.
|
|
@@ -81,9 +133,9 @@ module DamageControl
|
|
|
81
133
|
end
|
|
82
134
|
|
|
83
135
|
# Polls SCM for new changesets and yields them to the given block.
|
|
84
|
-
def poll
|
|
136
|
+
def poll
|
|
85
137
|
start = Time.now
|
|
86
|
-
from = next_changeset_identifier ||
|
|
138
|
+
from = next_changeset_identifier || @start_time
|
|
87
139
|
|
|
88
140
|
Log.info "Getting changesets for #{name} from #{from} (retrieved from #{checkout_dir})"
|
|
89
141
|
changesets = @scm.changesets(checkout_dir, from)
|
|
@@ -93,10 +145,11 @@ module DamageControl
|
|
|
93
145
|
# When the changesets are not changing, we can consider the last commit done
|
|
94
146
|
# and the quiet period elapsed. This is not 100% failsafe, but will work
|
|
95
147
|
# under most circumstances. In the worst case, we'll miss some files in
|
|
96
|
-
# the changesets, but they will be part of the next
|
|
148
|
+
# the changesets for really slow commits, but they will be part of the next
|
|
149
|
+
# changeset (on next poll).
|
|
97
150
|
commit_in_progress = true
|
|
98
151
|
while(commit_in_progress)
|
|
99
|
-
@quiet_period ||=
|
|
152
|
+
@quiet_period ||= DEFAULT_QUIET_PERIOD
|
|
100
153
|
Log.info "Sleeping for #{@quiet_period} seconds since #{name}'s SCM (#{@scm.name}) is not transactional."
|
|
101
154
|
sleep @quiet_period
|
|
102
155
|
next_changesets = @scm.changesets(checkout_dir, from)
|
|
@@ -156,6 +209,12 @@ module DamageControl
|
|
|
156
209
|
changesets_persister.load_upto(changeset_identifier, prior)
|
|
157
210
|
end
|
|
158
211
|
|
|
212
|
+
def changeset(changeset_identifier)
|
|
213
|
+
result = changesets(changeset_identifier, 1)[0]
|
|
214
|
+
raise "No changeset with id '#{changeset_identifier}' for project '#{name}'" unless result
|
|
215
|
+
result
|
|
216
|
+
end
|
|
217
|
+
|
|
159
218
|
def changeset_identifiers
|
|
160
219
|
changesets_persister.identifiers
|
|
161
220
|
end
|
|
@@ -177,23 +236,28 @@ module DamageControl
|
|
|
177
236
|
DamageControl::Visitor::YamlPersister.new(changesets_dir)
|
|
178
237
|
end
|
|
179
238
|
|
|
180
|
-
# Creates, persists and executes a
|
|
239
|
+
# Creates, persists and executes a Build for the changeset with the given
|
|
181
240
|
# +changeset_identifier+.
|
|
182
241
|
# Should be called with a block of arity 1 that will receive the build.
|
|
183
|
-
def
|
|
242
|
+
def execute_build(changeset_identifier, build_reason)
|
|
184
243
|
scm.checkout(checkout_dir, changeset_identifier)
|
|
185
|
-
build = Build.new(name, changeset_identifier, Time.now.utc)
|
|
244
|
+
build = Build.new(name, changeset_identifier, Time.now.utc, build_reason)
|
|
186
245
|
yield build
|
|
187
246
|
end
|
|
188
247
|
|
|
189
|
-
# Returns an array of existing
|
|
248
|
+
# Returns an array of existing Build s for the given +changeset_identifier+.
|
|
190
249
|
def builds(changeset_identifier)
|
|
191
250
|
Directories.build_dirs(name, changeset_identifier).collect do |dir|
|
|
192
251
|
# The dir's basename will always be a Time
|
|
193
|
-
Build.new(name, changeset_identifier, File.basename(dir).to_identifier)
|
|
252
|
+
Build.new(name, changeset_identifier, File.basename(dir).to_identifier, "TODO: get from file")
|
|
194
253
|
end
|
|
195
254
|
end
|
|
196
255
|
|
|
256
|
+
# Returns the Build for the given +changeset_identifier+ and +build_time+
|
|
257
|
+
def build(changeset_identifier, build_time)
|
|
258
|
+
Build.new(name, changeset_identifier, build_time, "FIXME")
|
|
259
|
+
end
|
|
260
|
+
|
|
197
261
|
# Returns the latest build.
|
|
198
262
|
def latest_build
|
|
199
263
|
changeset_identifiers.reverse.each do |changeset_identifier|
|
|
@@ -202,6 +266,9 @@ module DamageControl
|
|
|
202
266
|
end
|
|
203
267
|
nil
|
|
204
268
|
end
|
|
269
|
+
|
|
270
|
+
# Creates a duplicate of ourself, applying simple standard #{blah}
|
|
271
|
+
# transformations of all key,value pairs in +local_assigns+.
|
|
205
272
|
|
|
206
273
|
private
|
|
207
274
|
|