houston-core 0.7.0.beta4 → 0.7.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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -2
  3. data/.travis.yml +50 -0
  4. data/Gemfile +1 -2
  5. data/Gemfile.lock +1 -7
  6. data/README.md +21 -9
  7. data/app/assets/stylesheets/houston/application/actions.scss +25 -4
  8. data/app/assets/stylesheets/houston/application/sprint.scss +1 -1
  9. data/app/assets/stylesheets/houston/application/test.scss +1 -1
  10. data/app/assets/stylesheets/houston/application/test_run.scss +2 -2
  11. data/app/assets/stylesheets/houston/application/timeline.scss +1 -1
  12. data/app/assets/stylesheets/houston/core/roboto.scss.erb +2 -2
  13. data/app/controllers/actions_controller.rb +13 -5
  14. data/app/controllers/errors_controller.rb +2 -2
  15. data/app/controllers/releases_controller.rb +1 -1
  16. data/app/controllers/triggers_controller.rb +1 -1
  17. data/app/helpers/actions_helper.rb +36 -1
  18. data/app/helpers/navigation_helper.rb +6 -1
  19. data/app/models/github/pull_request.rb +13 -4
  20. data/app/models/task.rb +1 -1
  21. data/app/presenters/task_presenter.rb +6 -6
  22. data/app/views/actions/_actions.html.erb +12 -0
  23. data/app/views/actions/index.html.erb +19 -12
  24. data/app/views/actions/running.html.erb +47 -0
  25. data/app/views/actions/show.html.erb +13 -14
  26. data/app/views/errors/_actions.html.erb +1 -1
  27. data/app/views/errors/index.html.erb +5 -1
  28. data/app/views/layouts/_navigation.html.erb +10 -5
  29. data/config/initializers/add_navigation_renderers.rb +8 -6
  30. data/config/initializers/houston_deliver.rb +16 -0
  31. data/config/routes.rb +1 -0
  32. data/db/structure.sql +1 -1
  33. data/houston-core.gemspec +1 -1
  34. data/lib/generators/module_generator.rb +5 -1
  35. data/lib/houston/boot/actions.rb +4 -1
  36. data/lib/houston/boot/configuration.rb +8 -1
  37. data/lib/houston/boot/extensions.rb +35 -9
  38. data/lib/houston/boot/serializer.rb +6 -1
  39. data/lib/houston/version.rb +1 -1
  40. data/script/cibuild +2 -2
  41. data/templates/new-module/lib/houston/%name%.rb +30 -7
  42. data/test/acceptance/creating_a_release_test.rb +14 -21
  43. data/test/data/bare_repo.git/FETCH_HEAD +1 -1
  44. data/test/data/bare_repo.git/objects/60/3a4970ec7ed5eb3f7a578850c21b7162424465 +1 -0
  45. data/test/data/bare_repo.git/objects/b9/1a4fe778398bc87b29842ca84a2727b4763c9d +1 -0
  46. data/test/data/bare_repo.git/objects/ba/b88c00ec62b5f30bf720ab1136401d1f9d2310 +2 -0
  47. data/test/data/bare_repo.git/refs/heads/master +1 -1
  48. data/test/integration/commits_api_test.rb +1 -0
  49. data/test/test_helper.rb +2 -2
  50. data/test/unit/models/sprint_test.rb +1 -1
  51. data/tmp/.keep +0 -0
  52. metadata +16 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f6c7d2f0537e31c913013579570a403c3ed71e62
4
- data.tar.gz: bae46e8b42c70baaeebef413cfe6b38fdc72aab1
3
+ metadata.gz: 7313503e6891a81d74ee02d5c9b06315dce2df20
4
+ data.tar.gz: aa75366ce3181bebdb91e6f883a1c8879adbcdf9
5
5
  SHA512:
