houston-core 0.5.6 → 0.6.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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +94 -69
  3. data/app/adapters/houston/adapters/deployment/engineyard.rb +4 -3
  4. data/app/adapters/houston/adapters/version_control/git_adapter.rb +36 -42
  5. data/app/adapters/houston/adapters/version_control/git_adapter/github_repo.rb +2 -2
  6. data/app/adapters/houston/adapters/version_control/git_adapter/remote_repo.rb +23 -11
  7. data/app/adapters/houston/adapters/version_control/git_adapter/repo.rb +18 -8
  8. data/app/adapters/houston/adapters/version_control/null_repo.rb +8 -0
  9. data/app/assets/javascripts/core/handlebars_helpers.coffee +3 -3
  10. data/app/assets/stylesheets/application/github_repos.scss +77 -0
  11. data/app/assets/stylesheets/application/navigation.scss +2 -0
  12. data/app/assets/stylesheets/application/pull_requests.scss +44 -58
  13. data/app/assets/stylesheets/core/avatars.scss +5 -0
  14. data/app/assets/stylesheets/core/colors.scss.erb +10 -7
  15. data/app/concerns/commit_synchronizer.rb +3 -0
  16. data/app/controllers/github/pulls_controller.rb +12 -0
  17. data/app/controllers/project_tests_controller.rb +3 -3
  18. data/app/controllers/projects_controller.rb +45 -1
  19. data/app/controllers/releases_controller.rb +42 -26
  20. data/app/helpers/application_helper.rb +8 -0
  21. data/app/helpers/avatar_helper.rb +2 -2
  22. data/app/helpers/commit_helper.rb +2 -2
  23. data/app/helpers/oembed_helper.rb +8 -0
  24. data/app/helpers/project_helper.rb +4 -5
  25. data/app/helpers/release_helper.rb +11 -0
  26. data/app/helpers/timeline_helper.rb +1 -1
  27. data/app/helpers/url_helper.rb +0 -18
  28. data/app/interactors/cache_key_dependencies.rb +28 -0
  29. data/app/jobs/sync_all_tickets_job.rb +1 -0
  30. data/app/mailers/view_mailer.rb +0 -1
  31. data/app/models/commit.rb +1 -1
  32. data/app/models/github/pull_request.rb +82 -26
  33. data/app/models/github/pull_request_event.rb +2 -2
  34. data/app/models/milestone.rb +1 -0
  35. data/app/models/project.rb +14 -0
  36. data/app/models/project_dependencies.rb +5 -3
  37. data/app/models/task.rb +1 -1
  38. data/app/models/user.rb +41 -0
  39. data/app/views/commits/show.html.erb +9 -1
  40. data/app/views/github/pulls/index.html.erb +102 -0
  41. data/app/views/project_notification/new_release.html.erb +6 -0
  42. data/app/views/project_tickets/index.xls.erb +0 -7
  43. data/app/views/projects/_form.html.erb +29 -17
  44. data/app/views/projects/index.html.erb +3 -3
  45. data/app/views/projects/new_from_github.html.erb +67 -0
  46. data/app/views/releases/_commits.html.erb +1 -1
  47. data/app/views/releases/show.html.erb +9 -0
  48. data/app/views/users/_form.html.erb +35 -19
  49. data/config/application.rb +12 -0
  50. data/config/initializers/mime_types.rb +1 -0
  51. data/config/routes.rb +17 -3
  52. data/db/migrate/20151201042126_require_projects_to_have_name_and_slug.rb +6 -0
  53. data/db/migrate/20151202005557_add_head_sha_to_projects.rb +24 -0
  54. data/db/migrate/20151202011812_require_projects_to_have_color.rb +13 -0
  55. data/db/migrate/20151205204922_require_project_slugs_to_be_unique.rb +5 -0
  56. data/db/migrate/20151205214647_add_avatar_url_to_pull_requests.rb +5 -0
  57. data/db/migrate/20151209004458_add_json_labels_to_pull_requests.rb +5 -0
  58. data/db/migrate/20151209030113_add_timestamps_to_pull_requests.rb +6 -0
  59. data/db/structure.sql +31 -5
  60. data/houston.gemspec +7 -7
  61. data/lib/configuration.rb +3 -2
  62. data/lib/houston/version.rb +1 -1
  63. data/lib/rack/oembed.rb +23 -0
  64. data/templates/new-instance/config/jobs/cache_key_dependencies.rb +3 -0
  65. data/templates/new-instance/config/triggers/tests/slack_when_analyzed.rb +1 -4
  66. data/templates/new-instance/config/triggers/tests/slack_when_completed.rb +1 -1
  67. data/templates/new-instance/lib/slack_helpers.rb +1 -1
  68. data/test/integration/ticket_tasks_api_test.rb +1 -1
  69. data/test/unit/adapters/git_adapter_test.rb +29 -8
  70. data/test/unit/adapters/version_control_adapters_api_test.rb +2 -0
  71. data/test/unit/controllers/hooks_controller_test.rb +4 -4
  72. data/test/unit/models/commit_test.rb +2 -2
  73. data/test/unit/models/project_test.rb +2 -2
  74. data/test/unit/models/pull_request_test.rb +9 -4
  75. data/test/unit/models/task_test.rb +1 -1
  76. data/test/unit/models/ticket_test.rb +1 -1
  77. metadata +31 -16
