houston-core 0.6.3 → 0.7.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (232) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Gemfile.lock +53 -63
  4. data/app/adapters/houston/adapters/error_tracker/errbit_adapter.rb +2 -2
  5. data/app/adapters/houston/adapters/ticket_tracker/github_adapter.rb +1 -1
  6. data/app/adapters/houston/adapters/ticket_tracker/unfuddle_adapter.rb +5 -5
  7. data/app/adapters/houston/adapters/version_control/git_adapter.rb +4 -4
  8. data/app/assets/javascripts/{app → houston/app}/boot.coffee +0 -0
  9. data/app/assets/javascripts/{app → houston/app}/emoji.coffee.erb +0 -0
  10. data/app/assets/javascripts/{app → houston/app}/infinite_scroll.coffee +0 -0
  11. data/app/assets/javascripts/{app → houston/app}/models/commit.coffee +0 -0
  12. data/app/assets/javascripts/{app → houston/app}/models/release.coffee +0 -0
  13. data/app/assets/javascripts/{app → houston/app}/models/task.coffee +0 -0
  14. data/app/assets/javascripts/{app → houston/app}/models/tester.coffee +0 -0
  15. data/app/assets/javascripts/{app → houston/app}/models/testing_note.coffee +0 -0
  16. data/app/assets/javascripts/{app → houston/app}/models/ticket.coffee +0 -0
  17. data/app/assets/javascripts/{app → houston/app}/models/user.coffee +0 -0
  18. data/app/assets/javascripts/{app → houston/app}/mousetrap-bind-scoped.js +0 -0
  19. data/app/assets/javascripts/{app → houston/app}/releases.coffee +0 -0
  20. data/app/assets/javascripts/{app → houston/app}/table_row_expander.coffee +0 -0
  21. data/app/assets/javascripts/{app → houston/app}/ticket_tracker_refresh.coffee +0 -0
  22. data/app/assets/javascripts/{app → houston/app}/views/_show_sprint_view.coffee +0 -0
  23. data/app/assets/javascripts/{app → houston/app}/views/_tickets_view.coffee +0 -0
  24. data/app/assets/javascripts/{app → houston/app}/views/all_tickets_view.coffee +0 -0
  25. data/app/assets/javascripts/{app → houston/app}/views/commit_view.coffee +0 -0
  26. data/app/assets/javascripts/{app → houston/app}/views/edit_sprint_view.coffee +0 -0
  27. data/app/assets/javascripts/{app → houston/app}/views/edit_ticket_view.coffee +0 -0
  28. data/app/assets/javascripts/{app → houston/app}/views/find_or_create_ticket_view.coffee +0 -0
  29. data/app/assets/javascripts/{app → houston/app}/views/keyboard_shortcuts_modal.coffee +0 -0
  30. data/app/assets/javascripts/{app → houston/app}/views/new_ticket_modal.coffee +0 -0
  31. data/app/assets/javascripts/{app → houston/app}/views/new_ticket_view.coffee +0 -0
  32. data/app/assets/javascripts/{app → houston/app}/views/problems_view.coffee +0 -0
  33. data/app/assets/javascripts/{app → houston/app}/views/testing_note_view.coffee +0 -0
  34. data/app/assets/javascripts/{app → houston/app}/views/testing_report_view.coffee +0 -0
  35. data/app/assets/javascripts/{app → houston/app}/views/testing_ticket_view.coffee +0 -0
  36. data/app/assets/javascripts/{app → houston/app}/views/ticket_modal_view.coffee +0 -0
  37. data/app/assets/javascripts/{application.js → houston/application.js} +1 -1
  38. data/app/assets/javascripts/{core → houston/core}/app.coffee +0 -0
  39. data/app/assets/javascripts/{core → houston/core}/burndown_chart.coffee +0 -0
  40. data/app/assets/javascripts/{core → houston/core}/core_ext/array.coffee +0 -0
  41. data/app/assets/javascripts/{core → houston/core}/core_ext/date.coffee +0 -0
  42. data/app/assets/javascripts/{core → houston/core}/core_ext/number.coffee +0 -0
  43. data/app/assets/javascripts/{core → houston/core}/errors.coffee +1 -1
  44. data/app/assets/javascripts/{core → houston/core}/handlebars_helpers.coffee +0 -0
  45. data/app/assets/javascripts/{core → houston/core}/helpers.coffee +0 -0
  46. data/app/assets/javascripts/{core → houston/core}/jquery_extensions.coffee +5 -3
  47. data/app/assets/javascripts/{core → houston/core}/stacked_area_graph.coffee +0 -0
  48. data/app/assets/javascripts/{core → houston/core}/stacked_bar_graph.coffee +0 -0
  49. data/app/assets/javascripts/{dashboard.js → houston/dashboard.js} +1 -1
  50. data/app/assets/javascripts/{dashboard → houston/dashboard}/refresher.coffee +0 -0
  51. data/app/assets/javascripts/{vendor.js → houston/vendor.js} +0 -0
  52. data/app/assets/stylesheets/{application.css → houston/application.css} +0 -0
  53. data/app/assets/stylesheets/{application → houston/application}/ansi.scss +0 -0
  54. data/app/assets/stylesheets/{application → houston/application}/commit.scss +0 -0
  55. data/app/assets/stylesheets/{application → houston/application}/emoji.scss +0 -0
  56. data/app/assets/stylesheets/{application → houston/application}/exceptions.scss +1 -1
  57. data/app/assets/stylesheets/{application → houston/application}/find_or_create_ticket.scss +0 -0
  58. data/app/assets/stylesheets/{application → houston/application}/follow_up.scss +0 -0
  59. data/app/assets/stylesheets/{application → houston/application}/forms.scss +0 -0
  60. data/app/assets/stylesheets/{application → houston/application}/freight_train.css.scss +0 -0
  61. data/app/assets/stylesheets/{application → houston/application}/full_screen.scss +0 -0
  62. data/app/assets/stylesheets/{application → houston/application}/github_repos.scss +0 -0
  63. data/app/assets/stylesheets/{application → houston/application}/infinite_scroll.scss +0 -0
  64. data/app/assets/stylesheets/houston/application/jobs.scss +5 -0
  65. data/app/assets/stylesheets/{application → houston/application}/keyboard_shortcuts.scss +0 -0
  66. data/app/assets/stylesheets/{application → houston/application}/markdown.scss +0 -0
  67. data/app/assets/stylesheets/{application → houston/application}/mobile.scss +0 -0
  68. data/app/assets/stylesheets/{application → houston/application}/modals.scss +0 -0
  69. data/app/assets/stylesheets/{application → houston/application}/navigation.scss +0 -0
  70. data/app/assets/stylesheets/{application → houston/application}/new_ticket_view.scss +0 -0
  71. data/app/assets/stylesheets/{application → houston/application}/omnibar.scss +0 -0
  72. data/app/assets/stylesheets/{application → houston/application}/project_banner_buttons.scss +0 -0
  73. data/app/assets/stylesheets/{application → houston/application}/project_tiles.scss +0 -0
  74. data/app/assets/stylesheets/{application → houston/application}/projects.css.scss +0 -0
  75. data/app/assets/stylesheets/{application → houston/application}/pull_requests.scss +0 -0
  76. data/app/assets/stylesheets/{application → houston/application}/queue.scss +0 -0
  77. data/app/assets/stylesheets/{application → houston/application}/release_form.scss +0 -0
  78. data/app/assets/stylesheets/{application → houston/application}/releases.scss +1 -1
  79. data/app/assets/stylesheets/{application → houston/application}/sortable_table.scss +0 -0
  80. data/app/assets/stylesheets/{application → houston/application}/sprint.scss +0 -0
  81. data/app/assets/stylesheets/{application → houston/application}/tables.scss +0 -0
  82. data/app/assets/stylesheets/{application → houston/application}/test.scss +0 -0
  83. data/app/assets/stylesheets/{application → houston/application}/test_run.scss +0 -0
  84. data/app/assets/stylesheets/{application → houston/application}/testing_report.scss +0 -0
  85. data/app/assets/stylesheets/{application → houston/application}/ticket.scss +0 -0
  86. data/app/assets/stylesheets/{application → houston/application}/ticket_modal.scss +0 -0
  87. data/app/assets/stylesheets/{application → houston/application}/tickets.scss +0 -0
  88. data/app/assets/stylesheets/{application → houston/application}/timeline.scss +0 -0
  89. data/app/assets/stylesheets/{application → houston/application}/tips.scss +0 -0
  90. data/app/assets/stylesheets/{application → houston/application}/typeahead.scss +0 -0
  91. data/app/assets/stylesheets/{application → houston/application}/uploading.scss +0 -0
  92. data/app/assets/stylesheets/{application → houston/application}/user_wall.scss +0 -0
  93. data/app/assets/stylesheets/{application → houston/application}/users.scss +0 -0
  94. data/app/assets/stylesheets/{application → houston/application}/welcome.scss +0 -0
  95. data/app/assets/stylesheets/{core → houston/core}/alerts.scss +0 -0
  96. data/app/assets/stylesheets/{core → houston/core}/avatars.scss +0 -0
  97. data/app/assets/stylesheets/{core → houston/core}/burndown_chart.scss +0 -0
  98. data/app/assets/stylesheets/{core → houston/core}/colors.scss.erb +0 -0
  99. data/app/assets/stylesheets/{core → houston/core}/misc.scss +3 -0
  100. data/app/assets/stylesheets/{core → houston/core}/octicons-icons.scss +0 -0
  101. data/app/assets/stylesheets/{core → houston/core}/octicons.scss.erb +0 -0
  102. data/app/assets/stylesheets/{core → houston/core}/overrides.scss +0 -0
  103. data/app/assets/stylesheets/{core → houston/core}/roboto.scss.erb +0 -0
  104. data/app/assets/stylesheets/{core → houston/core}/scores.scss +0 -0
  105. data/app/assets/stylesheets/{dashboard.css → houston/dashboard.css} +0 -0
  106. data/app/assets/stylesheets/{dashboard → houston/dashboard}/dashboard.scss +0 -0
  107. data/app/assets/stylesheets/{print.css.scss → houston/print.css.scss} +0 -0
  108. data/app/assets/stylesheets/{variables.scss → houston/variables.scss} +0 -0
  109. data/app/assets/stylesheets/{vendor.css → houston/vendor.css} +0 -0
  110. data/app/concerns/houston/props.rb +96 -0
  111. data/app/concerns/project_adapter.rb +1 -1
  112. data/app/controllers/api/v1/measurements_controller.rb +32 -0
  113. data/app/controllers/application_controller.rb +1 -1
  114. data/app/controllers/authorizations_controller.rb +61 -0
  115. data/app/controllers/github/pulls_controller.rb +1 -1
  116. data/app/controllers/jobs_controller.rb +30 -1
  117. data/app/controllers/oauth/providers_controller.rb +45 -0
  118. data/app/controllers/project_options_controller.rb +2 -2
  119. data/app/controllers/projects_controller.rb +3 -3
  120. data/app/controllers/releases_controller.rb +3 -3
  121. data/app/controllers/user_options_controller.rb +2 -2
  122. data/app/controllers/users_controller.rb +1 -2
  123. data/app/helpers/application_helper.rb +49 -3
  124. data/app/helpers/email_helper.rb +19 -1
  125. data/app/helpers/release_helper.rb +1 -1
  126. data/app/helpers/ticket_helper.rb +0 -32
  127. data/app/interactors/cache_key_dependencies.rb +3 -8
  128. data/app/mailers/view_mailer.rb +8 -8
  129. data/app/models/authorization.rb +54 -0
  130. data/app/models/commit.rb +1 -1
  131. data/app/models/error.rb +14 -0
  132. data/app/models/github/comment_event.rb +4 -4
  133. data/app/models/github/pull_request.rb +39 -17
  134. data/app/models/github/pull_request_event.rb +1 -7
  135. data/app/models/job.rb +81 -0
  136. data/app/models/measurement.rb +2 -1
  137. data/app/models/oauth/provider.rb +35 -0
  138. data/app/models/project.rb +12 -3
  139. data/app/models/slackdown.rb +1 -1
  140. data/app/models/user.rb +28 -35
  141. data/app/presenters/measurements_presenter.rb +26 -0
  142. data/app/presenters/project_presenter.rb +1 -1
  143. data/app/views/authorizations/_form.html.erb +33 -0
  144. data/app/views/authorizations/edit.html.erb +7 -0
  145. data/app/views/authorizations/granted.html.erb +7 -0
  146. data/app/views/authorizations/index.html.erb +47 -0
  147. data/app/views/authorizations/new.html.erb +7 -0
  148. data/app/views/configuration_error/_no_ticket_tracker.html.erb +1 -1
  149. data/app/views/github/pulls/index.html.erb +5 -1
  150. data/app/views/jobs/index.html.erb +72 -0
  151. data/app/views/jobs/show.html.erb +25 -32
  152. data/app/views/layouts/_navigation.html.erb +1 -1
  153. data/app/views/layouts/application.html.erb +14 -15
  154. data/app/views/layouts/dashboard.html.erb +9 -10
  155. data/app/views/layouts/minimal.html.erb +14 -15
  156. data/app/views/layouts/naked.html.erb +52 -0
  157. data/app/views/layouts/naked_dashboard.html.erb +9 -10
  158. data/app/views/oauth/providers/_form.html.erb +54 -0
  159. data/app/views/oauth/providers/edit.html.erb +7 -0
  160. data/app/views/oauth/providers/index.html.erb +41 -0
  161. data/app/views/oauth/providers/new.html.erb +7 -0
  162. data/app/views/projects/_form.html.erb +8 -9
  163. data/app/views/projects/index.html.erb +1 -1
  164. data/app/views/users/_form.html.erb +20 -19
  165. data/bin/rails +1 -1
  166. data/config/application.rb +11 -10
  167. data/config/boot.rb +1 -2
  168. data/config/environments/development.rb +8 -6
  169. data/config/environments/production.rb +11 -16
  170. data/config/environments/test.rb +5 -5
  171. data/config/initializers/assets.rb +17 -7
  172. data/config/initializers/cookies_serializer.rb +1 -1
  173. data/config/initializers/houston_async.rb +22 -4
  174. data/config/initializers/houston_report_exception.rb +16 -11
  175. data/config/initializers/houston_scheduler.rb +1 -1
  176. data/config/initializers/houston_try.rb +12 -0
  177. data/config/routes.rb +29 -19
  178. data/db/fixtures/projects.yml +2 -2
  179. data/db/migrate/20160317140151_remove_limit_from_users_invitation_token.rb +9 -0
  180. data/db/migrate/20160419230411_create_oauth_providers.rb +14 -0
  181. data/db/migrate/20160420000616_create_authorizations.rb +16 -0
  182. data/db/migrate/20160507135209_create_jobs.rb +14 -0
  183. data/db/migrate/20160507135846_create_errors_2.rb +12 -0
  184. data/db/migrate/20160510233329_add_closed_at_and_merged_at_to_pull_requests.rb +7 -0
  185. data/db/migrate/20160625203412_convert_user_view_options_to_props.rb +21 -0
  186. data/db/migrate/20160625221840_convert_project_extended_attributes_to_props.rb +32 -0
  187. data/db/migrate/20160625230420_move_users_unfuddle_id_to_props.rb +14 -0
  188. data/db/structure.sql +247 -4
  189. data/{houston.gemspec → houston-core.gemspec} +8 -9
  190. data/lib/configuration.rb +15 -43
  191. data/lib/houston/version.rb +1 -1
  192. data/lib/houston_observer.rb +2 -7
  193. data/lib/parallel_enumerable.rb +2 -8
  194. data/templates/new-instance/app/assets/javascripts/.keep +0 -0
  195. data/templates/new-instance/app/assets/javascripts/application.js +13 -0
  196. data/templates/new-instance/app/assets/stylesheets/.keep +0 -0
  197. data/templates/new-instance/app/assets/stylesheets/application.css +13 -0
  198. data/templates/new-instance/app/controllers/.keep +0 -0
  199. data/templates/new-instance/app/models/.keep +0 -0
  200. data/templates/new-instance/app/views/.keep +0 -0
  201. data/templates/new-instance/config/alerts/errs.rb +1 -1
  202. data/templates/new-instance/config/jobs/purge_jobs.rb +3 -0
  203. data/templates/new-instance/config/main.rb +1 -0
  204. data/templates/new-instance/config/routes.rb +5 -0
  205. data/templates/new-instance/config/triggers/github/publish_comments_on_slack.rb +3 -3
  206. data/templates/new-instance/lib/houston/engine.rb +25 -0
  207. data/test/acceptance/creating_a_release_test.rb +1 -1
  208. data/test/integration/ci_integration_test.rb +2 -2
  209. data/test/integration/commits_api_test.rb +1 -1
  210. data/test/unit/controllers/hooks_controller_test.rb +1 -6
  211. data/test/unit/controllers/project_options_controller_test.rb +11 -11
  212. data/test/unit/controllers/user_options_controller_test.rb +13 -13
  213. data/test/unit/models/code_climate_coverage_report_test.rb +1 -1
  214. data/test/unit/models/commit_test.rb +4 -2
  215. data/test/unit/models/project_test.rb +5 -5
  216. data/test/unit/models/props_test.rb +57 -0
  217. data/test/unit/models/pull_request_test.rb +2 -2
  218. data/test/unit/models/test_run_test.rb +1 -1
  219. metadata +166 -151
  220. data/app/assets/javascripts/app/views/reports_view.coffee +0 -51
  221. data/app/controllers/oauth_consumers_controller.rb +0 -68
  222. data/app/controllers/reports_controller.rb +0 -215
  223. data/app/models/consumer_token.rb +0 -13
  224. data/app/models/github_token.rb +0 -8
  225. data/app/views/oauth_consumers/error.html.erb +0 -5
  226. data/app/views/oauth_consumers/index.html.erb +0 -29
  227. data/app/views/oauth_consumers/show.html.erb +0 -7
  228. data/app/views/reports/index.html.erb +0 -9
  229. data/app/views/reports/sprint.html.erb +0 -21
  230. data/app/views/reports/velocity.html.erb +0 -104
  231. data/config/initializers/oauth_consumers.rb +0 -18
  232. data/lib/patches/sprockets_output_path_for_assets.rb +0 -29