6
- metadata.gz: 01c289fcc4cd1aaff9496d2273b3703d87184f9ec4d1742f518fdf0d3861215cae4ef92889ba952fa3e7a6b3fe09ed3d8c2278cd30b61aee1601900e24b055fd
7
- data.tar.gz: b2f67d2a12797f57aa6e246889c051b6a68ac5733eb743c433e75f21f12b3beb00def052249166e4c258c9af992abed167248d31b115e389bfd29cc274438e02
6
+ metadata.gz: db72d7a2dcc0808d565cd6a2262e63d1ab0ffb8b144b4a701086126d92c6f1f9ceb944352209ed35bbf5d8a5777ecf80c6186ac9e986ae1ebbf319a6f89bf537
7
+ data.tar.gz: 4a99188cdd551613eac66106a3b0a86e79df16a64dc6f019e74137fb9bccd562ebc0fde7c4a78e575283aeaeb7ee3f00619a74fd9c1cc5b0f6c167ee41ce24a4
data/.gitignore CHANGED
@@ -8,8 +8,10 @@
8
8
  /.bundle
9
9
 
10
10
  # Ignore all logfiles and tempfiles.
11
- /log/*.log
12
- /tmp
11
+ /log/*
12
+ /tmp/*
13
+ !/log/.keep
14
+ !/tmp/.keep
13
15
 
14
16
  # Ignore built gems
15
17
  /pkg
data/.travis.yml ADDED
@@ -0,0 +1,50 @@
1
+ # .travis.yml
2
+ language: ruby
3
+
4
+ # By default, Travis clones the last 50 commits
5
+ # git:
6
+ # depth: 50
7
+
8
+ rvm:
9
+
10
+ # I would use 2.3.0, but Travis loads that binary on-demand
11
+ # whereas it has the 2.2.0 binary handy for the Ruby VM.
12
+ # This saves about 20 seconds.
13
+ - 2.2.0
14
+
15
+ addons:
16
+
17
+ # Travis's default version is 9.1; but Houston uses jsonb columns
18
+ postgresql: "9.4"
19
+
20
+ services:
21
+ - postgresql
22
+
23
+ before_install:
24
+
25
+ # This fixes a bug with the build:
26
+ # https://github.com/travis-ci/travis-ci/issues/5239
27
+ - gem update bundler
28
+
29
+ # This is required so that the Rugged gem can
30
+ # build libgit2 with support for the SSH protocol
31
+ - sudo apt-get install libssh2-1-dev
32
+
33
+ before_script:
34
+
35
+ # Create the test database
36
+ - psql -c 'create database houston_core_test;' -U postgres
37
+
38
+ # Give the VM public and private SSH keys.
39
+ # These aren't secret: we don't need to authenticate as anyone,
40
+ # we just need the keys to be able to clone a public repo from
41
+ # GitHub via the SSH protocol.
42
+ - echo -e "Host *\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
43
+ - echo -e "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDkTcgXnHuqR0gbwegnr9Zxz4hTkjjV/SpgJNPJz7mo/HKNbx0rqjj1P0yGR053R9GSFFim2ut4NK9DPPUkQdyucw+DoLkYRHJmlJ4BNa9NTCD0sl+eSXO2969kZojCYSOgbmkCJx8mdgTwhzdgE/jhBrsY0hPE6pRTlU+H68/zeNdJUAIJf0LLXOm3hpTKLA19VICltl/j9VvBJpgRHdBylXEyL8HokYpjkQQk1ZXj3m7Nlo8yDdg4VcljOJWC+Xh8kxRMfK5x/VRVsYKCQXN5QlzKeqf7USRDUS/7mFoPUBW+d4kwKtGxRsWuIL2yeqzifZUTOgsh9+ZWAWxWffQZ your_email@example.com" > ~/.ssh/id_rsa.pub
44
+ - echo -e "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA5E3IF5x7qkdIG8HoJ6/Wcc+IU5I41f0qYCTTyc+5qPxyjW8d\nK6o49T9MhkdOd0fRkhRYptrreDSvQzz1JEHcrnMPg6C5GERyZpSeATWvTUwg9LJf\nnklztvevZGaIwmEjoG5pAicfJnYE8Ic3YBP44Qa7GNITxOqUU5VPh+vP83jXSVAC\nCX9Cy1zpt4aUyiwNfVSApbZf4/VbwSaYER3QcpVxMi/B6JGKY5EEJNWV495uzZaP\nMg3YOFXJYziVgvl4fJMUTHyucf1UVbGCgkFzeUJcynqn+1EkQ1Ev+5haD1AVvneJ\nMCrRsUbFriC9snqs4n2VEzoLIffmVgFsVn30GQIDAQABAoIBAQDPQm2sQbti0mN8\nD4Uawl8D40v30n8WhUa7EbPTOmlqKAQ2sfDhex9KRbTLEmEBmImA/Eee8o9iCTIy\n8Fv8Fm6pUHt9G6Pti/XvemwW3Q3QNpSUkHqN0FDkgecQVqVBEb6uHo3mDm4RFINX\neOmkp30BjIK9/blEw1D0sFALLOEUPaDdPMwiXtFgqfrFSgpDET3TvQIwZ2LxxTm0\ncNmP3sCSlZHJNkZI4hBEWaaXR+V5/+C1qblDCo5blAWTcX3UzqrwUUJgFi6VnBuh\n7S9Q6+CEIU+4JRyWQNmY8YgZFaAp6IOr/kyfPxTP1+UEVVgcLn3WDYwfG9og0tmz\nfzlruAgBAoGBAPfz73Pey86tNZEanhJhbX8gVjzy2hvyhT0paHg0q/H6c1VWOtUH\nOwZ3Ns2xAZqJhlDqCHnQYSCZDly042U/theP4N8zo1APb4Yg4qdmXF9QE1+2M03r\nkS6138gU/CSCLf8pCYa6pA/GmsaXxloeJGLvT4fzOZRsVav80/92XHRhAoGBAOu2\nmKh4Gr1EjgN9QNbk9cQTSFDtlBEqO/0pTepvL73UvNp/BAn4iYZFU4WnklFVBSWc\nL84Sc732xU12TAbTTUsa6E7W29pS8u7zVTxlIdQIIU5pzDyU1pNNk2kpxzte5p3Y\nPDtniPFsoYLWoH0LpsKL93t2pLAj+IOkE6f3XBq5AoGAIKaYo5N1FxQr952frx/x\nQUpK0N/R5Ng8v18SiLG26rhmM5iVSrQXC7TrHI7wfR8a9tC6qP/NqnM9NuwC/bQ0\nEEo7/GhaWxKNRwZRkmWiSFLNGk9t1hbtGU+N1lUdFtmloPIQdRNiw0kN3JTj474Q\nYI7O1EItFORnK6yxZfR6HEECgYEA1CT7MGUoa8APsMRCXyaiq15Pb8bjxK8mXquW\nHLEFXuzhLCW1FORDoj0y9s/iuKC0iS0ROX8R/J7k5NrbgikbH8WP36UxKkYNr1IC\nHOFImPTYRSKjVsL+fIUNb1DSp3S6SsYbL7v3XJJQqtlQiDq8U8x1aQFXJ9C4EoLR\nzhKrKsECgYBtU/TSF/TATZY5XtrN9O+HX1Fbz70Ci8XgvioheVI2fezOcXPRzDcC\nOYPaCMNKA5E8gHdg4s0TN7uDvKTJ+KhSg2V7gZ39A28dHrJaRX7Nz4k6t2uEBjX9\na1JidpAIbJ+3w7+hj6L299tVZvS+Y/6Dz/uuEQGXfJg/l/5CCvQPsA==\n-----END RSA PRIVATE KEY-----" > ~/.ssh/id_rsa
45
+ - chmod 600 ~/.ssh/id_rsa*
46
+ - eval `ssh-agent -s`
47
+ - ssh-add ~/.ssh/id_rsa
48
+
49
+ script:
50
+ - script/cibuild
data/Gemfile CHANGED
@@ -16,9 +16,8 @@ group :test do
16
16
  gem "webmock", require: "webmock/minitest"
17
17
  gem "factory_girl_rails"
18
18
  gem "test_after_commit"
19
-
19
+
20
20
  # For Jenkins
21
- gem "therubyracer"
22
21
  gem "simplecov-json", require: false
23
22
  gem "minitest-reporters", require: false
24
23
  gem "minitest-reporters-turn_reporter", require: false
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- houston-core (0.7.0.beta4)
4
+ houston-core (0.7.0)
5
5
  activerecord-import
6
6
  activerecord-pluck_in_batches
7
7
  addressable (~> 2.3.8)
@@ -192,7 +192,6 @@ GEM
192
192
  jwt (1.5.4)
193
193
  launchy (2.4.3)
194
194
  addressable (~> 2.3)
195
- libv8 (3.16.14.13)
196
195
  loofah (2.0.3)
197
196
  nokogiri (>= 1.5.9)
198
197
  mail (2.6.4)
@@ -282,7 +281,6 @@ GEM
282
281
  thor (>= 0.18.1, < 2.0)
283
282
  rake (11.2.2)
284
283
  redcarpet (3.3.4)
285
- ref (2.0.0)
286
284
  responders (2.2.0)
287
285
  railties (>= 4.2.0, < 5.1)
288
286
  rest-client (1.8.0)
@@ -326,9 +324,6 @@ GEM
326
324
  activerecord
327
325
  test_after_commit (1.0.0)
328
326
  activerecord (>= 3.2)
329
- therubyracer (0.12.2)
330
- libv8 (~> 3.16.14.0)
331
- ref
332
327
  thor (0.19.1)
333
328
  thread_safe (0.3.5)
334
329
  tilt (2.0.5)
@@ -367,7 +362,6 @@ DEPENDENCIES
367
362
  shoulda-context
368
363
  simplecov-json
369
364
  test_after_commit
370
- therubyracer
371
365
  timecop
372
366
  webmock
373
367
 
data/README.md CHANGED
@@ -1,10 +1,14 @@
1
- # Houston Core [![Code Climate](https://codeclimate.com/github/houston/houston-core.png)](https://codeclimate.com/github/houston/houston-core)
1
+ # Houston Core
2
2
 
3
- ##### Mission Control for your projects and team
3
+ [![Gem Version](https://badge.fury.io/rb/houston-core.svg)](https://rubygems.org/gems/houston-core)
4
+ [![Code Climate](https://codeclimate.com/github/houston/houston-core.svg)](https://codeclimate.com/github/houston/houston-core)
5
+ [![Build Status](https://travis-ci.org/houston/houston-core.svg)](https://travis-ci.org/houston/houston-core)
6
+
7
+ Mission Control for your projects and teams.
4
8
 
5
9
  Houston interfaces with your version-control, ticket-tracking, continuous integration, and other systems to stitch together a picture of your projects and teams.
6
10
 
7
- It makes it easy to set up **triggers** and **notifications** like:
11
+ It makes it easy to set up **[triggers](https://github.com/houston/houston-core/wiki/Triggers)** to perform tasks like:
8
12
 
9
13
  - Resolving an exception report when a commit that mentions it is deployed
10
14
  - Slacking team members when a pull request is labeled or unlabeled
@@ -12,23 +16,26 @@ It makes it easy to set up **triggers** and **notifications** like:
12
16
 
13
17
  And it provides a foundation for custom views like **dashboards** and **reports**.
14
18
 
15
- Houston is also extensible through **[Modules](https://github.com/houston/houston-core/wiki/Modules)**.
16
-
19
+ Houston is also extensible through **[Modules](https://github.com/houston/houston-core/wiki/Modules)** like:
17
20
 
21
+ - [Houston::Slack](https://github.com/houston/houston-slack), which gives Houston the ability to listen to messages—and respond—via Slack
22
+ - [Houston::Alerts](https://github.com/houston/houston-alerts), which gives Houston the ability to treat tasks from arbitrary sources as a unified queue
23
+ - [Houston::Feedback](https://github.com/houston/houston-feedback), which adds a view for quickly importing, tagging, and searching customer feedback
24
+ - [Houston::Roadmaps](https://github.com/houston/houston-roadmaps), which adds a view for planning project milestones
18
25
 
19
- ## Getting Started with Houston
20
26
 
21
- ##### System Requirements
27
+ ## Requirements
22
28
 
23
29
  To use Houston, you must have
24
30
 
25
31
  - [Ruby 2.0+](https://www.ruby-lang.org/en/downloads)
26
32
  - [Postgres 9.4+](http://www.postgresql.org/download)
27
33
 
28
- ##### Hello World
34
+
35
+ ## Getting Started
29
36
 
30
37
  1. Install houston-core
31
-
38
+
32
39
  ```
33
40
  gem install houston-core
34
41
  ```
@@ -52,3 +59,8 @@ To use Houston, you must have
52
59
  ```
53
60
  bundle exec rails server
54
61
  ```
62
+
63
+
64
+ ## License
65
+
66
+ Houston is released under the [MIT License](http://www.opensource.org/licenses/MIT).
@@ -8,8 +8,29 @@ td.action-reliability {
8
8
  padding-right: 16px !important;
9
9
  }
10
10
 
11
- .action-success-rate {
12
- font-size: 0.75em;
13
- line-height: 20px;
14
- float: right;
11
+ .action-params {
12
+ pre {
13
+ border: none;
14
+ border-radius: 0;
15
+ background: 0;
16
+ font-size: 12px;
17
+ line-height: 16px;
18
+ max-width: 600px;
19
+ white-space: pre;
20
+ overflow-x: scroll;
21
+ margin: 0;
22
+ }
23
+
24
+ .action-params-short {
25
+ display: inline-block;
26
+ vertical-align: middle;
27
+ cursor: pointer;
28
+ margin: -6px -8px;
29
+
30
+ &:hover {
31
+ background: rgba(0, 136, 204, 0.08);
32
+ color: #0088cc;
33
+ }
34
+ }
35
+ .action-params-full { display: none; }
15
36
  }
@@ -56,7 +56,7 @@ h2.light {
56
56
  .task-worker { white-space: nowrap; }
57
57
 
58
58
  #average_effort {
59
- font-weight: 800;
59
+ font-weight: 700;
60
60
  }
61
61
  }
62
62
 
@@ -60,7 +60,7 @@
60
60
  .project-tests tbody {
61
61
  .test-suite-name {
62
62
  color: #777;
63
- font-weight: 800;
63
+ font-weight: 700;
64
64
  }
65
65
 
66
66
  .test-name {
@@ -24,7 +24,7 @@ h2.test-result-banner {
24
24
  font-size: 1.5em;
25
25
  margin: 0;
26
26
 
27
- .test-result-committer { font-weight: 800; }
27
+ .test-result-committer { font-weight: 700; }
28
28
  img { width: 1em; height: 1em; }
29
29
  }
30
30
  }
@@ -126,7 +126,7 @@ h2.test-result-banner {
126
126
  text-transform: uppercase;
127
127
  border-radius: 2px;
128
128
  color: white;
129
- font-weight: 800;
129
+ font-weight: 700;
130
130
  text-indent: 0;
131
131
 
132
132
  &.pass { color: #5DB64C; } // grass
@@ -186,7 +186,7 @@ $lightGray: #eee;
186
186
 
187
187
  .ticket-resolution {
188
188
  font-size: 0.88em;
189
- font-weight: 800;
189
+ font-weight: 700;
190
190
  color: #aaa;
191
191
 
192
192
  &::after { content: ':'; }
@@ -27,7 +27,7 @@
27
27
  url('<%= font_path("roboto-bold-webfont.woff") %>') format('woff'),
28
28
  url('<%= font_path("roboto-bold-webfont.ttf") %>') format('truetype'),
29
29
  url('<%= font_path("roboto-bold-webfont.svg") %>#robotobold') format('svg');
30
- font-weight: 800;
30
+ font-weight: 700;
31
31
  font-style: normal;
32
32
  }
33
33
 
@@ -71,7 +71,7 @@
71
71
  url('<%= font_path("roboto-bolditalic-webfont.woff") %>') format('woff'),
72
72
  url('<%= font_path("roboto-bolditalic-webfont.ttf") %>') format('truetype'),
73
73
  url('<%= font_path("roboto-bolditalic-webfont.svg") %>#robotobold_italic') format('svg');
74
- font-weight: 800;
74
+ font-weight: 700;
75
75
  font-style: italic;
76
76
  }
77
77
 
@@ -1,9 +1,10 @@
1
1
  class ActionsController < ApplicationController
2
2
 
3
3
  def index
4
- authorize! :show, :actions
4
+ authorize! :read, Action
5
5
 
6
- actions_by_name = Houston.actions.names.each_with_object({}) { |name, map| map[name] = { name: name } }
6
+ actions_by_name = Houston.actions.to_a.each_with_object({}) { |action, map|
7
+ map[action.name] = { name: action.name, required_params: action.required_params } }
7
8
  actions = Action.where(name: actions_by_name.keys)
8
9
 
9
10
  most_recent_actions = actions.joins(<<-SQL)
@@ -25,13 +26,20 @@ class ActionsController < ApplicationController
25
26
  end
26
27
 
27
28
  def show
28
- authorize! :show, :actions
29
+ authorize! :read, Action
29
30
  @action_name = params[:slug]
30
- @actions = Action.where(name: @action_name).preload(:error)
31
+ @actions = Action.where(name: @action_name).preload(:error).limit(50)
32
+ @actions = @actions.where(Action.arel_table[:started_at].lt(params[:before])) if params[:before]
33
+ render partial: "actions/actions" if request.xhr?
34
+ end
35
+
36
+ def running
37
+ authorize! :read, Action
38
+ @actions = Action.where(finished_at: nil)
31
39
  end
32
40
 
33
41
  def run
34
- authorize! :run, :actions
42
+ authorize! :run, Action
35
43
  Houston.actions.run params[:slug]
36
44
  redirect_to "/actions", notice: "#{params[:slug]} is running"
37
45
  end
@@ -1,8 +1,8 @@
1
1
  class ErrorsController < ApplicationController
2
2
 
3
3
  def index
4
- authorize! :show, Error
5
- @actions = Action.reorder(finished_at: :desc).where.not(error_id: nil).includes(:error).limit(50)
4
+ authorize! :read, Action
5
+ @actions = Action.reorder(finished_at: :desc).where.not(error_id: nil).preload(:error).limit(50)
6
6
  @actions = @actions.where(Action.arel_table[:finished_at].lt(params[:before])) if params[:before]
7
7
  render partial: "errors/actions" if request.xhr?
8
8
  end
@@ -50,7 +50,7 @@ class ReleasesController < ApplicationController
50
50
  authorize! :create, @release
51
51
 
52
52
  if @release.save
53
- ProjectNotification.release(@release).deliver! if params[:send_release_email]
53
+ Houston.deliver! ProjectNotification.release(@release) if params[:send_release_email]
54
54
  @release.tickets.resolve_all! if params[:resolve_tickets]
55
55
 
56
56
  redirect_to @release
@@ -1,7 +1,7 @@
1
1
  class TriggersController < ApplicationController
2
2
 
3
3
  def index
4
- authorize! :show, :triggers
4
+ authorize! :read, Action
5
5
  @triggers = Houston.triggers
6
6
  end
7
7
 
@@ -1,7 +1,42 @@
1
1
  module ActionsHelper
2
2
 
3
3
  def format_action_params(params)
4
- MultiJson.dump params
4
+ return "<pre>{}</pre>".html_safe if params == {}
5
+ <<-HTML.html_safe
6
+ <div class="action-params-short">
7
+ <pre>{ #{params.keys.map(&:inspect).join(", ")} }</pre>
8
+ </div>
9
+ <div class="action-params-full">
10
+ <pre>#{_add_white_space Houston::ParamsSerializer.new.dump(params)}</pre>
11
+ </div>
12
+ HTML
13
+ end
14
+
15
+ def _add_white_space(json)
16
+ scanner = StringScanner.new(json)
17
+ output = ""
18
+ indent = 0
19
+ until scanner.eos?
20
+ match = scanner.scan(/(?:[\[\]\{\}":,]|[^\[\]\{\}":,]+)/)
21
+ case match
22
+ when "{", "["
23
+ indent += 2
24
+ output << "#{match}\n#{" " * indent}"
25
+ when "}", "]"
26
+ indent -= 2
27
+ output << "\n#{" " * indent}#{match}"
28
+ when "\""
29
+ # hopefully this grabs the entire string
30
+ output << "\"" << scanner.scan(/.*?(?<!\\)"/)
31
+ when ":"
32
+ output << ": "
33
+ when ","
34
+ output << ",\n#{" " * indent}"
35
+ else
36
+ output << match
37
+ end
38
+ end
39
+ output
5
40
  end
6
41
 
7
42
  end
@@ -2,7 +2,9 @@ module NavigationHelper
2
2
 
3
3
  def render_navigation(key)
4
4
  renderer = Houston.get_navigation_renderer(key)
5
- instance_eval &renderer
5
+ return unless renderer.permitted?(current_ability)
6
+
7
+ render_nav_link renderer.name, renderer.path, icon: renderer.icon
6
8
  rescue KeyError
7
9
  Rails.logger.error "\e[31;1mThere is no navigation renderer named #{key.inspect}\e[0m"
8
10
  nil
@@ -20,6 +22,9 @@ module NavigationHelper
20
22
  return unless feature.permitted?(current_ability, current_project)
21
23
 
22
24
  render_nav_link feature.name, feature.project_path(current_project), icon: feature.icon
25
+ rescue KeyError
26
+ Rails.logger.error "\e[31;1mThere is no project feature named #{feature.inspect}\e[0m"
27
+ nil
23
28
  end
24
29
 
25
30
  def render_nav_menu(name, items: [], icon: "fa-circle-thin")
@@ -4,7 +4,7 @@ module Github
4
4
 
5
5
  self.table_name = "pull_requests"
6
6
 
7
- attr_readonly :project_id, :user_id, :repo, :number, :username, :base_ref, :base_sha, :url
7
+ attr_readonly :project_id, :user_id, :repo, :number, :username, :base_ref, :url
8
8
  attr_accessor :actor
9
9
 
10
10
  belongs_to :project
@@ -19,12 +19,14 @@ module Github
19
19
 
20
20
  after_create do
21
21
  Houston.observer.fire "github:pull:opened", pull_request: self
22
+ true
22
23
  end
23
24
 
24
25
  after_update do
25
26
  Houston.observer.fire "github:pull:updated", pull_request: self, changes: changes
26
27
  Houston.observer.fire "github:pull:closed", pull_request: self if closed_at_changed? && closed_at
27
28
  Houston.observer.fire "github:pull:reopened", pull_request: self if closed_at_changed? && !closed_at
29
+ true
28
30
  end
29
31
 
30
32
  validates :project_id, :title, :number, :repo, :url, :base_ref, :base_sha, :head_ref, :head_sha, :username, presence: true
@@ -88,8 +90,10 @@ module Github
88
90
 
89
91
  def sync!(projects = Project.unretired)
90
92
  expected_pulls = fetch!(projects)
93
+ existing_pulls = Houston.benchmark "Loading pull requests" do
94
+ open.where(project_id: projects.ids).to_a
95
+ end
91
96
  Houston.benchmark "Syncing pull requests" do
92
- existing_pulls = all.to_a
93
97
 
94
98
  # Fetch unexpected pulls so that we know
95
99
  # when they were closed and whether they
@@ -108,10 +112,15 @@ module Github
108
112
  expected_pr["base"]["repo"]["name"] == existing_pr.repo &&
109
113
  expected_pr["number"] == existing_pr.number }
110
114
 
115
+ # Maybe the pull request was closed?
116
+ existing_pr ||= where(repo: expected_pr["base"]["repo"]["name"], number: expected_pr["number"]).first
117
+
111
118
  existing_pr ||= Github::PullRequest.new
112
119
  existing_pr.merge_attributes(expected_pr)
113
- unless existing_pr.save
114
- Rails.logger.warn "\e[31m[pulls] Invalid PR: #{existing_pr.errors.full_messages.join("; ")}\e[0m"
120
+ if existing_pr.changes.any?
121
+ unless existing_pr.save
122
+ Rails.logger.warn "\e[31m[pulls] Invalid PR: #{existing_pr.errors.full_messages.join("; ")}\e[0m"
123
+ end
115
124
  end
116
125
  existing_pr
117
126
  end