damagecontrol 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +75 -0
- data/README.license +5 -0
- data/Rakefile +111 -0
- data/app/controllers/admin_controller.rb +10 -0
- data/app/controllers/application.rb +163 -0
- data/app/controllers/files_controller.rb +19 -0
- data/app/controllers/project_controller.rb +284 -0
- data/app/controllers/scm_controller.rb +49 -0
- data/app/helpers/admin_helper.rb +2 -0
- data/app/helpers/application_helper.rb +3 -0
- data/app/helpers/project_helper.rb +2 -0
- data/app/views/dhtml_sites.txt +6 -0
- data/app/views/files/list.rhtml +4 -0
- data/app/views/layouts/rscm.rhtml +79 -0
- data/app/views/project/_bugzilla.rhtml +13 -0
- data/app/views/project/_changesets_list.rhtml +52 -0
- data/app/views/project/_cvs.rhtml +171 -0
- data/app/views/project/_jira.rhtml +19 -0
- data/app/views/project/_mooky.rhtml +23 -0
- data/app/views/project/_null.rhtml +0 -0
- data/app/views/project/_project.rhtml +36 -0
- data/app/views/project/_rubyforge.rhtml +19 -0
- data/app/views/project/_scarab.rhtml +19 -0
- data/app/views/project/_scms.rhtml +15 -0
- data/app/views/project/_sourceforge.rhtml +19 -0
- data/app/views/project/_starteam.rhtml +43 -0
- data/app/views/project/_svn.rhtml +22 -0
- data/app/views/project/_trac.rhtml +13 -0
- data/app/views/project/_trackers.rhtml +18 -0
- data/app/views/project/changesets.rhtml +31 -0
- data/app/views/project/index.rhtml +23 -0
- data/app/views/project/view.rhtml +70 -0
- data/app/views/scm/checkout_status.rhtml +44 -0
- data/app/views/scm/diff.rhtml +1 -0
- data/app/views/scm/scroll.html +27 -0
- data/bin/damagecontrol +7 -0
- data/bin/damagecontrol-webrick +2 -0
- data/config/database.yml +20 -0
- data/config/environment.rb +60 -0
- data/config/environments/development.rb +3 -0
- data/config/environments/production.rb +2 -0
- data/config/environments/test.rb +3 -0
- data/lib/damagecontrol/app.rb +74 -0
- data/lib/damagecontrol/build.rb +104 -0
- data/lib/damagecontrol/diff_htmlizer.rb +82 -0
- data/lib/damagecontrol/diff_parser.rb +153 -0
- data/lib/damagecontrol/directories.rb +126 -0
- data/lib/damagecontrol/poller.rb +72 -0
- data/lib/damagecontrol/project.rb +213 -0
- data/lib/damagecontrol/project_dependencies.rb +8 -0
- data/lib/damagecontrol/scm_web.rb +50 -0
- data/lib/damagecontrol/standard_persister.rb +49 -0
- data/lib/damagecontrol/tracker.rb +164 -0
- data/lib/damagecontrol/visitor/build_executor.rb +32 -0
- data/lib/damagecontrol/visitor/diff_persister.rb +41 -0
- data/lib/damagecontrol/visitor/rss_writer.rb +43 -0
- data/lib/damagecontrol/visitor/yaml_persister.rb +71 -0
- data/public/404.html +6 -0
- data/public/500.html +6 -0
- data/public/dispatch.cgi +10 -0
- data/public/dispatch.fcgi +7 -0
- data/public/dispatch.rb +10 -0
- data/public/images/16x16/about.png +0 -0
- data/public/images/16x16/bug_green.png +0 -0
- data/public/images/16x16/bug_red.png +0 -0
- data/public/images/16x16/bug_yellow.png +0 -0
- data/public/images/16x16/component.png +0 -0
- data/public/images/16x16/console.png +0 -0
- data/public/images/16x16/console_error.png +0 -0
- data/public/images/16x16/document_add.png +0 -0
- data/public/images/16x16/document_delete.png +0 -0
- data/public/images/16x16/document_edit.png +0 -0
- data/public/images/16x16/document_exchange.png +0 -0
- data/public/images/16x16/document_new.png +0 -0
- data/public/images/16x16/document_warning.png +0 -0
- data/public/images/16x16/safe.png +0 -0
- data/public/images/16x16/scroll_information.png +0 -0
- data/public/images/16x16/wrench.png +0 -0
- data/public/images/24x24/box_delete.png +0 -0
- data/public/images/24x24/box_into.png +0 -0
- data/public/images/24x24/box_new.png +0 -0
- data/public/images/24x24/console_network.png +0 -0
- data/public/images/24x24/document_edit.png +0 -0
- data/public/images/24x24/find.png +0 -0
- data/public/images/24x24/folders.png +0 -0
- data/public/images/24x24/garbage.png +0 -0
- data/public/images/24x24/gear_connection.png +0 -0
- data/public/images/24x24/gear_delete.png +0 -0
- data/public/images/24x24/gears_run.png +0 -0
- data/public/images/24x24/home.png +0 -0
- data/public/images/24x24/navigate_left.png +0 -0
- data/public/images/24x24/navigate_right.png +0 -0
- data/public/images/24x24/package_new.png +0 -0
- data/public/images/24x24/safe.png +0 -0
- data/public/images/24x24/safe_new.png +0 -0
- data/public/images/24x24/safe_out.png +0 -0
- data/public/images/24x24/scroll_information.png +0 -0
- data/public/images/24x24/stop.png +0 -0
- data/public/images/24x24/wrench.png +0 -0
- data/public/images/README.license +2 -0
- data/public/images/blue-16.gif +0 -0
- data/public/images/blue-32.gif +0 -0
- data/public/images/bugzilla.png +0 -0
- data/public/images/cvs.png +0 -0
- data/public/images/footer.gif +0 -0
- data/public/images/green-128.gif +0 -0
- data/public/images/green-16.gif +0 -0
- data/public/images/green-32.gif +0 -0
- data/public/images/grey-16.gif +0 -0
- data/public/images/grey-32.gif +0 -0
- data/public/images/jira.gif +0 -0
- data/public/images/red-16.gif +0 -0
- data/public/images/red-32.gif +0 -0
- data/public/images/red-pulse-32.gif +0 -0
- data/public/images/rss.gif +0 -0
- data/public/images/rubyforge.png +0 -0
- data/public/images/scarab.gif +0 -0
- data/public/images/sourceforge.gif +0 -0
- data/public/images/starteam.png +0 -0
- data/public/images/svnlogo64.png +0 -0
- data/public/images/trac.png +0 -0
- data/public/index.html +1 -0
- data/public/javascripts/dw_event.js +34 -0
- data/public/javascripts/dw_tooltip.js +86 -0
- data/public/javascripts/dw_viewport.js +55 -0
- data/public/javascripts/pngfix.js +29 -0
- data/public/javascripts/toggle_diff.js +25 -0
- data/public/licenses/DAMAGECONTROL.license +28 -0
- data/public/licenses/INCORS.license +32 -0
- data/public/stylesheets/diff.css +23 -0
- data/public/stylesheets/style.css +307 -0
- data/script/breakpointer +5 -0
- data/script/console +30 -0
- data/script/generate +70 -0
- data/script/server +61 -0
- data/test/damagecontrol/a_program.rb +3 -0
- data/test/damagecontrol/a_slow_program.rb +3 -0
- data/test/damagecontrol/build_test.rb +59 -0
- data/test/damagecontrol/diff_htmlizer_test.rb +31 -0
- data/test/damagecontrol/diff_parser_test.rb +61 -0
- data/test/damagecontrol/file_ext.rb +12 -0
- data/test/damagecontrol/poller_test.rb +56 -0
- data/test/damagecontrol/project_test.rb +144 -0
- data/test/damagecontrol/scm_web_test.rb +22 -0
- data/test/damagecontrol/test.diff +38 -0
- data/test/damagecontrol/test.html +40 -0
- data/test/damagecontrol/tracker_test.rb +48 -0
- data/test/damagecontrol/visitor/changesets.rss +34 -0
- data/test/damagecontrol/visitor/diff_persister_test.rb +49 -0
- data/test/damagecontrol/visitor/rss_writer_test.rb +40 -0
- data/test/damagecontrol/visitor/yaml_persister_test.rb +40 -0
- data/test/functional/admin_controller_test.rb +17 -0
- data/test/functional/project_controller_test.rb +17 -0
- data/test/test_helper.rb +14 -0
- metadata +245 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
By specifying an issue tracker issue numbers/ids in developers' SCM commit messages
|
2
|
+
will be detected and highlighted with external links to the particular issue.
|
3
|
+
This way people browsing changesets can easily navigate to related issues.
|
4
|
+
<table width="100%">
|
5
|
+
<tr>
|
6
|
+
<td width="25%">Issue tracker</td>
|
7
|
+
<td class="config_cell"><%= tip(:txt => "scm_tip") %></td>
|
8
|
+
<td width="75%">
|
9
|
+
<%= text_or_select(@edit, :name => "tracker", :values => @trackers, :onchange => "showElement(this.value, trackers)") %>
|
10
|
+
</td>
|
11
|
+
</tr>
|
12
|
+
</table>
|
13
|
+
|
14
|
+
<% @trackers.each do |tracker| %>
|
15
|
+
<div id="<%= tracker.class.name %>" style="<% if(tracker.selected?)%>display:block<%else%>display:none<%end%>">
|
16
|
+
<%= render_partial(tracker.short, tracker) %>
|
17
|
+
</div>
|
18
|
+
<% end %>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<%
|
2
|
+
@changesets.each do |changeset|
|
3
|
+
%>
|
4
|
+
<table class="pane">
|
5
|
+
<tr class="pane">
|
6
|
+
<td colspan="3" class="changeset">
|
7
|
+
<div class="changeset-message">
|
8
|
+
<b><%= changeset.developer %></b>
|
9
|
+
(<%= changeset.time.to_human %> UTC)<br>
|
10
|
+
<%= @project.tracker.highlight(changeset.message) %>
|
11
|
+
</div>
|
12
|
+
</td>
|
13
|
+
</tr>
|
14
|
+
<%
|
15
|
+
changeset.each do |change|
|
16
|
+
%>
|
17
|
+
<tr>
|
18
|
+
<td width="5%"><%= tag("img", :src => change.icon) %></td>
|
19
|
+
<td width="5%"><%= change.revision %></td>
|
20
|
+
<td width="90%">
|
21
|
+
<a class="diff-toggle" href="#" onclick="toggleCode('<%= change.path %>');return false"><%= change.path %></a>
|
22
|
+
<br>
|
23
|
+
<div class="diff" id="<%= change.path %>">
|
24
|
+
<%= change.html_diff %>
|
25
|
+
</div>
|
26
|
+
</td>
|
27
|
+
</tr>
|
28
|
+
<% end %>
|
29
|
+
</table>
|
30
|
+
<% end %>
|
31
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<table id="projectstatus" class="pane">
|
2
|
+
<tr>
|
3
|
+
<th align="left">Project</th>
|
4
|
+
<th align="left">Latest Build</th>
|
5
|
+
<th align="left">Changeset RSS</th>
|
6
|
+
</tr>
|
7
|
+
<% @projects.each do |project| %>
|
8
|
+
<tr>
|
9
|
+
<td align="left"><%= link_to(project.name, {:controller => "project", :action => "view", :id => project.name}) %></td>
|
10
|
+
<td align="left">
|
11
|
+
<%
|
12
|
+
img = "/images/grey-16.gif"
|
13
|
+
build = project.latest_build
|
14
|
+
if(build)
|
15
|
+
img = build.small_image
|
16
|
+
end
|
17
|
+
%>
|
18
|
+
<%= tag("img", :src => img) %>
|
19
|
+
</td>
|
20
|
+
<td align="left"><%= link_to_image("rss.gif", :action => "changesets_rss", :id => project.name) %></td>
|
21
|
+
</tr>
|
22
|
+
<% end %>
|
23
|
+
</table>
|
@@ -0,0 +1,70 @@
|
|
1
|
+
<script><!--
|
2
|
+
|
3
|
+
function setTab(tab) {
|
4
|
+
stickyTab = tab
|
5
|
+
showElement(stickyTab, tabs)
|
6
|
+
}
|
7
|
+
|
8
|
+
function executeOnLoad() {
|
9
|
+
<% @scms.each do |scm| %>
|
10
|
+
<%= scm.short %>_init();
|
11
|
+
<% end %>
|
12
|
+
tab = "<%= @params['tab'] || 'tabs[0]' %>"
|
13
|
+
setTab(tab)
|
14
|
+
}
|
15
|
+
|
16
|
+
function showElement(selected, elements) {
|
17
|
+
// hide all divs in the group unless selected
|
18
|
+
for(element in elements) {
|
19
|
+
id = elements[element]
|
20
|
+
style = "none"
|
21
|
+
if(id == selected) {
|
22
|
+
style = "block"
|
23
|
+
}
|
24
|
+
document.getElementById(id).style.display = style
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
tabs = ["projects", "scms", "trackers", "changesets"]
|
29
|
+
|
30
|
+
scms = [
|
31
|
+
<% @scms.each do |scm| %>
|
32
|
+
"<%= scm.class.name %>",
|
33
|
+
<% end %> "" ];
|
34
|
+
scm_tip = "<div class='tp1'>If you don't find your SCM here, file a feature request in DamageControl's JIRA</div>";
|
35
|
+
|
36
|
+
trackers = [
|
37
|
+
<% @trackers.each do |tracker| %>
|
38
|
+
"<%= tracker.class.name %>",
|
39
|
+
<% end %> "" ];
|
40
|
+
|
41
|
+
// -->
|
42
|
+
</script>
|
43
|
+
|
44
|
+
<ul id="foldertab">
|
45
|
+
<li><a href="javascript:setTab('projects')"><img src="/images/16x16/wrench.png"> General</a></li>
|
46
|
+
<li><a href="javascript:setTab('scms')"><img src="/images/16x16/safe.png"> Source Control</a></li>
|
47
|
+
<li><a href="javascript:setTab('trackers')"><img src="/images/16x16/scroll_information.png"> Issue Tracker</a></li>
|
48
|
+
</ul>
|
49
|
+
|
50
|
+
<% if(@edit) %>
|
51
|
+
<form action="/project/save">
|
52
|
+
<% end %>
|
53
|
+
|
54
|
+
<div id="projects" style="display:block">
|
55
|
+
<%= render_partial("project", @project) %>
|
56
|
+
</div>
|
57
|
+
|
58
|
+
<div id="scms" style="display:none">
|
59
|
+
<%= render_partial("scms", @scms) %>
|
60
|
+
</div>
|
61
|
+
|
62
|
+
<div id="trackers" style="display:none">
|
63
|
+
<%= render_partial("trackers", @trackers) %>
|
64
|
+
</div>
|
65
|
+
|
66
|
+
<% if(@edit) %>
|
67
|
+
<input type="submit" value="Save"/>
|
68
|
+
|
69
|
+
</form>
|
70
|
+
<% end %>
|
@@ -0,0 +1,44 @@
|
|
1
|
+
<script>
|
2
|
+
var nl = /\n/gi;
|
3
|
+
|
4
|
+
var req;
|
5
|
+
|
6
|
+
function loadXMLDoc(url) {
|
7
|
+
if (window.XMLHttpRequest) {
|
8
|
+
req = new XMLHttpRequest();
|
9
|
+
req.onreadystatechange = processReqChange;
|
10
|
+
req.open("GET", url, true);
|
11
|
+
req.send(null);
|
12
|
+
} else if (window.ActiveXObject) {
|
13
|
+
req = new ActiveXObject("Microsoft.XMLHTTP");
|
14
|
+
if (req) {
|
15
|
+
req.onreadystatechange = processReqChange;
|
16
|
+
req.open("GET", url, true);
|
17
|
+
req.send();
|
18
|
+
}
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
function processReqChange() {
|
23
|
+
// only if req shows "loaded"
|
24
|
+
if (req.readyState == 4) {
|
25
|
+
// only if "OK"
|
26
|
+
if (req.status == 200) {
|
27
|
+
var checkout_list = window.frames['checkout_list']
|
28
|
+
checkout_list.document.body.innerHTML = req.responseText.replace(nl, "<br>")
|
29
|
+
checkout_list.scrollBy(0, 3000)
|
30
|
+
|
31
|
+
setTimeout("loadXMLDoc('<%= @checkout_list_path %>')", 1000)
|
32
|
+
} else {
|
33
|
+
alert("There was a problem retrieving the XML data:\n" +
|
34
|
+
req.responseText);
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
function executeOnLoad() {
|
40
|
+
loadXMLDoc('<%= @checkout_list_path %>')
|
41
|
+
}
|
42
|
+
</script>
|
43
|
+
|
44
|
+
<iframe id="checkout_list" name="checkout_list" rows="20" cols="120">Your browser doesn't support IFRAME.</iframe>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= File.open(@diff_html).read %>
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>RSCM</title>
|
4
|
+
<link type="text/css" rel="stylesheet" href="/stylesheets/style.css">
|
5
|
+
<script>
|
6
|
+
|
7
|
+
// TODO: border and black backgound/white FG. + file icons (prefix with U/D/A/M
|
8
|
+
|
9
|
+
// http://www.dyn-web.com/dhtml/iframes/
|
10
|
+
|
11
|
+
var regX = /\n/gi;
|
12
|
+
|
13
|
+
function addscroll() {
|
14
|
+
var checkout_list = window.frames['checkout_list']
|
15
|
+
checkout_list.document.body.innerHTML = "hippie\nyoyo<br>hippie<br>yoyo<br>hippie<br>yoyo<br>hippie<br>yoyo<br>".replace(regX, "<br>")
|
16
|
+
checkout_list.scrollBy(0, 3000)
|
17
|
+
}
|
18
|
+
|
19
|
+
</script>
|
20
|
+
</head>
|
21
|
+
<body>
|
22
|
+
|
23
|
+
<iframe id="checkout_list" name="checkout_list" rows="20" cols="80">We'll see some checked out files here soon.....
|
24
|
+
</iframe>
|
25
|
+
<input type="Button" value="Scroll" id="myButton" onClick="addscroll()">
|
26
|
+
|
27
|
+
</body></html>
|
data/bin/damagecontrol
ADDED
data/config/database.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
development:
|
2
|
+
adapter: mysql
|
3
|
+
database: rails_development
|
4
|
+
host: localhost
|
5
|
+
username: root
|
6
|
+
password:
|
7
|
+
|
8
|
+
test:
|
9
|
+
adapter: mysql
|
10
|
+
database: rails_test
|
11
|
+
host: localhost
|
12
|
+
username: root
|
13
|
+
password:
|
14
|
+
|
15
|
+
production:
|
16
|
+
adapter: mysql
|
17
|
+
database: rails_production
|
18
|
+
host: localhost
|
19
|
+
username: root
|
20
|
+
password:
|
@@ -0,0 +1,60 @@
|
|
1
|
+
RAILS_ROOT = File.dirname(__FILE__) + "/../"
|
2
|
+
RAILS_ENV = ENV['RAILS_ENV'] || 'development'
|
3
|
+
|
4
|
+
|
5
|
+
# Mocks first.
|
6
|
+
ADDITIONAL_LOAD_PATHS = ["#{RAILS_ROOT}/test/mocks/#{RAILS_ENV}"]
|
7
|
+
|
8
|
+
# Then model subdirectories.
|
9
|
+
ADDITIONAL_LOAD_PATHS.concat(Dir["#{RAILS_ROOT}/app/models/[_a-z]*"])
|
10
|
+
|
11
|
+
# Followed by the standard includes.
|
12
|
+
ADDITIONAL_LOAD_PATHS.concat %w(
|
13
|
+
app
|
14
|
+
app/models
|
15
|
+
app/controllers
|
16
|
+
app/helpers
|
17
|
+
config
|
18
|
+
lib
|
19
|
+
vendor
|
20
|
+
).map { |dir| "#{RAILS_ROOT}/#{dir}" }
|
21
|
+
|
22
|
+
# Prepend to $LOAD_PATH
|
23
|
+
ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) }
|
24
|
+
|
25
|
+
|
26
|
+
# Require Rails gems.
|
27
|
+
require 'rubygems'
|
28
|
+
require_gem 'activerecord'
|
29
|
+
require_gem 'actionpack'
|
30
|
+
require_gem 'actionmailer'
|
31
|
+
require_gem 'rails'
|
32
|
+
|
33
|
+
|
34
|
+
# Environment-specific configuration.
|
35
|
+
require_dependency "environments/#{RAILS_ENV}"
|
36
|
+
ActiveRecord::Base.configurations = YAML::load(File.open("#{RAILS_ROOT}/config/database.yml"))
|
37
|
+
ActiveRecord::Base.establish_connection
|
38
|
+
|
39
|
+
|
40
|
+
# Configure defaults if the included environment did not.
|
41
|
+
begin
|
42
|
+
RAILS_DEFAULT_LOGGER = Logger.new("#{RAILS_ROOT}/log/#{RAILS_ENV}.log")
|
43
|
+
rescue StandardError
|
44
|
+
RAILS_DEFAULT_LOGGER = Logger.new(STDERR)
|
45
|
+
RAILS_DEFAULT_LOGGER.level = Logger::WARN
|
46
|
+
RAILS_DEFAULT_LOGGER.warn(
|
47
|
+
"Rails Error: Unable to access log file. Please ensure that log/#{RAILS_ENV}.log exists and is chmod 0777. " +
|
48
|
+
"The log level has been raised to WARN and the output directed to STDERR until the problem is fixed."
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
[ActiveRecord::Base, ActionController::Base, ActionMailer::Base].each do |klass|
|
53
|
+
klass.logger ||= RAILS_DEFAULT_LOGGER
|
54
|
+
end
|
55
|
+
[ActionController::Base, ActionMailer::Base].each do |klass|
|
56
|
+
klass.template_root ||= "#{RAILS_ROOT}/app/views/"
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
# Include your app's configuration here:
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'drb'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'needle'
|
4
|
+
require_gem 'rscm'
|
5
|
+
require 'damagecontrol/poller'
|
6
|
+
require 'damagecontrol/standard_persister'
|
7
|
+
|
8
|
+
# Wire up the whole DamageControl app with Needle's nice block based DI framework.
|
9
|
+
# I wonder - is BDI (Block Dependency Injection) a new flavour of DI?
|
10
|
+
REGISTRY = Needle::Registry.define do |b|
|
11
|
+
b.persister do
|
12
|
+
DamageControl::StandardPersister.new
|
13
|
+
end
|
14
|
+
|
15
|
+
b.poller do
|
16
|
+
DamageControl::Poller.new do |project, changesets|
|
17
|
+
b.persister.save_changesets(project, changesets)
|
18
|
+
b.persister.save_diffs(project, changesets)
|
19
|
+
b.persister.save_rss(project)
|
20
|
+
changeset = changesets.latest
|
21
|
+
project.build(changeset.identifier) do |build|
|
22
|
+
env = {
|
23
|
+
'PKG_BUILD' => changeset.identifier.to_s, # Rake standard
|
24
|
+
'DAMAGECONTROL_BUILD_LABEL' => changeset.identifier.to_s # For others
|
25
|
+
}
|
26
|
+
build.execute(project.build_command, env)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
b.drb_server do
|
32
|
+
DamageControl::DrbServer.new('druby://localhost:9000')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module DamageControl
|
37
|
+
|
38
|
+
class App
|
39
|
+
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
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'rscm/path_converter'
|
2
|
+
require 'damagecontrol/directories'
|
3
|
+
|
4
|
+
module DamageControl
|
5
|
+
# File structure
|
6
|
+
#
|
7
|
+
# .damagecontrol/
|
8
|
+
# SomeProject/
|
9
|
+
# project.yaml
|
10
|
+
# checkout/
|
11
|
+
# changesets/
|
12
|
+
# 2802/
|
13
|
+
# changeset.yaml (serialised ChangeSet object)
|
14
|
+
# diffs/ (serialised diff files)
|
15
|
+
# builds/
|
16
|
+
# 2005280271234500/ (timestamp of build start)
|
17
|
+
# stdout.log
|
18
|
+
# stderr.log
|
19
|
+
# artifacts/
|
20
|
+
#
|
21
|
+
class Build
|
22
|
+
attr_reader :time
|
23
|
+
|
24
|
+
# 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
|
27
|
+
end
|
28
|
+
|
29
|
+
# The changeset we belong to
|
30
|
+
def changeset
|
31
|
+
Directories.changeset
|
32
|
+
end
|
33
|
+
|
34
|
+
# Executes +command+ with the environment variables +env+ and persists the command for future reference.
|
35
|
+
# This will prevent the same build from being executed in the future.
|
36
|
+
def execute(command, env={})
|
37
|
+
command_file = Directories.build_command_file(@project_name, @changeset_identifier, @time)
|
38
|
+
raise BuildException.new("This build has already been executed and cannot be re-executed. It was executed with '#{File.open(command_file).read}'") if File.exist?(command_file)
|
39
|
+
FileUtils.mkdir_p(File.dirname(command_file))
|
40
|
+
File.open(command_file, "w") do |io|
|
41
|
+
io.write(command)
|
42
|
+
end
|
43
|
+
stderr = Directories.stderr(@project_name, @changeset_identifier, @time)
|
44
|
+
stdout = Directories.stdout(@project_name, @changeset_identifier, @time)
|
45
|
+
command_line = "#{command} > #{stdout} 2> #{stderr}"
|
46
|
+
|
47
|
+
begin
|
48
|
+
with_working_dir(checkout_dir) do
|
49
|
+
env.each {|k,v| ENV[k]=v}
|
50
|
+
IO.popen(command_line) do |io|
|
51
|
+
File.open(pid_file, "w") do |pid_io|
|
52
|
+
pid_io.write(pid)
|
53
|
+
end
|
54
|
+
|
55
|
+
# there is nothing to read, since we're redirecting to file,
|
56
|
+
# but we still need to read in order to block till process id done.
|
57
|
+
io.read
|
58
|
+
end
|
59
|
+
end
|
60
|
+
ensure
|
61
|
+
exit_code = $? >> 8
|
62
|
+
File.open(exit_code_file, "w") do |io|
|
63
|
+
io.write(exit_code)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns the exit code of the build process, or nil if the process was killed
|
69
|
+
def exit_code
|
70
|
+
if(File.exist?(exit_code_file))
|
71
|
+
File.read(exit_code_file).to_i
|
72
|
+
else
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns the pid of the build process
|
78
|
+
def pid
|
79
|
+
File.read(pid_file).to_i
|
80
|
+
end
|
81
|
+
|
82
|
+
def kill
|
83
|
+
Process.kill("SIGHUP", pid)
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def checkout_dir
|
89
|
+
Directories.checkout_dir(@project_name)
|
90
|
+
end
|
91
|
+
|
92
|
+
def exit_code_file
|
93
|
+
Directories.build_exit_code_file(@project_name, @changeset_identifier, @time)
|
94
|
+
end
|
95
|
+
|
96
|
+
def pid_file
|
97
|
+
Directories.build_pid_file(@project_name, @changeset_identifier, @time)
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
class BuildException < Exception
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module DamageControl
|
2
|
+
|
3
|
+
# Visitor that can visit an array of diffs and produce nice HTML
|
4
|
+
# TODO: add line numbers.
|
5
|
+
class DiffHtmlizer
|
6
|
+
# Creates a new DiffHtmlizer that will write HTML
|
7
|
+
# to the IO object +io+ when visiting an array of diffs.
|
8
|
+
def initialize(io)
|
9
|
+
@io = io
|
10
|
+
end
|
11
|
+
|
12
|
+
def visitDiff(diff)
|
13
|
+
@io << "<div>\n"
|
14
|
+
end
|
15
|
+
|
16
|
+
def visitDiffEnd(diff)
|
17
|
+
@io << "</div>\n"
|
18
|
+
end
|
19
|
+
|
20
|
+
def visitLine(line)
|
21
|
+
if(line.removed?)
|
22
|
+
@io << "<pre class='diff' id='removed'>"
|
23
|
+
if(line.removed)
|
24
|
+
@io << line.prefix.html_encoded
|
25
|
+
@io << "<span id='removedchars'>"
|
26
|
+
@io << line.removed.html_encoded
|
27
|
+
@io << "</span>"
|
28
|
+
@io << line.suffix.html_encoded
|
29
|
+
else
|
30
|
+
@io << line.html_encoded
|
31
|
+
end
|
32
|
+
@io << "</pre>"
|
33
|
+
elsif(line.added?)
|
34
|
+
@io << "<pre class='diff' id='added'>"
|
35
|
+
if(line.added)
|
36
|
+
@io << line.prefix.html_encoded
|
37
|
+
@io << "<span id='addedchars'>"
|
38
|
+
@io << line.added.html_encoded
|
39
|
+
@io << "</span>"
|
40
|
+
@io << line.suffix.html_encoded
|
41
|
+
else
|
42
|
+
@io << line.html_encoded
|
43
|
+
end
|
44
|
+
@io << "</pre>"
|
45
|
+
else
|
46
|
+
@io << "<pre class='diff' id='context'>"
|
47
|
+
@io << line.html_encoded
|
48
|
+
@io << "</pre>"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Not used
|
54
|
+
class Plain
|
55
|
+
def initialize(io)
|
56
|
+
@io = io
|
57
|
+
end
|
58
|
+
|
59
|
+
def visitDiff(diff)
|
60
|
+
end
|
61
|
+
|
62
|
+
def visitDiffEnd(diff)
|
63
|
+
end
|
64
|
+
|
65
|
+
def visitLine(line)
|
66
|
+
@io << line.html_encoded
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class String
|
72
|
+
def html_encoded
|
73
|
+
self.gsub(/./) do
|
74
|
+
case $&
|
75
|
+
when "&" then "&"
|
76
|
+
when "<" then "<"
|
77
|
+
when ">" then ">"
|
78
|
+
else $&
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|