houston-core 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +69 -68
  3. data/app/adapters/houston/adapters/deployment/engineyard.rb +1 -1
  4. data/app/adapters/houston/adapters/version_control/git_adapter/github_repo.rb +19 -0
  5. data/app/adapters/houston/adapters/version_control/git_adapter/remote_repo.rb +27 -0
  6. data/app/assets/images/drag-grip.png +0 -0
  7. data/app/assets/javascripts/app/infinite_scroll.coffee +1 -1
  8. data/app/assets/javascripts/app/views/_show_sprint_view.coffee +9 -9
  9. data/app/assets/javascripts/application.js +1 -0
  10. data/app/assets/javascripts/core/app.coffee +5 -0
  11. data/app/assets/javascripts/{app → core}/stacked_area_graph.coffee +0 -0
  12. data/app/assets/javascripts/{app → core}/stacked_bar_graph.coffee +0 -0
  13. data/app/assets/javascripts/dashboard.js +1 -0
  14. data/app/assets/javascripts/vendor.js +0 -1
  15. data/app/assets/stylesheets/application/exceptions.scss +3 -1
  16. data/app/assets/stylesheets/application/navigation.scss +84 -21
  17. data/app/assets/stylesheets/application/releases.scss +32 -2
  18. data/app/assets/stylesheets/application/test.scss +34 -0
  19. data/app/assets/stylesheets/core/colors.scss.erb +33 -3
  20. data/app/assets/stylesheets/dashboard/dashboard.scss +11 -7
  21. data/app/assets/stylesheets/variables.scss +3 -0
  22. data/app/concerns/belongs_to_commit.rb +14 -0
  23. data/app/concerns/project_adapter.rb +24 -6
  24. data/app/controllers/api/v1/projects_controller.rb +18 -0
  25. data/app/controllers/api/v1/sprint_tasks_controller.rb +1 -1
  26. data/app/controllers/deploys_controller.rb +1 -0
  27. data/app/controllers/project_tests_controller.rb +49 -19
  28. data/app/controllers/releases_controller.rb +5 -0
  29. data/app/controllers/test_runs_controller.rb +16 -1
  30. data/app/helpers/test_run_helper.rb +24 -0
  31. data/app/models/deploy.rb +13 -0
  32. data/app/models/github/pull_request.rb +39 -4
  33. data/app/models/release.rb +42 -0
  34. data/app/models/task.rb +3 -2
  35. data/app/models/test_run.rb +4 -0
  36. data/app/presenters/project_presenter.rb +28 -0
  37. data/app/views/deploys/show.html.erb +4 -0
  38. data/app/views/github/pulls/index.html.erb +4 -1
  39. data/app/views/layouts/_mobile_navigation.html.erb +14 -17
  40. data/app/views/layouts/_navigation.html.erb +87 -87
  41. data/app/views/layouts/dashboard.html.erb +2 -2
  42. data/app/views/project_tests/index.html.erb +22 -7
  43. data/app/views/releases/_index.html.erb +65 -0
  44. data/app/views/releases/_results.html.erb +47 -0
  45. data/app/views/releases/index.html.erb +29 -65
  46. data/app/views/sprints/dashboard.html.erb +4 -2
  47. data/config/environments/production.rb +1 -1
  48. data/config/environments/test.rb +4 -1
  49. data/config/initializers/add_navigation_renderers.rb +6 -0
  50. data/config/initializers/requirements.rb +1 -0
  51. data/config/routes.rb +3 -0
  52. data/db/migrate/20151226154901_add_search_vector_to_releases.rb +6 -0
  53. data/db/migrate/20151226155305_generate_index_on_releases.rb +5 -0
  54. data/db/migrate/20151228183704_drop_unused_tables.rb +35 -0
  55. data/db/migrate/20160120145757_add_successful_to_deploys.rb +10 -0
  56. data/db/structure.sql +19 -67
  57. data/houston.gemspec +3 -3
  58. data/lib/configuration.rb +4 -2
  59. data/lib/core_ext/array.rb +37 -0
  60. data/lib/houston/version.rb +1 -1
  61. data/test/integration/ci_integration_test.rb +14 -13
  62. data/test/unit/models/project_test.rb +33 -0
  63. data/test/unit/models/pull_request_test.rb +71 -1
  64. metadata +24 -14
  65. data/app/models/historical_head.rb +0 -5