data/app/models/commit.rb CHANGED
@@ -173,7 +173,7 @@ class Commit < ActiveRecord::Base
173
173
 
174
174
  clean_message.gsub!(TICKET_PATTERN) { tickets << [$1.to_i, $2]; "" }
175
175
  clean_message.gsub!(TIME_PATTERN) { hours = $1.to_f; hours /= 60 if $2.starts_with?("m"); "" }
176
- clean_message.gsub!(EXTRA_ATTRIBUTE_PATTERN) { (attributes[$1] ||= []).push($2); "" }
176
+ clean_message.gsub!(EXTRA_ATTRIBUTE_PATTERN) { (attributes[$1] ||= []).concat($2.split(",").map(&:strip).reject(&:blank?)); "" }
177
177
  while clean_message.gsub!(TAG_PATTERN) { tags << $1; "" }; end
178
178
 
179
179
  {tags: tags, tickets: tickets, hours_worked: hours, attributes: attributes, clean_message: clean_message.strip}
@@ -0,0 +1,14 @@
1
+ class Error < ActiveRecord::Base
2
+
3
+ validates :sha, :message, :backtrace, presence: true
4
+
5
+ def self.find_or_create_for_exception(exception)
6
+ message = exception.message
7
+ backtrace = exception.backtrace.join("\n")
8
+ sha = Digest::SHA1.hexdigest([message, backtrace].join)
9
+ create_with(message: message, backtrace: backtrace).find_or_create_by(sha: sha)
10
+ rescue ActiveRecord::RecordNotUnique
11
+ find_by(sha: sha)
12
+ end
13
+
14
+ end
@@ -2,7 +2,7 @@ require "github/event"
2
2
 