@@ -35,8 +35,8 @@ module Github
35
35
  return unless pr && pr.persisted?
36
36
 
37
37
  case action
38
- when "labeled" then pr.add_label! payload["label"]["name"], as: actor
39
- when "unlabeled" then pr.remove_label! payload["label"]["name"], as: actor
38
+ when "labeled" then pr.add_label! payload["label"], as: actor
39
+ when "unlabeled" then pr.remove_label! payload["label"], as: actor
40
40
  end
41
41
  end
42
42
 
@@ -30,6 +30,7 @@ class Milestone < ActiveRecord::Base
30
30
  arel_table[:tickets_count].gt(0).and(
31
31
  arel_table[:closed_tickets_count].eq(arel_table[:tickets_count])))
32
32
  end
33
+ alias :closed :completed
33
34
 
34
35
  def uncompleted
35
36
  where(
@@ -15,6 +15,7 @@ class Project < ActiveRecord::Base
15
15
  has_many :roles, -> { joins(:user).merge(User.unretired) }, dependent: :destroy, validate: false
16
16
  has_many :value_statements, dependent: :destroy
17
17
  has_many :pull_requests, class_name: "Github::PullRequest"
18
+ belongs_to :head, class_name: "Commit", foreign_key: "head_sha", primary_key: "sha"
18
19
 
19
20
  Houston.config.project_roles.each do |role|
20
21
  collection_name = role.downcase.gsub(' ', '_').pluralize
@@ -27,6 +28,9 @@ class Project < ActiveRecord::Base
27
28
  reject_if: proc { |attrs| attrs[:user_id].blank? or attrs[:name].blank? }
28
29
  accepts_nested_attributes_for :value_statements, :allow_destroy => true
29
30
 
31
+ before_validation :generate_default_slug, :set_default_color
32
+ validates_presence_of :name, :slug, :color
33
+
30
34
 
31
35
 
32
36
  has_adapter :TicketTracker,
@@ -248,4 +252,14 @@ class Project < ActiveRecord::Base
248
252
 
249
253
 
250
254
 
255
+ private
256
+
257
+ def generate_default_slug
258
+ self.slug = self.name.to_s.underscore.gsub("/", "-").dasherize.gsub(".", "").gsub(/\s+/, "_") unless slug
259
+ end
260
+
261
+ def set_default_color
262
+ self.color = "default" unless color
263
+ end
264
+
251
265
  end
@@ -41,10 +41,12 @@ class ProjectDependencies < SimpleDelegator
41
41
  end
42
42
 
43
43
  def lockfile
44
- return "" if repo.nil?
44
+ return @lockfile if defined?(@lockfile)
45
+ return "" unless repo.exists?
45
46
 
46
- @lockfile = read_file("Gemfile.lock", commit: repo.branch("master")) unless defined?(@lockfile)
47
- @lockfile
47
+ Houston.benchmark "[project_dependencies.lockfile] #{slug}" do
48
+ @lockfile = read_file("Gemfile.lock", commit: head_sha)
49
+ end
48
50
  rescue Houston::Adapters::VersionControl::FileNotFound
49
51
  @lockfile = ""
50
52
  end
@@ -123,7 +123,7 @@ class Task < ActiveRecord::Base
123
123
 
124
124
 
125
125
 
126
- def committed!(commit)
126
+ def mark_committed!(commit)
127
127
  update_column :first_commit_at, commit.authored_at unless committed?
128
128
  Houston.observer.fire "task:committed", self
129
129
  end
@@ -117,6 +117,47 @@ class User < ActiveRecord::Base
117
117
 
118
118
 
119
119
 
120
+ # Extract to Houston::GitHub
121
+ # ------------------------------------------------------------------------- #
122
+
123
+ def self.find_by_github_username(username)
124
+ # If we can already identify the user who has the given username, return them
125
+ user = ::User.where(["view_options->'github_username' = ?", username]).first
126
+ return user if user
127
+
128
+ # Look up the email address of the GitHub user and see if we can
129
+ # identify the Houston user by the GitHub user's email address.
130
+ user = Houston.github.user(username)
131
+ user = find_by_email_address user.email if user
132
+
133
+ # We couldn't find the user by their email address, now
134
+ # we'll look at their nicknames
135
+ user = find_by_nickname username unless user
136
+
137
+ # We've failed to identify this user
138
+ unless user
139
+ Rails.logger.warn "\e[31m[pulls] Unable to identify a user for the GitHub username \e[1m#{username}\e[0m"
140
+ return nil
141
+ end
142
+
143
+ # If we have identified the user, store their username so that
144
+ # we can skip the email-lookup step in the future.
145
+ user.set_github_username! username
146
+ user
147
+ end
148
+
149
+ def github_username
150
+ view_options["github_username"]
151
+ end
152
+
153
+ def set_github_username!(username)
154
+ update_column :view_options, view_options.merge("github_username" => username)
155
+ end
156
+
157
+ # ------------------------------------------------------------------------- #
158
+
159
+
160
+
120
161
  # LDAP Overrides
121
162
  # ------------------------------------------------------------------------- #
122
163
 
@@ -1,3 +1,11 @@
1
+ <% content_for :meta do %>
2
+ <%= tag "meta", property: "og:type", content: "website" %>
3
+ <%= tag "meta", property: "og:site_name", content: "#{@project.slug} / commit" %>
4
+ <%= tag "meta", property: "og:title", content: @commit.summary %>
5
+ <%= tag "meta", property: "og:description", content: @commit.description %>
6
+ <%= tag "meta", property: "og:url", content: commit_url(@commit) %>
7
+ <% end %>
8
+
1
9
  <%= render partial: "projects/header", locals: {project: @project, postfix: @commit.sha[0...8]} %>
2
10
 
3
11
  <div class="commit-profile">
@@ -16,7 +24,7 @@
16
24
  <%= div_for(release) do %>
17
25
  <p class="release-header">
18
26
  <%= release.released_at.strftime("%l:%M %p") %>&nbsp;&nbsp;
19
- <%= link_to "details &rarr;".html_safe, release %>
27
+ <%= link_to "details &rarr;".html_safe, release_url(release) %>
20
28
 
21
29
  <% if can?(:read, @project.commits.build) %>
22
30
  <span class="commit-range"><%= link_to_release_commit_range(release) %></span>
@@ -0,0 +1,102 @@
1
+ <% content_for :title do %>
2
+ <h1 class="project-banner space-below">
3
+ <span id="pull_request_count" class="light"><%= @pulls.count %></span>
4
+ Pull Requests
5
+ </h1>
6
+ <% end %>
7
+
8
+ <div class="pull-request-labels pull-request-labels-toggle">
9
+ <% @labels.each do |label| %>
10
+ <%= check_box_tag label["name"], "1", @selected_labels.member?(label["name"]) %><%= label_tag label["name"] do %>
11
+ <%= pull_request_label(label) %>
12
+ <% end %>
13
+ <% end %>
14
+ </div>
15
+
16
+ <div class="nomargin">
17
+ <table id="pull_requests" class="table table-sortable table-striped">
18
+ <thead>
19
+ <tr>
20
+ <td class="table-margin"></td>
21
+ <td class="pull-request-avatar"></td>
22
+ <th class="pull-request-project">Project</th>
23
+ <th class="pull-request-title">Title</th>
24
+ <th class="pull-request-labels">Labels</th>
25
+ <th class="pull-request-age sort-asc">Age</th>
26
+ <td class="table-margin"></td>
27
+ </tr>
28
+ </thead>
29
+ <tbody>
30
+ <% @pulls.each do |pull| %>
31
+ <tr class="pull-request <%= pull.labels.map { |label| "pull-request-label-#{label["name"]}" }.join(" ") %>">
32
+ <td class="table-margin"></td>
33
+ <td class="pull-request-avatar">
34
+ <% if pull.user %>
35
+ <%= avatar_for pull.user, size: 32 %>
36
+ <% elsif pull.avatar_url %>
37
+ <%= image_tag pull.avatar_url, size: "32", alt: pull.username, class: "avatar avatar-not-our-user" %>
38
+ <% end %>
39
+ </td>
40
+ <td class="pull-request-project"><%= pull.project.slug %></td>
41
+ <td class="pull-request-title">
42
+ <%= link_to emojify(pull.title), pull.url, target: "_blank" %>
43
+ </td>
44
+ <td class="pull-request-labels">
45
+ <% pull.json_labels.each do |label| %>
46
+ <%= pull_request_label(label) %>
47
+ <% end %>
48
+ </td>
49
+ <td class="pull-request-age" data-timestamp="<%= pull.created_at.iso8601 if pull.created_at %>">
50
+ <%= format_time_ago(pull.created_at) %>
51
+ </td>
52
+ <td class="table-margin"></td>
53
+ </tr>
54
+ <% end %>
55
+ </tbody>
56
+ </table>
57
+ </div>
58
+
59
+ <% content_for :javascripts do %>
60
+ <script type="text/javascript">
61
+ $.tablesorter.addParser({
62
+ id: 'labels',
63
+ type: 'text',
64
+ is: function(s) { return false; }, // return false so this parser is not auto detected
65
+ format: function(s, table, td) {
66
+ var $td = $(td),
67
+ labels = _.map($td.find('.label'), function(el) { return $(el).text() });
68
+ return labels.sort().join(',');
69
+ }
70
+ });
71
+
72
+ $(function() {
73
+ $('#pull_requests').tablesorter({
74
+ headers: {
75
+ 4: {sorter: 'labels'},
76
+ 5: {sorter: 'timestamp'}
77
+ }
78
+ });
79
+
80
+ function filterPullRequests() {
81
+ var visibleClasses = $('.pull-request-labels-toggle :checkbox:checked').map(function(el) {
82
+ return 'pull-request-label-' + $(this).attr('name');
83
+ });
84
+
85
+ $('.pull-request').each(function() {
86
+ var $pull = $(this),
87
+ classes = $pull.attr('class').split(' '); classes.shift();
88
+ $pull.toggle(_.all(classes, function(cssClass) {
89
+ return _.contains(visibleClasses, cssClass);
90
+ }));
91
+ });
92
+
93
+ $('#pull_request_count').html($('.pull-request:visible').length);
94
+ }
95
+ filterPullRequests();
96
+
97
+ $('.pull-request-labels-toggle :checkbox').click(function(e) {
98
+ filterPullRequests();
99
+ });
100
+ });
101
+ </script>
102
+ <% end %>
@@ -29,3 +29,9 @@
29
29
  </span>
30
30
  </p>
31
31
  <% end %>
32
+
33
+ <% unless @release.new_record? %>
34
+ <p style="margin-top: 3em;">
35
+ <%= link_to "permalink", release_url(@release) %>
36
+ </p>
37
+ <% end %>
@@ -49,13 +49,11 @@
49
49
  <Column ss:AutoFitWidth="0" ss:Width="700"/>
50
50
  <Column ss:AutoFitWidth="0" ss:Width="85"/>
51
51
  <Column ss:AutoFitWidth="0" ss:Width="85"/>
52
- <Column ss:AutoFitWidth="0" ss:Width="85"/>
53
52
  <Row ss:StyleID="s65">
54
53
  <Cell><Data ss:Type="String">Number</Data></Cell>
55
54
  <Cell><Data ss:Type="String">Type</Data></Cell>
56
55
  <Cell><Data ss:Type="String">Reporter Name</Data></Cell>
57
56
  <Cell><Data ss:Type="String">Summary</Data></Cell>
58
- <Cell><Data ss:Type="String">Effort</Data></Cell>
59
57
  <Cell><Data ss:Type="String">Opened</Data></Cell>
60
58
  <Cell><Data ss:Type="String">Closed</Data></Cell>
61
59
  </Row>
@@ -65,11 +63,6 @@
65
63
  <Cell><Data ss:Type="String"><%= ticket.type %></Data></Cell>
66
64
  <Cell><Data ss:Type="String"><%= ticket.reporter_name %></Data></Cell>
67
65
  <Cell><Data ss:Type="String"><%= ticket.summary %></Data></Cell>
68
- <% if ticket.effort.blank? %>
69
- <Cell />
70
- <% else %>
71
- <Cell><Data ss:Type="Number"><%= ticket.effort %></Data></Cell>
72
- <% end %>
73
66
  <Cell ss:StyleID="s64"><Data ss:Type="DateTime"><%= xls_time ticket.opened_at %></Data></Cell>
74
67
  <% if ticket.closed_at %>
75
68
  <Cell ss:StyleID="s64"><Data ss:Type="DateTime"><%= xls_time ticket.closed_at %></Data></Cell>
@@ -1,23 +1,23 @@
1
- <%= form_for @project, :html => { :class => 'form-horizontal' } do |f| %>
1
+ <%= form_for @project, :html => { :class => "form-horizontal" } do |f| %>
2
2
  <fieldset>
3
3
  <div class="control-group">
4
- <%= f.label :name, :class => 'control-label' %>
4
+ <%= f.label :name, :class => "control-label" %>
5
5
  <div class="controls">
6
- <%= f.text_field :name, :class => 'text_field' %>
6
+ <%= f.text_field :name, :class => "text_field" %>
7
7
  </div>
8
8
  </div>
9
9
 
10
10
  <div class="control-group">
11
- <%= f.label :slug, :class => 'control-label' %>
11
+ <%= f.label :slug, :class => "control-label" %>
12
12
  <div class="controls">
13
- <%= f.text_field :slug, :class => 'text_field' %>
13
+ <%= f.text_field :slug, :class => "text_field" %>
14
14
  </div>
15
15
  </div>
16
16
 
17
17
  <div class="control-group">
18
- <%= f.label :color, :class => 'control-label' %>
18
+ <%= f.label :color, :class => "control-label" %>
19
19
  <div class="controls">
20
- <%= f.select :color, Houston.config.project_colors.keys.map { |name| [name.titleize, name] }, :class => 'select_field' %>
20
+ <%= f.select :color, [["None", "default"]] + Houston.config.project_colors.keys.map { |name| [name.titleize, name] }, :class => "select_field" %>
21
21
  <% if Project.count > 0 %>
22
22
  (Unused colors: <%= (Houston.config.project_colors.keys - Project.pluck(:color).uniq).map(&:titleize).join(", ") %>)
23
23
  <% end %>
@@ -42,7 +42,7 @@
42
42
  <div class="project-adapter-fields" data-adapter="<%= name %>">
43
43
  <% adapter.namespace.adapter(name).parameters.each do |parameter| %>
44
44
  <% errors = @project.errors[parameter].flatten %>
45
- <%= label_tag "project[extended_attributes][#{parameter}]", parameter.to_s.gsub('_', ' ').gsub(/\b('?[a-z])/) { $1.capitalize } %>
45
+ <%= label_tag "project[extended_attributes][#{parameter}]", parameter.to_s.gsub("_", " ").gsub(/\b('?[a-z])/) { $1.capitalize } %>
46
46
  <%= text_field_tag "project[extended_attributes][#{parameter}]", @project.extended_attributes[parameter.to_s], :class => "text_field #{"error" if errors.any?}" %>
47
47
  <% if errors.any? %>
48
48
  <span class="help-inline">&nbsp;<%= errors.to_sentence %></span>
@@ -58,9 +58,9 @@
58
58
  <hr />
59
59
 
60
60
  <div class="control-group">
61
- <%= f.label :code_climate_repo_token, "Code Climate Repo Token", :class => 'control-label' %>
61
+ <%= f.label :code_climate_repo_token, "Code Climate Repo Token", :class => "control-label" %>
62
62
  <div class="controls">
63
- <%= f.text_field :code_climate_repo_token, :class => 'text_field' %>
63
+ <%= f.text_field :code_climate_repo_token, :class => "text_field" %>
64
64
  </div>
65
65
  </div>
66
66
 
@@ -95,7 +95,7 @@
95
95
  <hr />
96
96
 
97
97
  <div class="control-group">
98
- <%= f.label :roles, "Teammates", :class => 'control-label' %>
98
+ <%= f.label :roles, "Teammates", :class => "control-label" %>
99
99
  <div class="controls changes-nested-editor">
100
100
  <%= f.nested_editor_for :roles do |f| -%>
101
101
  <%= f.select :user_id, [nil] + User.all.map { |user| [user.name, user.id] } %>
@@ -105,16 +105,19 @@
105
105
  </div>
106
106
 
107
107
  <div class="control-group">
108
- <%= f.label :min_passing_verdicts, "Min. Passing Verdicts", :class => 'control-label' %>
108
+ <%= f.label :min_passing_verdicts, "Min. Passing Verdicts", :class => "control-label" %>
109
109
  <div class="controls">
110
- <%= f.text_field :min_passing_verdicts, :class => 'text_field' %>
110
+ <%= f.text_field :min_passing_verdicts, :class => "text_field" %>
111
111
  </div>
112
112
  </div>
113
113
 
114
114
  <div class="form-actions">
115
- <%= f.submit nil, :class => 'btn btn-primary' %>
116
- <%= link_to 'Cancel', projects_path, :class => 'btn' %>
117
- <%= link_to 'Retire', retire_project_path(@project), :method => 'put', :class => 'btn btn-danger' if @project.persisted? && can?(:destroy, @project) %>
115
+ <%= f.submit nil, :class => "btn btn-primary" %>
116
+ <%= link_to "Cancel", projects_path, :class => "btn" %>
117
+
118
+ <% if @project.persisted? && can?(:destroy, @project) %>
119
+ <button class="btn btn-delete btn-danger" id="retire_project_button">Retire</button>
120
+ <% end %>
118
121
  </div>
119
122
  </fieldset>
120
123
  <% end %>
@@ -125,7 +128,7 @@
125
128
  NestedEditorFor.init();
126
129
 
127
130
  function dasherize(string) {
128
- return inflect.dasherize(inflect.underscore(string));
131
+ return inflect.dasherize(inflect.underscore(string)).replace(/\./g, '').replace(/\s+/g, '_');
129
132
  }
130
133
 
131
134
  var $name = $('#project_name'),
@@ -166,6 +169,15 @@
166
169
 
167
170
  $('.project-feature').click(showProjectFeatureParameters);
168
171
  showProjectFeatureParameters();
172
+
173
+ <% if @project.persisted? && can?(:destroy, @project) %>
174
+ $('#retire_project_button').click(function(e) {
175
+ e.preventDefault();
176
+ $.put('<%= retire_project_path(@project) %>')
177
+ .success(function() { window.location = '/projects'; })
178
+ .error(function() { console.log(arguments); });
179
+ });
180
+ <% end %>
169
181
  });
170
182
  </script>
171
183
  <% end %>
@@ -2,7 +2,8 @@
2
2
  <h1 class="project-banner space-below">
3
3
  Projects
4
4
  <%= link_to "New Project", new_project_path, :class => "btn btn-primary" if can?(:create, Project) %>
5
-
5
+ <%= link_to "Add Projects from GitHub", add_github_projects_path, :class => "btn btn-primary" if can?(:create, Project) %>
6
+
6
7
  <%= render partial: "projects/keyboard_shortcuts" %>
7
8
  </h1>
8
9
  <% end %>
@@ -75,9 +76,8 @@
75
76
  </td>
76
77
 
77
78
  <% KeyDependency.all.each do |dependency| %>
78
- <% version = ProjectDependency.new(project, dependency).version %>
79
79
  <td class="project-dependency">
80
- <%= version %>
80
+ <%= project.extended_attributes["key_dependency.#{dependency.slug}"] %>
81
81
  </td>
82
82
  <% end %>
83
83
 
@@ -0,0 +1,67 @@
1
+ <% content_for :title do %>
2
+ <h1 class="project-banner space-below">
3
+ Add Projects from GitHub
4
+ </h1>
5
+ <% end %>
6
+
7
+ <p>Select the repos to create projects for in Houston</p>
8
+
9
+ <%= form_tag do %>
10
+ <ul id="repos">
11
+ <% @repos.each do |repo| %>
12
+ <li class="repo <%= "disabled" if repo[:project] %> <%= "retired" if repo[:project] && repo[:project].retired? %>">
13
+ <% if repo[:project] %>
14
+ <input type="checkbox" checked disabled />
15
+ <% else %>
16
+ <input type="checkbox" name="repos[]" value="<%= repo[:full_name] %>" id="<%= repo[:full_name] %>" />
17
+ <% end %>
18
+
19
+ <label for="<%= repo[:full_name] %>">
20
+ <span class="repo-visibility">
21
+ <% if repo[:private] %>
22
+ <i class="fa fa-lock"></i>
23
+ <% else %>
24
+ <i class="fa fa-unlock"></i>
25
+ <% end %>
26
+ </span>
27
+ <span class="repo-owner">
28
+ <%= repo[:owner] %>
29
+ </span>
30
+ <span class="repo-name">
31
+ <% if repo[:project] %>
32
+ <span class="label <%= repo[:project].color %>">
33
+ <%= repo[:name] %>
34
+ </span>
35
+ <% else %>
36
+ <b><%= repo[:name] %></b>
37
+ <% end %>
38
+ </span>
39
+ </label>
40
+ </li>
41
+ <% end %>
42
+ </ul>
43
+
44
+ <div class="form-actions">
45
+ <button type="submit" class="btn btn-primary">Add</button>
46
+ </div>
47
+ <% end %>
48
+
49
+ <% content_for :javascripts do %>
50
+ <script type="text/javascript">
51
+ $(function() {
52
+ $(':checkbox').click(function(e) {
53
+ var $checkbox = $(e.target);
54
+ $checkbox.closest('.repo').toggleClass('selected', $checkbox.prop('checked'));
55
+ });
56
+
57
+ $('[type="submit"]').click(function(e) {
58
+ e.preventDefault();
59
+ $(e.target)
60
+ .prop('disabled', true)
61
+ .html('<i class="fa fa-spinner fa-spin"></i> Adding Projects...')
62
+ .closest('form')
63
+ .submit();
64
+ });
65
+ });
66
+ </script>
67
+ <% end %>