@@ -66,8 +66,9 @@ class Task < ActiveRecord::Base
66
66
  joins(:sprint).where("sprints.end_date >= current_date")
67
67
  end
68
68
 
69
- def checked_out_by(user)
70
- where(checked_out_by_id: user.id)
69
+ def checked_out_by(user, during: nil)
70
+ raise ArgumentError, "Please specify :during which Sprint" unless during
71
+ all.merge(SprintTask.where(sprint_id: during.id))
71
72
  end
72
73
 
73
74
  def find_by_project_and_shorthand(project_slug, shorthand)
@@ -36,6 +36,10 @@ class TestRun < ActiveRecord::Base
36
36
  where arel_table[:id].not_in(ids)
37
37
  end
38
38
 
39
+ def pending
40
+ where completed_at: nil
41
+ end
42
+
39
43
  def completed
40
44
  where arel_table[:completed_at].not_eq(nil)
41
45
  end
@@ -0,0 +1,28 @@
1
+ class ProjectPresenter
2
+ include UrlHelper
3
+
4
+ def initialize(projects)
5
+ @projects = OneOrMany.new(projects)
6
+ end
7
+
8
+ def as_json(*args)
9
+ projects = @projects
10
+ projects = Houston.benchmark "[#{self.class.name.underscore}] Load objects" do
11
+ projects.load
12
+ end if projects.is_a?(ActiveRecord::Relation)
13
+ Houston.benchmark "[#{self.class.name.underscore}] Prepare JSON" do
14
+ projects.map(&method(:project_to_json))
15
+ end
16
+ end
17
+
18
+ def project_to_json(project)
19
+ { id: project.id,
20
+ name: project.name,
21
+ slug: project.slug,
22
+ color: {
23
+ name: project.color,
24
+ hex: project.color_value.hex },
25
+ props: project.extended_attributes }
26
+ end
27
+
28
+ end
@@ -20,6 +20,10 @@
20
20
  Completed: <b><%= @deploy.completed_at %></b>
21
21
  </p>
22
22
 
23
+ <p>
24
+ Succeeded: <b><%= @deploy.succeeded? %></b>
25
+ </p>
26
+
23
27
  <p>
24
28
  Duration: <b><%= @deploy.duration %></b> seconds
25
29
  </p>
@@ -37,7 +37,9 @@
37
37
  <%= image_tag pull.avatar_url, size: "32", alt: pull.username, class: "avatar avatar-not-our-user" %>
38
38
  <% end %>
39
39
  </td>
40
- <td class="pull-request-project"><%= pull.project.slug %></td>
40
+ <td class="pull-request-project">
41
+ <%= link_to pull.project.slug, pull.project.repo.pulls_url, class: "label #{pull.project.color}", target: "_blank" %>
42
+ </td>
41
43
  <td class="pull-request-title">
42
44
  <%= link_to emojify(pull.title), pull.url, target: "_blank" %>
43
45
  </td>