3
3
  module Github
4
4
  class CommentEvent < Event
5
- attr_reader :comment, :project, :user
5
+ attr_reader :action, :comment, :project, :user
6
6
 
7
7
  class << self
8
8
  attr_accessor :type
@@ -16,13 +16,13 @@ module Github
16
16
  def initialize(payload)
17
17
  super
18
18
  @comment = payload.fetch "comment"
19
+ @action = payload.fetch "action", "created"
19
20
  comment["project"] = Project.find_by_slug payload["repository"]["name"]
20
21
  end
21
22
 
22
23
  def process!
23
- Rails.logger.info "\e[34m[github] Processing Comment Event (type: #{type})\e[0m"
24
- Houston.observer.fire "github:comment", comment
25
- Houston.observer.fire "github:comment:#{type}", comment
24
+ Houston.observer.fire "github:comment:#{action}", comment
25
+ Houston.observer.fire "github:comment:#{action}:#{type}", comment
26
26
  end
27
27
 
28
28
  end
@@ -1,5 +1,7 @@
1
1
  module Github
2
2
  class PullRequest < ActiveRecord::Base
3
+ include Houston::Props
4
+
3
5
  self.table_name = "pull_requests"
4
6
 
5
7
  attr_readonly :project_id, :user_id, :repo, :number, :username, :base_ref, :base_sha, :url
