damagecontrol 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (155) hide show
  1. data/README +75 -0
  2. data/README.license +5 -0
  3. data/Rakefile +111 -0
  4. data/app/controllers/admin_controller.rb +10 -0
  5. data/app/controllers/application.rb +163 -0
  6. data/app/controllers/files_controller.rb +19 -0
  7. data/app/controllers/project_controller.rb +284 -0
  8. data/app/controllers/scm_controller.rb +49 -0
  9. data/app/helpers/admin_helper.rb +2 -0
  10. data/app/helpers/application_helper.rb +3 -0
  11. data/app/helpers/project_helper.rb +2 -0
  12. data/app/views/dhtml_sites.txt +6 -0
  13. data/app/views/files/list.rhtml +4 -0
  14. data/app/views/layouts/rscm.rhtml +79 -0
  15. data/app/views/project/_bugzilla.rhtml +13 -0
  16. data/app/views/project/_changesets_list.rhtml +52 -0
  17. data/app/views/project/_cvs.rhtml +171 -0
  18. data/app/views/project/_jira.rhtml +19 -0
  19. data/app/views/project/_mooky.rhtml +23 -0
  20. data/app/views/project/_null.rhtml +0 -0
  21. data/app/views/project/_project.rhtml +36 -0
  22. data/app/views/project/_rubyforge.rhtml +19 -0
  23. data/app/views/project/_scarab.rhtml +19 -0
  24. data/app/views/project/_scms.rhtml +15 -0
  25. data/app/views/project/_sourceforge.rhtml +19 -0
  26. data/app/views/project/_starteam.rhtml +43 -0
  27. data/app/views/project/_svn.rhtml +22 -0
  28. data/app/views/project/_trac.rhtml +13 -0
  29. data/app/views/project/_trackers.rhtml +18 -0
  30. data/app/views/project/changesets.rhtml +31 -0
  31. data/app/views/project/index.rhtml +23 -0
  32. data/app/views/project/view.rhtml +70 -0
  33. data/app/views/scm/checkout_status.rhtml +44 -0
  34. data/app/views/scm/diff.rhtml +1 -0
  35. data/app/views/scm/scroll.html +27 -0
  36. data/bin/damagecontrol +7 -0
  37. data/bin/damagecontrol-webrick +2 -0
  38. data/config/database.yml +20 -0
  39. data/config/environment.rb +60 -0
  40. data/config/environments/development.rb +3 -0
  41. data/config/environments/production.rb +2 -0
  42. data/config/environments/test.rb +3 -0
  43. data/lib/damagecontrol/app.rb +74 -0
  44. data/lib/damagecontrol/build.rb +104 -0
  45. data/lib/damagecontrol/diff_htmlizer.rb +82 -0
  46. data/lib/damagecontrol/diff_parser.rb +153 -0
  47. data/lib/damagecontrol/directories.rb +126 -0
  48. data/lib/damagecontrol/poller.rb +72 -0
  49. data/lib/damagecontrol/project.rb +213 -0
  50. data/lib/damagecontrol/project_dependencies.rb +8 -0
  51. data/lib/damagecontrol/scm_web.rb +50 -0
  52. data/lib/damagecontrol/standard_persister.rb +49 -0
  53. data/lib/damagecontrol/tracker.rb +164 -0
  54. data/lib/damagecontrol/visitor/build_executor.rb +32 -0
  55. data/lib/damagecontrol/visitor/diff_persister.rb +41 -0
  56. data/lib/damagecontrol/visitor/rss_writer.rb +43 -0
  57. data/lib/damagecontrol/visitor/yaml_persister.rb +71 -0
  58. data/public/404.html +6 -0
  59. data/public/500.html +6 -0
  60. data/public/dispatch.cgi +10 -0
  61. data/public/dispatch.fcgi +7 -0
  62. data/public/dispatch.rb +10 -0
  63. data/public/images/16x16/about.png +0 -0
  64. data/public/images/16x16/bug_green.png +0 -0
  65. data/public/images/16x16/bug_red.png +0 -0
  66. data/public/images/16x16/bug_yellow.png +0 -0
  67. data/public/images/16x16/component.png +0 -0
  68. data/public/images/16x16/console.png +0 -0
  69. data/public/images/16x16/console_error.png +0 -0
  70. data/public/images/16x16/document_add.png +0 -0
  71. data/public/images/16x16/document_delete.png +0 -0
  72. data/public/images/16x16/document_edit.png +0 -0
  73. data/public/images/16x16/document_exchange.png +0 -0
  74. data/public/images/16x16/document_new.png +0 -0
  75. data/public/images/16x16/document_warning.png +0 -0
  76. data/public/images/16x16/safe.png +0 -0
  77. data/public/images/16x16/scroll_information.png +0 -0
  78. data/public/images/16x16/wrench.png +0 -0
  79. data/public/images/24x24/box_delete.png +0 -0
  80. data/public/images/24x24/box_into.png +0 -0
  81. data/public/images/24x24/box_new.png +0 -0
  82. data/public/images/24x24/console_network.png +0 -0
  83. data/public/images/24x24/document_edit.png +0 -0
  84. data/public/images/24x24/find.png +0 -0
  85. data/public/images/24x24/folders.png +0 -0
  86. data/public/images/24x24/garbage.png +0 -0
  87. data/public/images/24x24/gear_connection.png +0 -0
  88. data/public/images/24x24/gear_delete.png +0 -0
  89. data/public/images/24x24/gears_run.png +0 -0
  90. data/public/images/24x24/home.png +0 -0
  91. data/public/images/24x24/navigate_left.png +0 -0
  92. data/public/images/24x24/navigate_right.png +0 -0
  93. data/public/images/24x24/package_new.png +0 -0
  94. data/public/images/24x24/safe.png +0 -0
  95. data/public/images/24x24/safe_new.png +0 -0
  96. data/public/images/24x24/safe_out.png +0 -0
  97. data/public/images/24x24/scroll_information.png +0 -0
  98. data/public/images/24x24/stop.png +0 -0
  99. data/public/images/24x24/wrench.png +0 -0
  100. data/public/images/README.license +2 -0
  101. data/public/images/blue-16.gif +0 -0
  102. data/public/images/blue-32.gif +0 -0
  103. data/public/images/bugzilla.png +0 -0
  104. data/public/images/cvs.png +0 -0
  105. data/public/images/footer.gif +0 -0
  106. data/public/images/green-128.gif +0 -0
  107. data/public/images/green-16.gif +0 -0
  108. data/public/images/green-32.gif +0 -0
  109. data/public/images/grey-16.gif +0 -0
  110. data/public/images/grey-32.gif +0 -0
  111. data/public/images/jira.gif +0 -0
  112. data/public/images/red-16.gif +0 -0
  113. data/public/images/red-32.gif +0 -0
  114. data/public/images/red-pulse-32.gif +0 -0
  115. data/public/images/rss.gif +0 -0
  116. data/public/images/rubyforge.png +0 -0
  117. data/public/images/scarab.gif +0 -0
  118. data/public/images/sourceforge.gif +0 -0
  119. data/public/images/starteam.png +0 -0
  120. data/public/images/svnlogo64.png +0 -0
  121. data/public/images/trac.png +0 -0
  122. data/public/index.html +1 -0
  123. data/public/javascripts/dw_event.js +34 -0
  124. data/public/javascripts/dw_tooltip.js +86 -0
  125. data/public/javascripts/dw_viewport.js +55 -0
  126. data/public/javascripts/pngfix.js +29 -0
  127. data/public/javascripts/toggle_diff.js +25 -0
  128. data/public/licenses/DAMAGECONTROL.license +28 -0
  129. data/public/licenses/INCORS.license +32 -0
  130. data/public/stylesheets/diff.css +23 -0
  131. data/public/stylesheets/style.css +307 -0
  132. data/script/breakpointer +5 -0
  133. data/script/console +30 -0
  134. data/script/generate +70 -0
  135. data/script/server +61 -0
  136. data/test/damagecontrol/a_program.rb +3 -0
  137. data/test/damagecontrol/a_slow_program.rb +3 -0
  138. data/test/damagecontrol/build_test.rb +59 -0
  139. data/test/damagecontrol/diff_htmlizer_test.rb +31 -0
  140. data/test/damagecontrol/diff_parser_test.rb +61 -0
  141. data/test/damagecontrol/file_ext.rb +12 -0
  142. data/test/damagecontrol/poller_test.rb +56 -0
  143. data/test/damagecontrol/project_test.rb +144 -0
  144. data/test/damagecontrol/scm_web_test.rb +22 -0
  145. data/test/damagecontrol/test.diff +38 -0
  146. data/test/damagecontrol/test.html +40 -0
  147. data/test/damagecontrol/tracker_test.rb +48 -0
  148. data/test/damagecontrol/visitor/changesets.rss +34 -0
  149. data/test/damagecontrol/visitor/diff_persister_test.rb +49 -0
  150. data/test/damagecontrol/visitor/rss_writer_test.rb +40 -0
  151. data/test/damagecontrol/visitor/yaml_persister_test.rb +40 -0
  152. data/test/functional/admin_controller_test.rb +17 -0
  153. data/test/functional/project_controller_test.rb +17 -0
  154. data/test/test_helper.rb +14 -0
  155. 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
@@ -0,0 +1,7 @@
1
+ begin
2
+ require 'damagecontrol/app'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ require_gem 'damagecontrol'
6
+ end
7
+ DamageControl::App.new.run
@@ -0,0 +1,2 @@
1
+ rails_webrick_server = File.dirname(__FILE__) + "/../script/server"
2
+ load(rails_webrick_server)
@@ -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,3 @@
1
+ Dependencies.mechanism = :load
2
+ ActionController::Base.consider_all_requests_local = true
3
+ BREAKPOINT_SERVER_PORT = 42531
@@ -0,0 +1,2 @@
1
+ Dependencies.mechanism = :require
2
+ ActionController::Base.consider_all_requests_local = false
@@ -0,0 +1,3 @@
1
+ Dependencies.mechanism = :require
2
+ ActionController::Base.consider_all_requests_local = true
3
+ ActionMailer::Base.delivery_method = :test
@@ -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 "&amp;"
76
+ when "<" then "&lt;"
77
+ when ">" then "&gt;"
78
+ else $&
79
+ end
80
+ end
81
+ end
82
+ end