@@ -85,6 +87,7 @@
85
87
  $('.pull-request').each(function() {
86
88
  var $pull = $(this),
87
89
  classes = $pull.attr('class').split(' '); classes.shift();
90
+ classes = _.filter(classes, function(str) { return str; });
88
91
  $pull.toggle(_.all(classes, function(cssClass) {
89
92
  return _.contains(visibleClasses, cssClass);
90
93
  }));
@@ -1,23 +1,20 @@
1
1
  <div class="navbar navbar-fixed-top navbar-inverse">
2
2
  <div class="navbar-inner">
3
- <div class="container-fluid">
4
- <%= link_to Houston.config.title, main_app.root_url, class: "brand" %>
3
+ <%= link_to Houston.config.title, main_app.root_url, class: "brand" %>
5
4
 
6
- <ul class="nav pull-right nav-inline">
7
- <% if current_user -%>
8
- <li class="current-user dropdown">
9
- <a href="#" class="dropdown-toggle" data-toggle="dropdown"><%= avatar_for(current_user, size: 30) %> <b class="caret"></b></a>
10
- <ul class="dropdown-menu">
11
- <li><%= link_to "Settings", main_app.edit_user_path(current_user) %></li>
12
- <li><%= link_to "Sign out", main_app.destroy_user_session_path %></li>
13
- </ul>
14
- </li>
15
- <% else -%>
16
- <li><%= link_to "Sign in", main_app.new_user_session_path %></li>
17
- <% end -%>
18
- </ul>
19
-
20
- </div>
5
+ <ul class="nav pull-right nav-inline">
6
+ <% if current_user -%>
7
+ <li class="current-user dropdown">
8
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown"><%= avatar_for(current_user, size: 30) %> <b class="caret"></b></a>
9
+ <ul class="dropdown-menu">
10
+ <li><%= link_to "Settings", main_app.edit_user_path(current_user) %></li>
11
+ <li><%= link_to "Sign out", main_app.destroy_user_session_path %></li>
12
+ </ul>
13
+ </li>
14
+ <% else -%>
15
+ <li><%= link_to "Sign in", main_app.new_user_session_path %></li>
16
+ <% end -%>
17
+ </ul>
21
18
  </div>
22
19
  </div>
23
20
 
@@ -1,96 +1,96 @@
1
1
  <div class="navbar navbar-fixed-top navbar-inverse">
2
2
  <div class="navbar-inner">
3
- <div class="container-fluid">
4
- <%= link_to Houston.config.title, main_app.root_url, class: "brand" %>
5
-
6
- <ul class="nav pull-right">
7
- <% if current_user -%>
8
- <li class="current-user dropdown">
9
- <a href="#" class="dropdown-toggle" data-toggle="dropdown"><%= avatar_for(current_user, size: 30) %> <b class="caret"></b></a>
10
- <ul class="dropdown-menu">
11
- <li><%= link_to "Settings", main_app.edit_user_path(current_user) %></li>
12
- <li><%= link_to "Sign out", main_app.destroy_user_session_path %></li>
13
- </ul>
14
- </li>
15
- <% else -%>
16
- <li><%= link_to "Sign in", main_app.new_user_session_path %></li>
17
- <% end -%>
18
- </ul>
19
- <ul class="nav">
20
-
21
- <% if current_user -%>
22
- <li class="dropdown current-project <%= current_project && current_project.color %>">
23
- <a href="#" title="Feedback" class="dropdown-toggle" data-toggle="dropdown">
24
- <%= current_project ? current_project.name : "Select Project" %>
25
- </a>
26
- <ul class="dropdown-menu">
27
- <% followed_projects.each do |project| %>
28
- <% if project == current_project %>
29
- <li class="current">
30
- <b class="bubble <%= project.color %>"></b> <%= project.name %></a>
31
- </li>
32
- <% else %>
33
- <li>
34
- <% path = if !current_feature
35
- # we're not on a project page,
36
- # just refresh the page and set the project
37
- "?project=#{project.slug}"
38
- elsif !project.features.include?(current_feature)
39
- # we're using a feature that this project
40
- # doesn't support. Navigate to the root URL
41
- # and set the project
42
- main_app.root_path(project: project.slug)
43
- else
44
- feature_path(project, current_feature)
45
- end %>
46
- <%= link_to path do %>
47
- <b class="bubble <%= project.color %>"></b> <%= project.name %></a>
48
- <% end %>
49
- </li>
50
- <% end %>
3
+ <%= link_to Houston.config.title, main_app.root_url, class: "brand" %>
4
+
5
+ <ul class="nav pull-right">
6
+ <% if current_user -%>
7
+ <li class="current-user dropdown">
8
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown"><%= avatar_for(current_user, size: 30) %> <b class="caret"></b></a>
9
+ <ul class="dropdown-menu">
10
+ <li><%= link_to "Settings", main_app.edit_user_path(current_user) %></li>
11
+ <li><%= link_to "Sign out", main_app.destroy_user_session_path %></li>
12
+ </ul>
13
+ </li>
14
+ <% else -%>
15
+ <li><%= link_to "Sign in", main_app.new_user_session_path %></li>
16
+ <% end -%>
17
+ </ul>
18
+ <ul class="nav">
19
+
20
+ <% Houston.config.navigation.each do |navigation| %><%= render_navigation navigation %><% end %>
21
+
22
+ <li class="divider-vertical"></li>
23
+
24
+ <% if can?(:read, Project) -%>
25
+ <%= render_nav_link "Projects", main_app.projects_path, icon: "fa-database" %>
26
+ <% end -%>
27
+
28
+ <% if can?(:read, User) -%>
29
+ <%= render_nav_link "Team", main_app.users_path, icon: "fa-user" %>
30
+ <% end -%>
31
+
32
+ <% if can?(:read, :job) -%>
33
+ <%= render_nav_link "Jobs", main_app.jobs_path, icon: "fa-user" %>
34
+ <% end -%>
35
+
36
+ </ul>
37
+ </div>
38
+ </div>
39
+
40
+ <% if current_user -%>
41
+ <div class="navbar project-navbar <%= current_project.color %>">
42
+ <ul class="nav">
43
+
44
+ <li class="dropdown current-project <%= current_project && current_project.color %>">
45
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">
46
+ <%= current_project ? current_project.name : "Select Project" %>
47
+ </a>
48
+ <ul class="dropdown-menu">
49
+ <% followed_projects.each do |project| %>
50
+ <% if project == current_project %>
51
+ <li class="current">
52
+ <b class="bubble <%= project.color %>"></b> <%= project.name %></a>
53
+ </li>
54
+ <% else %>
55
+ <li>
56
+ <% path = if !current_feature
57
+ # we're not on a project page,
58
+ # just refresh the page and set the project
59
+ "?project=#{project.slug}"
60
+ elsif !project.features.include?(current_feature)
61
+ # we're using a feature that this project
62
+ # doesn't support. Navigate to the root URL
63
+ # and set the project
64
+ main_app.root_path(project: project.slug)
65
+ else
66
+ feature_path(project, current_feature)
67
+ end %>
68
+ <%= link_to path do %>
69
+ <b class="bubble <%= project.color %>"></b> <%= project.name %></a>
51
70
  <% end %>
52
- </ul>
53
- </li>
71
+ </li>
72
+ <% end %>
54
73
  <% end %>
55
-
56
-
57
-
58
- <% Houston.config.navigation.each do |navigation| %><%= render_navigation navigation %><% end %>
59
-
60
- <li class="divider-vertical"></li>
61
-
62
- <% if can?(:read, Project) -%>
63
- <%= render_nav_link "Projects", main_app.projects_path, icon: "fa-database" %>
64
- <% end -%>
65
-
66
- <% if can?(:read, User) -%>
67
- <%= render_nav_link "Team", main_app.users_path, icon: "fa-user" %>
68
- <% end -%>
69
-
70
- <% if can?(:read, :job) -%>
71
- <%= render_nav_link "Jobs", main_app.jobs_path, icon: "fa-user" %>
72
- <% end -%>
73
-
74
74
  </ul>
75
- </div>
76
- </div>
77
- </div>
75
+ </li>
78
76
 
79
- <% if current_project && current_project.persisted? %>
80
- <div class="project-navbar <%= current_project.color %>">
81
- <% if current_project.features.any? %>
82
- <ul class="nav">
83
- <% current_project.features.each do |feature| %>
84
- <%= render_nav_for_feature(feature) %>
85
- <% end %>
86
- </ul>
87
- <% else %>
88
- <div class="project-no-features">
89
- No features are enabled for <%= current_project.name %>.
90
- <% if can?(:update, current_project) %>
91
- You can enable features in <%= link_to "Project Settings", main_app.edit_project_path(current_project) %>.
77
+ <% if current_project && current_project.persisted? %>
78
+ <% if current_project.features.any? %>
79
+ <% current_project.features.each do |feature| %>
80
+ <% if feature == :settings %>
81
+ <li class="divider-vertical"></li>
82
+ <% end %>
83
+ <%= render_nav_for_feature(feature) %>
84
+ <% end %>
85
+ <% else %>
86
+ <li class="project-no-features">
87
+ No features are enabled for <%= current_project.name %>.
88
+ <% if can?(:update, current_project) %>
89
+ You can enable features in <%= link_to "Project Settings", main_app.edit_project_path(current_project) %>.
90
+ <% end %>
91
+ </li>
92
92
  <% end %>
93
- </div>
94
- <% end %>
93
+ <% end %>
94
+ </ul>
95
95
  </div>
96
96
  <% end %>
@@ -27,10 +27,10 @@
27
27
  <link href="images/apple-touch-icon-114x114.png" rel="apple-touch-icon" sizes="114x114">
28
28
  </head>
29
29
  <body class="dashboard">
30
- <div id="container" <% if params[:zoom].to_f > 0 %> style="transform: scale(<%= params[:zoom].to_f %>); transform-origin: 50% 0; -webkit-transform: scale(<%= params[:zoom].to_f %>); -webkit-transform-origin: 50% 0; -moz-transform: scale(<%= params[:zoom].to_f %>); -moz-transform-origin: 50% 0;" <% end %>>
30
+ <div id="container" <% if params[:zoom].to_f > 0 %> style="transform: scale(<%= params[:zoom].to_f %>); transform-origin: 50% 0; -webkit-transform: scale(<%= params[:zoom].to_f %>); -webkit-transform-origin: 50% 0; -moz-transform: scale(<%= params[:zoom].to_f %>); -moz-transform-origin: 50% 0; width: <%= (1 / params[:zoom].to_f) * 100 %>%; height: <%= (1 / params[:zoom].to_f) * 100 %>%;" <% end %>>
31
31
  <%= yield -%>
32
32
  </div>
33
-
33
+
34
34
  <!-- Le javascript
35
35
  ================================================== -->
36
36
  <!-- Placed at the end of the document so the pages load faster -->
@@ -10,21 +10,31 @@
10
10
  <th class="test-name sort-asc">Name</th>
11
11
  <th class="test-passes">Passes</th>
12
12
  <th class="test-fails">Fails</th>
13
+ <th class="test-graph">Graph</th>
14
+ <th class="test-duration">Duration</th>
13
15
  <td class="table-margin"></td>
14
16
  </tr>
15
17
  </thead>
16
18
  <tbody>
17
- <% @tests.each do |(id, suite, name, passes, fails)| %>
19
+ <% @tests.each do |test| %>
18
20
  <tr class="test">
19
21
  <td class="table-margin"></td>
20
22
  <td class="test-name">
21
- <%= link_to project_test_url(slug: @project.slug, id: id) do %>
22
- <span class="test-suite-name"><%= suite.gsub("__", "::") %></span>
23
- <span class="test-name"><%= name.to_s.gsub(/^(test :|: )/, "") %></span>
23
+ <%= link_to project_test_url(slug: @project.slug, id: test[:id]) do %>
24
+ <span class="test-suite-name"><%= test[:suite].gsub("__", "::") %></span>
25
+ <span class="test-name"><%= test[:name].to_s.gsub(/^(test :|: )/, "") %></span>
24
26
  <% end %>
25
27
  </td>
26
- <td class="test-passes <%= "zero" if passes.zero? %>"><%= passes %></td>
27
- <td class="test-fails <%= "zero" if fails.zero? %>"><%= fails %></td>
28
+ <td class="test-passes"><%= test_results_pass_count(test)%></td>
29
+ <td class="test-fails"><%= test_results_fail_count(test)%></td>
30
+ <td class="test-graph" data-position="<%= test_results_count(test) %>"><%= test_results_graph(test) %></td>
31
+ <td class="test-duration">
32
+ <span class="test-duration-average"><%= test[:duration_avg].to_i %>ms</span>
33
+ <span class="test-duration-range">
34
+ <%= test[:duration5].to_i %>ms &ndash; <%= test[:duration95].to_i %>ms
35
+ </span>
36
+ </td>
37
+ <td class="table-margin"></td>
28
38
  </tr>
29
39
  <% end %>
30
40
  </tbody>
@@ -34,7 +44,12 @@
34
44
  <% content_for :javascripts do %>
35
45
  <script type="text/javascript">
36
46
  $(function() {
37
- $('#project_tests').tablesorter();
47
+ $('#project_tests').tablesorter({
48
+ headers: {
49
+ 4: {sorter: 'property'},
50
+ 5: {sorter: 'digit'}
51
+ }
52
+ });
38
53
  });
39
54
  </script>
40
55
  <% end %>
@@ -0,0 +1,65 @@
1
+ <% if @deploys.none? %>
2
+
3
+ <div class="alert alert-info">
4
+ There have been no deploys of <b><%= @project.name %></b> to <b><%= @environment %></b> recorded.
5
+ </div>
6
+
7
+ <% else %>
8
+
9
+ <% date = nil %>
10
+ <div id="<%= @project.slug %>_changelog" class="-houston-changelog">
11
+ <% @deploys.each do |deploy| %>
12
+ <% deploy.project = @project; release = deploy.release %>
13
+ <% next if release.nil? && deploy.commit.nil? %>
14
+
15
+ <% if date.nil? || date != deploy.date %>
16
+ <% if date %></div><% end %><%# end .-houston-changelog-day %>
17
+ <div class="-houston-changelog-day">
18
+ <h4><%= format_release_date(date = deploy.date) %></h4>
19
+ <% end %>
20
+
21
+ <% unless release %>
22
+ <div class="release release-deploy">
23
+ <p class="release-header">
24
+ <%= deploy.completed_at.strftime("%l:%M %p") %>&nbsp;&nbsp;
25
+ <% deploy.commit.project = @project %>
26
+ Deploy to <%= link_to_commit deploy.commit, style: "float: none;" %>
27
+
28
+ <span style="float: right;">
29
+ <%= link_to "New Release", new_release_path(slug: @project.slug, environment: @environment, deploy_id: deploy.id), class: "btn btn-mini btn-default" %>
30
+ </span>
31
+ </div>
32
+ <% else %>
33
+ <%= div_for(release) do %>
34
+ <% release.project = @project %>
35
+ <p class="release-header">
36
+ <%= release.released_at.strftime("%l:%M %p") %>&nbsp;&nbsp;
37
+ <%= link_to "details &rarr;".html_safe, release_url(release) %>
38
+
39
+ <% if can?(:read, @project.commits.build) %>
40
+ <span class="commit-range"><%= link_to_release_commit_range(release) %></span>
41
+ <% end %>
42
+ </p>
43
+
44
+ <% if release.release_changes.any? %>
45
+ <div class="release-changes">
46
+ <% ordered_by_tag(release.release_changes).each do |change| %>
47
+ <% if can?(:read, change) %>
48
+ <div class="change">
49
+ <%= format_change_tag change.tag %>
50
+ <div class="change-summary"><%= format_change(change) %></div>
51
+ </div>
52
+ <% end %>
53
+ <% end %>
54
+ </div>
55
+ <% else %>
56
+ <div class="release-no-changes">No changes</div>
57
+ <% end %>
58
+ <% end %>
59
+ <% end %>
60
+
61
+ <% end %>
62
+ <% if date %></div><% end %><%# end .-houston-changelog-day %>
63
+ </div>
64
+
65
+ <% end %>
@@ -0,0 +1,47 @@
1
+ <% if @releases.none? %>
2
+
3
+ <div class="alert alert-info">
4
+ No results found.
5
+ </div>
6
+
7
+ <% else %>
8
+
9
+ <% date = nil %>
10
+ <div id="<%= @project.slug %>_changelog" class="-houston-changelog release-search-result">
11
+ <% @releases.each do |release| %>
12
+ <% release.project = @project %>
13
+
14
+ <% if date.nil? || date != release.date %>
15
+ <% if date %></div><% end %><%# end .-houston-changelog-day %>
16
+ <div class="-houston-changelog-day">
17
+ <h4><%= format_release_date(date = release.date) %></h4>
18
+ <% end %>
19
+
20
+ <%= div_for(release) do %>
21
+ <% release.project = @project %>
22
+ <p class="release-header">
23
+ <%= release.released_at.strftime("%l:%M %p") %>&nbsp;&nbsp;
24
+ <%= link_to "details &rarr;".html_safe, release_url(release) %>
25
+
26
+ <% if can?(:read, @project.commits.build) %>
27
+ <span class="commit-range"><%= link_to_release_commit_range(release) %></span>
28
+ <% end %>
29
+ </p>
30
+
31
+ <div class="release-changes">
32
+ <% ordered_by_tag(release.release_changes).each do |change| %>
33
+ <% if can?(:read, change) %>
34
+ <div class="change">
35
+ <%= format_change_tag change.tag %>
36
+ <div class="change-summary"><%= format_change(change) %></div>
37
+ </div>
38
+ <% end %>
39
+ <% end %>
40
+ </div>
41
+ <% end %>
42
+
43
+ <% end %>
44
+ <% if date %></div><% end %><%# end .-houston-changelog-day %>
45
+ </div>
46
+
47
+ <% end %>