@@ -15,9 +17,15 @@ module Github
15
17
  before_save :associate_user_with_self, if: :username_changed?
16
18
  after_commit :associate_commits_with_self
17
19
 
18
- after_destroy { Houston.observer.fire "github:pull:closed", self }
19
- after_create { Houston.observer.fire "github:pull:opened", self }
20
- after_update { Houston.observer.fire "github:pull:updated", self, changes }
20
+ after_create do
21
+ Houston.observer.fire "github:pull:opened", self
22
+ end
23
+
24
+ after_update do
25
+ Houston.observer.fire "github:pull:updated", self, changes
26
+ Houston.observer.fire "github:pull:closed", self if closed_at_changed? && closed_at
27
+ Houston.observer.fire "github:pull:reopened", self if closed_at_changed? && !closed_at
28
+ end
21
29
 
22
30
  validates :project_id, :title, :number, :repo, :url, :base_ref, :base_sha, :head_ref, :head_sha, :username, presence: true
23
31
  validates :number, uniqueness: { scope: :project_id }
@@ -36,8 +44,8 @@ module Github
36
44
  # requests from 52 repos.
37
45
  def fetch!(projects = Project.unretired)
38
46
  repos = projects
39
- .where("extended_attributes->'git_location' LIKE '%github.com%'")
40
- .pluck("extended_attributes->'git_location'")
47
+ .where("props->>'git.location' LIKE '%github.com%'")
48
+ .pluck("props->>'git.location'")
41
49
  .map { |url| _repo_name_from_url(url) }
