damagecontrol 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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