damagecontrol 0.5.0 → 0.5.0.1391
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|