42
50
  .compact
43
51
 
@@ -83,12 +91,14 @@ module Github
83
91
  Houston.benchmark "Syncing pull requests" do
84
92
  existing_pulls = all.to_a
85
93
 
86
- # Delete unexpected pulls
94
+ # Fetch unexpected pulls so that we know
95
+ # when they were closed and whether they
96
+ # were merged.
87
97
  existing_pulls.each do |existing_pr|
88
98
  unless expected_pulls.detect { |expected_pr|
89
99
  expected_pr["base"]["repo"]["name"] == existing_pr.repo &&
90
100
  expected_pr["number"] == existing_pr.number }
91
- existing_pr.destroy
101
+ expected_pulls << existing_pr.fetch!
92
102
  end
93
103
  end
94
104
 
@@ -108,16 +118,6 @@ module Github
108
118
  end
109
119
  end
110
120
 
111
- def close!(github_pr, options={})
112
- pr = find_by(
113
- repo: github_pr["base"]["repo"]["name"],
114
- number: github_pr["number"])
115
- return unless pr
116
-
117
- pr.actor = options[:as]
118
- pr.destroy
119
- end
120
-
121
121
  def upsert!(github_pr, options={})
122
122
  retry_count ||= 0
123
123
  upsert(github_pr).tap do |pr|
@@ -138,6 +138,18 @@ module Github
138
138
  .merge_attributes(github_pr)
139
139
  end
140
140
 
141
+ def open
142
+ where(closed_at: nil)
143
+ end
144
+
145
+ def closed
146
+ where.not(closed_at: nil)
147
+ end
148
+
149
+ def merged
150
+ where.not(merged_at: nil)
151
+ end
152
+
141
153
  def labeled(*labels)
142
154
  where(["exists (select 1 from jsonb_array_elements(pull_requests.json_labels) as \"label\" where \"label\"->>'name' IN (?))", labels])
143
155
  end
@@ -198,6 +210,14 @@ module Github
198
210
  project.repo.create_commit_status(head_sha, status)
199
211
  end
200
212
 
213
+ def full_repo
214
+ url[/https:\/\/github.com\/(.*)\/pull\/\d+/, 1]
215
+ end
216
+
217
+ def fetch!
218
+ Houston.github.pull_request(full_repo, number).to_h.with_indifferent_access
219
+ end
220
+
201
221
 
202
222
 
203
223
  def merge_attributes(pr)
@@ -214,6 +234,8 @@ module Github
214
234
  self.body = pr["body"]
215
235
  self.base_sha = pr["base"]["sha"]
216
236
  self.head_sha = pr["head"]["sha"]
237
+ self.closed_at = pr["closed_at"]
238
+ self.merged_at = pr["merged_at"]
217
239
  self.labels = pr["labels"] if pr.key?("labels")
218
240
 
219
241
  self
@@ -20,14 +20,8 @@ module Github
20
20
  return
21
21
  end
22
22
 
23
- # Delete pull requests when they are closed
24
- if action == "closed"
25
- PullRequest.close! pull_request, as: actor
26
- return
27
- end
28
-
29
23
  # Ensure that we have a record of this open pull request
30
- # action: labeled, unlabeled, opened, reopened, or synchronized
24
+ # action: labeled, unlabeled, opened, closed, reopened, or synchronized
31
25
  pr = PullRequest.upsert! pull_request, as: actor
32
26
 
33
27
  # The Pull Request may be invalid if it isn't for a
data/app/models/job.rb ADDED
@@ -0,0 +1,81 @@
1
+ class Job < ActiveRecord::Base
2
+
3
+ validates :name, :started_at, presence: true
4
+ belongs_to :error
5
+
6
+ default_scope -> { order(started_at: :desc) }
7
+
8
+
9
+
10
+ def self.started_before(time)
11
+ where arel_table[:started_at].lteq time
12
+ end
13
+
14
+ def self.record(job_name)
15
+ job = Job.create!(name: job_name, started_at: Time.now)
16
+ begin
17
+ exception = nil
18
+
19
+ Houston.reconnect do
20
+ yield
21
+ end
22
+
23
+ rescue SocketError,
24
+ Errno::ECONNREFUSED,
25
+ Errno::ETIMEDOUT,
26
+ Faraday::ConnectionFailed,
27
+ Faraday::SSLError,
28
+ Faraday::HTTP::ServerError,
29
+ Faraday::HTTP::Unauthorized,
30
+ Faraday::TimeoutError,
31
+ Rugged::NetworkError,
32
+ Unfuddle::ConnectionError,
33
+ Octokit::BadGateway,
34
+ Octokit::InternalServerError,
35
+ Net::OpenTimeout,
36
+ exceptions_wrapping(PG::ConnectionBad)
37
+
38
+ # Note that the job failed, but do not report _these_ exceptions
39
+ exception = $!
40
+
41
+ rescue Exception # rescues StandardError by default; but we want to rescue and report all errors
42
+
43
+ # Report all other exceptions
44
+ exception = $!
45
+ Houston.report_exception($!, parameters: {job_id: job.id, job_name: job_name})
46
+
47
+ ensure
48
+ begin
49
+ Houston.reconnect do
50
+ job.finish! exception
51
+ end
52
+ rescue Exception # rescues StandardError by default; but we want to rescue and report all errors
53
+ Houston.report_exception($!, parameters: {job_id: job.id, job_name: job_name})
54
+ end
55
+ end
56
+ end
57
+
58
+
59
+
60
+ def exception=(exception)
61
+ self.error = Error.find_or_create_for_exception(exception) if exception
62
+ end
63
+
64
+ def finish!(exception)
65
+ update_attributes! finished_at: Time.now, succeeded: exception.nil?, exception: exception
66
+ end
67
+
68
+ def duration
69
+ return nil unless finished?
70
+ finished_at - started_at
71
+ end
72
+
73
+ def finished?
74
+ finished_at.present?
75
+ end
76
+
77
+ def in_progress?
78
+ finished_at.nil?
79
+ end
80
+
81
+ end
@@ -49,6 +49,7 @@ class Measurement < ActiveRecord::Base
49
49
 
50
50
  def for(subject)
51
51
  return where(subject_type: nil, subject_id: nil) if subject.nil?
52
+ return where(subject_type: subject.name) if subject.is_a?(Class)
52
53
  where(subject_type: subject.class.name, subject_id: subject.id)
53
54
  end
54
55
 
@@ -65,7 +66,7 @@ class Measurement < ActiveRecord::Base
65
66
  # - weekly.hours.*.fix
66
67
  def named(*name_patterns)
67
68
  name_patterns = name_patterns.flatten.map { |pattern| pattern
68
- .gsub(/\{([\w,]+)\}/) { "(#{$~.captures[0].gsub(/,/, "|")})" }
69
+ .gsub(/\{([\w\-,]+)\}/) { "(#{$~.captures[0].gsub(/,/, "|")})" }
69
70
  .gsub(/\*$/, "%") }
70
71
  where(["name SIMILAR TO ?", "(#{name_patterns.join("|")})"])
71
72
  end
@@ -0,0 +1,35 @@
1
+ module Oauth
2
+ class Provider < ActiveRecord::Base
3
+ self.table_name = "oauth_providers"
4
+
5
+ validates :name, :site, :authorize_path, :token_path, :client_id, :client_secret, presence: true
6
+
7
+ def authorize_url(params={})
8
+ client.auth_code.authorize_url params.merge(redirect_uri: oauth2_callback_url)
9
+ end
10
+
11
+ def redeem_access_token(code, params={})
12
+ client.auth_code.get_token(code, params.merge(redirect_uri: oauth2_callback_url))
13
+ end
14
+
15
+ def refresh_access_token(authorization)
16
+ OAuth2::AccessToken.from_hash(client, authorization.attributes).refresh!
17
+ end
18
+
19
+ private
20
+
21
+ def client
22
+ @client ||= OAuth2::Client.new(
23
+ client_id,
24
+ client_secret,
25
+ site: site,
26
+ authorize_url: authorize_path,
27
+ token_url: token_path)
28
+ end
29
+
30
+ def oauth2_callback_url
31
+ "http://#{Houston.host}/oauth2/callback"
32
+ end
33
+
34
+ end
35
+ end
@@ -2,6 +2,7 @@ class Project < ActiveRecord::Base
2
2
  extend ProjectAdapter
3
3
  include Retirement
4
4
  include FeatureState
5
+ include Houston::Props
5
6
 
6
7
  has_many :releases, dependent: :destroy
7
8
  has_many :commits, dependent: :destroy, extend: CommitSynchronizer
@@ -59,7 +60,7 @@ class Project < ActiveRecord::Base
59
60
  end
60
61
 
61
62
  def show_release_notes_for?(environment_name)
62
- extended_attributes["releases.ignore.#{environment_name.downcase}"] != "1"
63
+ props["releases.ignore.#{environment_name.downcase}"] != "1"
63
64
  end
64
65
 
65
66
  def environment(environment_name)
@@ -67,11 +68,19 @@ class Project < ActiveRecord::Base
67
68
  end
68
69
 
69
70
  def extended_attributes
70
- super || (self.extended_attributes = {})
71
+ raise NotImplementedError, "This feature has been deprecated; use props"
72
+ end
73
+
74
+ def extended_attributes=(value)
75
+ raise NotImplementedError, "This feature has been deprecated; use props"
71
76
  end
72
77
 
73
78
  def view_options
74
- super || {}
79
+ raise NotImplementedError, "This feature has been deprecated; use props"
80
+ end
81
+
82
+ def view_options=(value)
83
+ raise NotImplementedError, "This feature has been deprecated; use props"
75
84
  end
76
85
 
77
86
  def testers
@@ -3,7 +3,7 @@ class Slackdown
3
3
  attr_reader :markdown
4
4
 
5
5
  def initialize(markdown)
6
- @markdown = markdown
6
+ @markdown = markdown.to_s
7
7
  end
8
8
 
9
9
  def convert
data/app/models/user.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  class User < ActiveRecord::Base
2
2
  include Retirement
3
+ include Houston::Props
3
4
 
4
5
  has_many :testing_notes
5
6
  has_many :roles, :dependent => :destroy
6
7
  has_many :credentials, :class_name => "UserCredentials", :dependent => :destroy
7
- has_many :consumer_tokens
8
8
  has_many :tickets, foreign_key: "reporter_id"
9
9
  has_and_belongs_to_many :commits
10
10
  belongs_to :current_project, class_name: "Project"
@@ -73,10 +73,6 @@ class User < ActiveRecord::Base
73
73
  with_email_address(email_address).first
74
74
  end
75
75
 
76
- def self.with_view_option(option, value)
77
- where(["view_options->? = ?", option, value])
78
- end
79
-
80
76
 
81
77
 
82
78
  def email=(value)
@@ -86,7 +82,7 @@ class User < ActiveRecord::Base
86
82
  end
87
83
 
88
84
  def email_addresses
89
- super || []
85
+ (super || []).reject(&:blank?)
90
86
  end
91
87
 
92
88
  def alias_emails
@@ -94,7 +90,7 @@ class User < ActiveRecord::Base
94
90
  end
95
91
 
96
92
  def alias_emails=(value)
97
- self.email_addresses = [email] + Array.wrap(value)
93
+ self.email_addresses = [email] + Array.wrap(value).reject(&:blank?)
98
94
  end
99
95
 
100
96
 
@@ -112,7 +108,19 @@ class User < ActiveRecord::Base
112
108
  end
113
109
 
114
110
  def view_options
115
- super || {}
111
+ raise NotImplementedError, "This feature has been deprecated; use props"
112
+ end
113
+
114
+ def view_options=(value)
115
+ raise NotImplementedError, "This feature has been deprecated; use props"
116
+ end
117
+
118
+ def unfuddle_id
119
+ raise NotImplementedError, "This feature has been deprecated; use props[\"unfuddle.id\"]"
120
+ end
121
+
122
+ def unfuddle_id=(value)
123
+ raise NotImplementedError, "This feature has been deprecated; use props[\"unfuddle.id\"]"
116
124
  end
117
125
 
118
126
 
@@ -121,37 +129,22 @@ class User < ActiveRecord::Base
121
129
  # ------------------------------------------------------------------------- #
122
130
 
123
131
  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
132
+ find_by_prop "github.username", username do |username|
142
133
 
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
134
+ # Look up the email address of the GitHub user and see if we can
135
+ # identify the Houston user by the GitHub user's email address.
136
+ user = Houston.github.user(username)
137
+ user = find_by_email_address user.email if user
148
138
 
149
- def github_username
150
- view_options["github_username"]
139
+ # We couldn't find the user by their email address, now
140
+ # we'll look at their nicknames
141
+ user = find_by_nickname username unless user
142
+
143
+ end
151
144
  end
152
145
 
153
- def set_github_username!(username)
154
- update_column :view_options, view_options.merge("github_username" => username)
146
+ def github_username
147
+ props["github.username"]
155
148
  end
156
149
 
157
150
  # ------------------------------------------------------------------------- #