Commit 9e505cfd99e69fa1679ec83a8179b7f94a14a948

Authored by Andrey Subbota
2 parents 8f096106 483bfce2
Exists in master and in 1 other branch production

Merge branch 'errbit' into feature/upstream_merge

.travis.yml
  1 +language: ruby
1 2 rvm:
2 3 - 1.9.3
3 4 - 1.9.2
4 5 - 1.8.7
5 6  
6   -
7 7 # To stop Travis from running tests for a new commit,
8 8 # add the following to your commit message: [ci skip]
9 9 # You should add this when you edit documentation or comments, etc.
10   -
... ...
Gemfile
1 1 source 'http://rubygems.org'
2 2  
3   -gem 'rails', '~> 3.2.0'
  3 +gem 'rails', '3.2.6'
4 4  
5 5 gem 'nokogiri'
6 6 gem 'mongoid', '~> 2.4.10'
... ... @@ -45,7 +45,7 @@ group :development, :test do
45 45 gem 'webmock', :require => false
46 46 unless ENV["CI"]
47 47 gem 'ruby-debug', :platform => :mri_18
48   - gem (RUBY_VERSION == "1.9.2" ? 'ruby-debug19' : 'debugger'), :platform => :mri_19
  48 + gem 'debugger', :platform => :mri_19
49 49 end
50 50 # gem 'rpm_contrib', :git => "git://github.com/bensymonds/rpm_contrib.git", :branch => "mongo-1.4.0_update"
51 51 end
... ...
Gemfile.lock
... ... @@ -2,35 +2,35 @@ GEM
2 2 remote: http://rubygems.org/
3 3 specs:
4 4 SystemTimer (1.2.3)
5   - actionmailer (3.2.3)
6   - actionpack (= 3.2.3)
  5 + actionmailer (3.2.6)
  6 + actionpack (= 3.2.6)
7 7 mail (~> 2.4.4)
8 8 actionmailer_inline_css (1.3.1)
9 9 actionmailer (>= 3.0.0)
10 10 nokogiri (>= 1.4.4)
11 11 premailer (>= 1.7.1)
12   - actionpack (3.2.3)
13   - activemodel (= 3.2.3)
14   - activesupport (= 3.2.3)
  12 + actionpack (3.2.6)
  13 + activemodel (= 3.2.6)
  14 + activesupport (= 3.2.6)
15 15 builder (~> 3.0.0)
16 16 erubis (~> 2.7.0)
17 17 journey (~> 1.0.1)
18 18 rack (~> 1.4.0)
19 19 rack-cache (~> 1.2)
20 20 rack-test (~> 0.6.1)
21   - sprockets (~> 2.1.2)
22   - activemodel (3.2.3)
23   - activesupport (= 3.2.3)
  21 + sprockets (~> 2.1.3)
  22 + activemodel (3.2.6)
  23 + activesupport (= 3.2.6)
24 24 builder (~> 3.0.0)
25   - activerecord (3.2.3)
26   - activemodel (= 3.2.3)
27   - activesupport (= 3.2.3)
  25 + activerecord (3.2.6)
  26 + activemodel (= 3.2.6)
  27 + activesupport (= 3.2.6)
28 28 arel (~> 3.0.2)
29 29 tzinfo (~> 0.3.29)
30   - activeresource (3.2.3)
31   - activemodel (= 3.2.3)
32   - activesupport (= 3.2.3)
33   - activesupport (3.2.3)
  30 + activeresource (3.2.6)
  31 + activemodel (= 3.2.6)
  32 + activesupport (= 3.2.6)
  33 + activesupport (3.2.6)
34 34 i18n (~> 0.6)
35 35 multi_json (~> 1.0)
36 36 addressable (2.2.8)
... ... @@ -94,7 +94,7 @@ GEM
94 94 inherited_resources (1.3.1)
95 95 has_scope (~> 0.5.0)
96 96 responders (~> 0.6)
97   - journey (1.0.3)
  97 + journey (1.0.4)
98 98 json (1.7.3)
99 99 kaminari (0.13.0)
100 100 actionpack (>= 3.0.0)
... ... @@ -174,23 +174,23 @@ GEM
174 174 rack-ssl-enforcer (0.2.4)
175 175 rack-test (0.6.1)
176 176 rack (>= 1.0)
177   - rails (3.2.3)
178   - actionmailer (= 3.2.3)
179   - actionpack (= 3.2.3)
180   - activerecord (= 3.2.3)
181   - activeresource (= 3.2.3)
182   - activesupport (= 3.2.3)
  177 + rails (3.2.6)
  178 + actionmailer (= 3.2.6)
  179 + actionpack (= 3.2.6)
  180 + activerecord (= 3.2.6)
  181 + activeresource (= 3.2.6)
  182 + activesupport (= 3.2.6)
183 183 bundler (~> 1.0)
184   - railties (= 3.2.3)
  184 + railties (= 3.2.6)
185 185 rails_autolink (1.0.9)
186 186 rails (~> 3.1)
187   - railties (3.2.3)
188   - actionpack (= 3.2.3)
189   - activesupport (= 3.2.3)
  187 + railties (3.2.6)
  188 + actionpack (= 3.2.6)
  189 + activesupport (= 3.2.6)
190 190 rack-ssl (~> 1.3.2)
191 191 rake (>= 0.8.7)
192 192 rdoc (~> 3.4)
193   - thor (~> 0.14.6)
  193 + thor (>= 0.14.6, < 2.0)
194 194 raindrops (0.8.0)
195 195 rake (0.9.2.2)
196 196 rbx-require-relative (0.0.9)
... ... @@ -238,7 +238,7 @@ GEM
238 238 daemons (>= 1.0.9)
239 239 eventmachine (>= 0.12.6)
240 240 rack (>= 1.0.0)
241   - thor (0.14.6)
  241 + thor (0.15.2)
242 242 tilt (1.3.3)
243 243 treetop (1.4.10)
244 244 polyglot
... ... @@ -293,7 +293,7 @@ DEPENDENCIES
293 293 oruen_redmine_client
294 294 pivotal-tracker
295 295 rack-ssl-enforcer
296   - rails (~> 3.2.0)
  296 + rails (= 3.2.6)
297 297 rails_autolink (~> 1.0.9)
298 298 ri_cal
299 299 rspec (~> 2.6)
... ...
README.md
... ... @@ -144,10 +144,10 @@ git clone http://github.com/errbit/errbit.git
144 144 ```bash
145 145 gem install heroku
146 146 heroku create example-errbit --stack cedar
147   -heroku addons:add mongohq:free
148   -cp -f config/mongoid.mongohq.yml config/mongoid.yml
  147 +heroku addons:add mongolab:starter
  148 +cp -f config/mongoid.mongolab.yml config/mongoid.yml
149 149 git add -f config/mongoid.yml
150   -git commit -m "Added mongoid config for MongoHQ"
  150 +git commit -m "Added mongoid config for Mongolab"
151 151 heroku addons:add sendgrid:starter
152 152 heroku config:add HEROKU=true
153 153 heroku config:add ERRBIT_HOST=some-hostname.example.com
... ... @@ -163,18 +163,35 @@ heroku run rake db:seed
163 163  
164 164 * If you are using a free database on Heroku, you may want to periodically clear resolved errors to free up space.
165 165  
166   -```bash
167   -# Install the heroku cron addon, to clear resolved errors daily:
168   -heroku addons:add cron:daily
  166 + * With the heroku-scheduler add-on (replacement for cron):
169 167  
170   -# Or, clear resolved errors manually:
171   -heroku rake errbit:db:clear_resolved
172   -```
  168 + ```bash
  169 + # Install the heroku scheduler add-on
  170 + heroku addons:add scheduler:standard
  171 +
  172 + # Go open the dashboard to schedule the job. You should use
  173 + # 'rake errbit:db:clear_resolved' as the task command, and schedule it
  174 + # at whatever frequency you like (once/day should work great).
  175 + heroku addons:open scheduler
  176 + ```
  177 +
  178 + * With the cron add-on:
  179 +
  180 + ```bash
  181 + # Install the heroku cron addon, to clear resolved errors daily:
  182 + heroku addons:add cron:daily
  183 + ```
  184 +
  185 + * Or clear resolved errors manually:
  186 +
  187 + ```bash
  188 + heroku rake errbit:db:clear_resolved
  189 + ```
173 190  
174 191 * You may want to enable the deployment hook for heroku :
175 192  
176 193 ```bash
177   -heroku addons:add deployhooks:http url="http://YOUR_ERRBIT_HOST/deploys.txt?api_key=YOUR_API_KEY"
  194 +heroku addons:add deployhooks:http --url="http://YOUR_ERRBIT_HOST/deploys.txt?api_key=YOUR_API_KEY"
178 195 ```
179 196  
180 197 * Enjoy!
... ... @@ -216,6 +233,43 @@ You can change the requested account permissions by setting `github_access_scope
216 233 </table>
217 234  
218 235  
  236 +### GitHub authentication when served on Heroku
  237 +
  238 +You will need to set up Heroku variables accordingly as described in [Configuring GitHub authentication](#configuring-github-authentication):
  239 +
  240 +* GITHUB_AUTHENTICATION
  241 +
  242 +```bash
  243 +heroku config:add GITHUB_AUTHENTICATION=true
  244 +```
  245 +
  246 +* GITHUB_CLIENT_ID
  247 +
  248 +```bash
  249 +heroku config:add GITHUB_CLIENT_ID=the_client_id_provided_by_GitHub
  250 +```
  251 +
  252 +* GITHUB_SECRET
  253 +
  254 +```bash
  255 +heroku config:add GITHUB_SECRET=the_secret_provided_by_GitHub
  256 +```
  257 +
  258 +* GITHUB_ACCESS_SCOPE - set only one scope `repo` or `public_repo`. If you really need to put more than one, separate them with comma.
  259 +
  260 +```bash
  261 +heroku config:add GITHUB_ACCESS_SCOPE=repo,public_repo
  262 +```
  263 +
  264 +__Note__: To avoid restarting your Heroku app 4 times you can set Heroku variables in a single command, i.e:
  265 +
  266 +```bash
  267 +heroku config:add GITHUB_AUTHENTICATION=true \
  268 +GITHUB_CLIENT_ID=the_client_id_provided_by_GitHub \
  269 +GITHUB_SECRET=the_secret_provided_by_GitHub \
  270 +GITHUB_ACCESS_SCOPE=repo,public_repo
  271 +```
  272 +
219 273 ### Configuring LDAP authentication:
220 274  
221 275 * In `config/config.yml`, set `user_has_username` to `true`
... ...
app/assets/javascripts/application.js.erb
... ... @@ -6,6 +6,7 @@
6 6 //= require jquery.alerts
7 7 //= require rails.alerts
8 8 //= require errbit
  9 +//= require apps.show
9 10 //= require_self
10 11  
11 12 // Allow any gems named 'errbit_*' to require their own assets
... ...
app/controllers/errs_controller.rb
... ... @@ -46,7 +46,7 @@ class ErrsController &lt; ApplicationController
46 46 else
47 47 @tracker = GithubIssuesTracker.new(
48 48 :app => @app,
49   - :login => current_user.github_login,
  49 + :username => current_user.github_login,
50 50 :oauth_token => current_user.github_oauth_token
51 51 )
52 52 end
... ... @@ -114,8 +114,8 @@ class ErrsController &lt; ApplicationController
114 114 end
115 115  
116 116 def destroy_several
117   - @selected_problems.each(&:destroy)
118   - flash[:notice] = "#{pluralize(@selected_problems.count, 'err has', 'errs have')} been deleted."
  117 + nb_problem_destroy = ProblemDestroy.execute(@selected_problems)
  118 + flash[:notice] = "#{pluralize(nb_problem_destroy, 'err has', 'errs have')} been deleted."
119 119 redirect_to :back
120 120 end
121 121  
... ...
app/interactors/problem_destroy.rb 0 → 100644
... ... @@ -0,0 +1,48 @@
  1 +class ProblemDestroy
  2 +
  3 + attr_reader :problem
  4 +
  5 + def initialize(problem)
  6 + @problem = problem
  7 + end
  8 +
  9 + def execute
  10 + delete_errs
  11 + delete_comments
  12 + problem.delete
  13 + end
  14 +
  15 + ##
  16 + # Destroy all problem pass in args
  17 + #
  18 + # @params [ Array[Problem] ] problems the list of problem need to be delete
  19 + # can be a single Problem
  20 + # @return [ Integer ]
  21 + # the number of problem destroy
  22 + #
  23 + def self.execute(problems)
  24 + Array(problems).each{ |problem|
  25 + ProblemDestroy.new(problem).execute
  26 + }.count
  27 + end
  28 +
  29 + private
  30 +
  31 + def errs_id
  32 + problem.errs.only(:id).map(&:id)
  33 + end
  34 +
  35 + def comments_id
  36 + problem.comments.only(:id).map(&:id)
  37 + end
  38 +
  39 + def delete_errs
  40 + Err.collection.remove(:_id => { '$in' => errs_id })
  41 + Notice.collection.remove(:err_id => { '$in' => errs_id })
  42 + end
  43 +
  44 + def delete_comments
  45 + Comment.collection.remove(:_id => { '$in' => comments_id })
  46 + end
  47 +
  48 +end
... ...
app/models/issue_trackers/github_issues_tracker.rb
... ... @@ -21,7 +21,7 @@ class IssueTrackers::GithubIssuesTracker &lt; IssueTracker
21 21  
22 22 def check_params
23 23 if Fields.detect {|f| self[f[0]].blank? }
24   - errors.add :base, 'You must specify your GitHub repository, username and password'
  24 + errors.add :base, 'You must specify your GitHub username and password'
25 25 end
26 26 end
27 27  
... ...
app/models/issue_trackers/pivotal_labs_tracker.rb
... ... @@ -17,11 +17,18 @@ class IssueTrackers::PivotalLabsTracker &lt; IssueTracker
17 17 PivotalTracker::Client.token = api_token
18 18 PivotalTracker::Client.use_ssl = true
19 19 project = PivotalTracker::Project.find project_id.to_i
20   - story = project.stories.create :name => issue_title(problem), :story_type => 'bug', :description => body_template.result(binding)
21   - problem.update_attributes(
22   - :issue_link => "https://www.pivotaltracker.com/story/show/#{story.id}",
23   - :issue_type => Label
24   - )
  20 + story = project.stories.create :name => issue_title(problem),
  21 + :story_type => 'bug', :description => body_template.result(binding),
  22 + :requested_by => reported_by.name
  23 +
  24 + if story.errors.present?
  25 + raise IssueTrackers::IssueTrackerError, story.errors.first
  26 + else
  27 + problem.update_attributes(
  28 + :issue_link => "https://www.pivotaltracker.com/story/show/#{story.id}",
  29 + :issue_type => Label
  30 + )
  31 + end
25 32 end
26 33  
27 34 def body_template
... ...
app/models/notice.rb
... ... @@ -94,6 +94,14 @@ class Notice
94 94 backtrace.select { |l| l && l['file'] && l['file'].include?("[PROJECT_ROOT]") }
95 95 end
96 96  
  97 + def backtrace
  98 + # If gems are vendored into project, treat vendored gem dir as [GEM_ROOT]
  99 + (read_attribute(:backtrace) || []).map do |line|
  100 + # Changes "[PROJECT_ROOT]/rubygems/ruby/1.9.1/gems" to "[GEM_ROOT]/gems"
  101 + line.merge 'file' => line['file'].to_s.gsub(/\[PROJECT_ROOT\]\/.*\/ruby\/[0-9.]+\/gems/, '[GEM_ROOT]/gems')
  102 + end
  103 + end
  104 +
97 105 protected
98 106  
99 107 def increase_counter_cache
... ... @@ -121,7 +129,7 @@ class Notice
121 129 send("#{h}=",sanitize_hash(send(h)))
122 130 end
123 131 # Set unknown backtrace files
124   - backtrace.each{|line| line['file'] = "[unknown source]" if line['file'].blank? }
  132 + read_attribute(:backtrace).each{|line| line['file'] = "[unknown source]" if line['file'].blank? }
125 133 end
126 134  
127 135 def sanitize_hash(h)
... ...
app/models/problem.rb
... ... @@ -119,7 +119,7 @@ class Problem
119 119 end
120 120 collection.update({'_id' => self.id},
121 121 {'$set' => {'app_name' => self.app_name,
122   - 'last_deploy_at' => self.last_deploy_at}})
  122 + 'last_deploy_at' => self.last_deploy_at.try(:utc)}})
123 123 end
124 124 end
125 125  
... ...
app/views/apps/show.html.haml
1 1 - content_for :title, @app.name
2 2 - content_for :head do
3 3 = auto_discovery_link_tag :atom, app_path(@app, User.token_authentication_key => current_user.authentication_token, :format => "atom"), :title => "Errbit notices for #{@app.name} at #{request.host}"
4   - = javascript_include_tag 'apps.show'
5 4 - content_for :meta do
6 5 %strong Errors Caught:
7 6 = @app.problems.count
... ... @@ -12,7 +11,7 @@
12 11 - content_for :action_bar do
13 12 - if current_user.admin?
14 13 = link_to 'edit', edit_app_path(@app), :class => 'button'
15   - = link_to 'destroy', app_path(@app), :method => :delete, :confirm => 'Seriously?', :class => 'button'
  14 + = link_to 'destroy', app_path(@app), :method => :delete, :data => { :confirm => 'Seriously?' }, :class => 'button'
16 15 - if @all_errs
17 16 = link_to 'unresolved errs', app_path(@app), :class => 'button'
18 17 - else
... ...
app/views/errs/_issue_tracker_links.html.haml
1 1 - if @app.issue_tracker_configured? || current_user.github_account?
2 2 - if @problem.issue_link.present?
3 3 %span= link_to 'go to issue', @problem.issue_link, :class => "#{@problem.issue_type}_goto goto-issue"
4   - = link_to 'unlink issue', unlink_issue_app_err_path(@app, @problem), :method => :delete, :confirm => "Unlink err issues?", :class => "unlink-issue"
  4 + = link_to 'unlink issue', unlink_issue_app_err_path(@app, @problem), :method => :delete, :data => { :confirm => "Unlink err issues?" }, :class => "unlink-issue"
5 5 - elsif @problem.issue_link == "pending"
6 6 %span.disabled= link_to 'creating...', '#', :class => "#{@problem.issue_type}_inactive create-issue"
7 7 = link_to 'retry', create_issue_app_err_path(@app, @problem), :method => :post
8 8 - else
9   - - if current_user.can_create_github_issues? && @app.github_repo?
10   - %span= link_to 'create issue', create_issue_app_err_path(@app, @problem, :tracker => 'user_github'), :method => :post, :class => "github_create create-issue"
  9 + - if @app.github_repo?
  10 + - if current_user.can_create_github_issues?
  11 + %span= link_to 'create issue', create_issue_app_err_path(@app, @problem, :tracker => 'user_github'), :method => :post, :class => "github_create create-issue"
  12 + - elsif @app.issue_tracker_configured? && @app.issue_tracker.is_a?(GithubIssuesTracker)
  13 + %span= link_to 'create issue', create_issue_app_err_path(@app, @problem), :method => :post, :class => "github_create create-issue"
11 14 - if @app.issue_tracker_configured? && !@app.issue_tracker.is_a?(GithubIssuesTracker)
12 15 %span= link_to 'create issue', create_issue_app_err_path(@app, @problem), :method => :post, :class => "#{@app.issue_tracker.label}_create create-issue"
... ...
app/views/errs/_table.html.haml
... ... @@ -40,7 +40,7 @@
40 40 %td.issue_link
41 41 - if problem.app.issue_tracker_configured? && problem.issue_link.present? && problem.issue_link != 'pending'
42 42 = link_to image_tag("#{problem.issue_type}_goto.png"), problem.issue_link, :target => "_blank"
43   - %td.resolve= link_to image_tag("thumbs-up.png"), resolve_app_err_path(problem.app, problem), :title => "Resolve", :method => :put, :confirm => err_confirm, :class => 'resolve' if problem.unresolved?
  43 + %td.resolve= link_to image_tag("thumbs-up.png"), resolve_app_err_path(problem.app, problem), :title => "Resolve", :method => :put, :data => { :confirm => err_confirm }, :class => 'resolve' if problem.unresolved?
44 44 - if errs.none?
45 45 %tr
46 46 %td{:colspan => (any_issue_links ? 8 : 7)}
... ...
app/views/errs/show.html.haml
... ... @@ -13,7 +13,7 @@
13 13 = last_notice_at(@problem).to_s(:precise)
14 14 - content_for :action_bar do
15 15 - if @problem.unresolved?
16   - %span= link_to 'resolve', resolve_app_err_path(@app, @problem), :method => :put, :confirm => err_confirm, :class => 'resolve'
  16 + %span= link_to 'resolve', resolve_app_err_path(@app, @problem), :method => :put, :data => { :confirm => err_confirm }, :class => 'resolve'
17 17 - if current_user.authentication_token
18 18 %span= link_to 'iCal', app_err_path(:app_id => @app.id, :id => @problem.id, :format => "ics", :auth_token => current_user.authentication_token), :class => "calendar_link"
19 19 %span>= link_to 'up', (request.env['HTTP_REFERER'] ? :back : app_errs_path(@app)), :class => 'up'
... ... @@ -28,7 +28,7 @@
28 28 %table.comment
29 29 %tr
30 30 %th
31   - %span= link_to '&#10008;'.html_safe, app_err_comment_path(@app, @problem, comment), :method => :delete, :confirm => "Are sure you don't need this comment?", :class => "destroy-comment"
  31 + %span= link_to '&#10008;'.html_safe, app_err_comment_path(@app, @problem, comment), :method => :delete, :data => { :confirm => "Are sure you don't need this comment?" }, :class => "destroy-comment"
32 32 = time_ago_in_words(comment.created_at, true) << " ago by "
33 33 = link_to comment.user.email, user_path(comment.user)
34 34 %tr
... ...
app/views/notices/_user_attributes.html.haml
... ... @@ -6,4 +6,4 @@
6 6 - user.each do |user_key, user_value|
7 7 %tr
8 8 %th= user_key
9   - %td= auto_link(user_value).html_safe
  9 + %td= auto_link(user_value.to_s).html_safe
... ...
app/views/shared/_link_github_account.html.haml
1 1 - if Errbit::Config.github_authentication && user == current_user
2 2 - if user.github_account?
3   - %span.unlink_github= link_to "Unlink GitHub account", unlink_github_user_path(user), :method => :delete, :confirm => "Are you sure?"
  3 + %span.unlink_github= link_to "Unlink GitHub account", unlink_github_user_path(user), :method => :delete, :data => { :confirm => "Are you sure?" }
4 4 - else
5 5 %span.github= link_to "Link GitHub account", user_omniauth_authorize_path(:github)
... ...
app/views/users/_fields.html.haml
... ... @@ -18,7 +18,7 @@
18 18 = f.text_field :github_login
19 19  
20 20 .required
21   - = f.label 'Entries per page'
  21 + = f.label :per_page, 'Entries per page'
22 22 = f.select :per_page, [10, 20, 30, 50, 75, 100]
23 23  
24 24 .required
... ...
app/views/users/show.html.haml
... ... @@ -3,7 +3,7 @@
3 3 = render 'shared/link_github_account', :user => @user
4 4 %span= link_to('Add a New User', new_user_path, :class => 'add')
5 5 = link_to 'edit', edit_user_path(@user), :class => 'button'
6   - = link_to 'destroy', user_path(@user), :method => :delete, :confirm => 'Seriously?', :class => 'button'
  6 + = link_to 'destroy', user_path(@user), :method => :delete, :data => { :confirm => 'Seriously?' }, :class => 'button'
7 7  
8 8  
9 9 %table.single_user
... ...
config/environments/production.rb
... ... @@ -28,9 +28,6 @@ Errbit::Application.configure do
28 28 # Use a different cache store in production
29 29 # config.cache_store = :mem_cache_store
30 30  
31   - # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
32   - config.assets.precompile += %w( apps.show.js )
33   -
34 31 # Disable Rails's static asset server
35 32 # In production, Apache or nginx will already do this
36 33 config.serve_static_assets = false
... ...
config/initializers/_load_config.rb
... ... @@ -9,7 +9,7 @@ unless defined?(Errbit::Config)
9 9 if ENV['HEROKU']
10 10 Errbit::Config.host = ENV['ERRBIT_HOST']
11 11 Errbit::Config.email_from = ENV['ERRBIT_EMAIL_FROM']
12   - Errbit::Config.email_at_notices = [1,3,10] #ENV['ERRBIT_EMAIL_AT_NOTICES']
  12 + Errbit::Config.email_at_notices = ENV['ERRBIT_EMAIL_AT_NOTICES']
13 13 Errbit::Config.confirm_resolve_err = ENV['ERRBIT_CONFIRM_RESOLVE_ERR']
14 14 Errbit::Config.user_has_username = ENV['ERRBIT_USER_HAS_USERNAME']
15 15 Errbit::Config.allow_comments_with_issue_tracker = ENV['ERRBIT_ALLOW_COMMENTS_WITH_ISSUE_TRACKER']
... ...
config/initializers/mongo.rb
1   -if mongo = ENV['MONGOHQ_URL'] || ENV['MONGOLAB_URI']
  1 +if mongo = ENV['MONGOLAB_URI'] || ENV['MONGOHQ_URL']
2 2 settings = URI.parse(mongo)
3 3 database_name = settings.path.gsub(/^\//, '')
4 4  
... ...
config/mongoid.mongolab.yml 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +# Mongoid Configuration for MongoLab on Heroku
  2 +# ===========================================
  3 +#
  4 +# Copy this file to config/mongoid.yml,
  5 +# commit it to your repo, then push to heroku.
  6 +
  7 +production:
  8 + uri: <%= ENV['MONGOLAB_URI'] %>
... ...
db/migrate/20120603112130_change_github_url_to_github_repo.rb
... ... @@ -3,7 +3,7 @@ class ChangeGithubUrlToGithubRepo &lt; Mongoid::Migration
3 3 App.collection.update({}, {'$rename' => {'github_url' => 'github_repo'}}, multi: true, safe: true)
4 4 App.all.each do |app|
5 5 app.send :normalize_github_repo
6   - app.save!
  6 + app.save
7 7 end
8 8 end
9 9  
... ...
spec/interactors/problem_destroy_spec.rb 0 → 100644
... ... @@ -0,0 +1,77 @@
  1 +require 'spec_helper'
  2 +
  3 +describe ProblemDestroy do
  4 + let(:problem_destroy) {
  5 + ProblemDestroy.new(problem)
  6 + }
  7 +
  8 + context "in unit way" do
  9 + let(:problem) {
  10 + problem = Problem.new()
  11 + problem.stub(:errs).and_return(mock(:criteria, :only => [err_1, err_2]))
  12 + problem.stub(:comments).and_return(mock(:criteria, :only => [comment_1, comment_2]))
  13 + problem.stub(:delete)
  14 + problem
  15 + }
  16 + let(:err_1) { Err.new }
  17 + let(:err_2) { Err.new }
  18 +
  19 + let(:comment_1) { Comment.new }
  20 + let(:comment_2) { Comment.new }
  21 +
  22 + describe "#initialize" do
  23 + it 'take a problem like args' do
  24 + problem_destroy.problem.should == problem
  25 + end
  26 + end
  27 +
  28 + describe "#execute" do
  29 + it 'destroy the problem himself' do
  30 + problem.should_receive(:delete).and_return(true)
  31 + problem_destroy.execute
  32 + end
  33 +
  34 + it 'delete all errs associate' do
  35 + Err.collection.should_receive(:remove).with(:_id => { '$in' => [err_1.id, err_2.id] })
  36 + problem_destroy.execute
  37 + end
  38 +
  39 + it 'delete all comments associate' do
  40 + Comment.collection.should_receive(:remove).with(:_id => { '$in' => [comment_1.id, comment_2.id] })
  41 + problem_destroy.execute
  42 + end
  43 +
  44 + it 'delete all notice of associate to this errs' do
  45 + Notice.collection.should_receive(:remove).with({:err_id => { '$in' => [err_1.id, err_2.id] }})
  46 + problem_destroy.execute
  47 + end
  48 + end
  49 +
  50 + end
  51 +
  52 + context "in integration way" do
  53 + let!(:problem) { Fabricate(:problem) }
  54 + let!(:comment_1) { Fabricate(:comment, :err => problem) }
  55 + let!(:comment_2) { Fabricate(:comment, :err => problem) }
  56 + let!(:err_1) { Fabricate(:err, :problem => problem) }
  57 + let!(:err_2) { Fabricate(:err, :problem => problem) }
  58 + let!(:notice_1_1) { Fabricate(:notice, :err => err_1) }
  59 + let!(:notice_1_2) { Fabricate(:notice, :err => err_1) }
  60 + let!(:notice_2_1) { Fabricate(:notice, :err => err_2) }
  61 + let!(:notice_2_2) { Fabricate(:notice, :err => err_2) }
  62 +
  63 + it 'should all destroy' do
  64 + problem_destroy.execute
  65 + Problem.where(:_id => problem.id).entries.should be_empty
  66 + Err.where(:_id => err_1.id).entries.should be_empty
  67 + Err.where(:_id => err_2.id).entries.should be_empty
  68 + Comment.where(:_id => comment_1.id).entries.should be_empty
  69 + Comment.where(:_id => comment_2.id).entries.should be_empty
  70 + Notice.where(:_id => notice_1_1.id).entries.should be_empty
  71 + Notice.where(:_id => notice_1_2.id).entries.should be_empty
  72 + Notice.where(:_id => notice_2_1.id).entries.should be_empty
  73 + Notice.where(:_id => notice_2_2.id).entries.should be_empty
  74 + end
  75 + end
  76 +
  77 +end
... ...
spec/mailers/mailer_spec.rb
... ... @@ -5,21 +5,19 @@ describe Mailer do
5 5 include EmailSpec::Helpers
6 6 include EmailSpec::Matchers
7 7  
8   - before do
9   - @notice = Fabricate(:notice, :message => "class < ActionController::Base")
10   - @email = Mailer.err_notification(@notice).deliver
11   - end
  8 + let(:notice) { Fabricate(:notice, :message => "class < ActionController::Base") }
  9 + let!(:email) { Mailer.err_notification(notice).deliver }
12 10  
13 11 it "should send the email" do
14 12 ActionMailer::Base.deliveries.size.should == 1
15 13 end
16 14  
17 15 it "should html-escape the notice's message for the html part" do
18   - @email.should have_body_text("class &lt; ActionController::Base")
  16 + email.should have_body_text("class &lt; ActionController::Base")
19 17 end
20 18  
21 19 it "should have inline css" do
22   - @email.should have_body_text('<p class="backtrace" style="')
  20 + email.should have_body_text('<p class="backtrace" style="')
23 21 end
24 22 end
25 23 end
... ...
spec/models/issue_trackers/pivotal_labs_tracker_spec.rb
1 1 require 'spec_helper'
2 2  
3 3 describe IssueTrackers::PivotalLabsTracker do
4   - it "should create an issue on Pivotal Tracker with problem params, and set issue link for problem" do
5   - notice = Fabricate :notice
6   - tracker = Fabricate :pivotal_labs_tracker, :app => notice.app, :project_id => 10
7   - problem = notice.problem
8 4  
9   - story_id = 5
10   - @issue_link = "https://www.pivotaltracker.com/story/show/#{story_id}"
  5 + let(:user) { Fabricate(:user) }
  6 + let(:notice) { Fabricate(:notice) }
  7 + let(:tracker) { Fabricate :pivotal_labs_tracker, :app => notice.app, :project_id => 10 }
  8 + let(:problem) { notice.problem }
  9 + let(:story_id) { 5 }
  10 + let(:issue_link) { "https://www.pivotaltracker.com/story/show/#{story_id}" }
  11 +
  12 + it "creates an issue on Pivotal Tracker with problem params, and set issue link for problem" do
11 13 project_body = "<project><id>#{tracker.project_id}</id><name>TestProject</name></project>"
12 14 stub_request(:get, "https://www.pivotaltracker.com/services/v3/projects/#{tracker.project_id}").
13   - to_return(:status => 200, :headers => {'Location' => @issue_link}, :body => project_body )
  15 + to_return(:status => 200, :headers => {'Location' => issue_link}, :body => project_body )
14 16 story_body = "<story><name>Test Story</name><id>#{story_id}</id></story>"
15 17 stub_request(:post, "https://www.pivotaltracker.com/services/v3/projects/#{tracker.project_id}/stories").
16   - to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => story_body )
  18 + to_return(:status => 201, :headers => {'Location' => issue_link}, :body => story_body )
17 19  
18   - problem.app.issue_tracker.create_issue(problem)
  20 + problem.app.issue_tracker.create_issue(problem, user)
19 21 problem.reload
20 22  
21 23 requested = have_requested(:post, "https://www.pivotaltracker.com/services/v3/projects/#{tracker.project_id}/stories")
... ... @@ -24,7 +26,20 @@ describe IssueTrackers::PivotalLabsTracker do
24 26 WebMock.should requested.with(:body => /<name>\[#{ problem.environment }\]\[#{problem.where}\] #{problem.message.to_s.truncate(100)}<\/name>/)
25 27 WebMock.should requested.with(:body => /<description>.+<\/description>/m)
26 28  
27   - problem.issue_link.should == @issue_link
  29 + problem.issue_link.should == issue_link
  30 + end
  31 +
  32 + it "raises IssueTrackers::IssueTrackerError exception when invalid params and does not set issue link for problem" do
  33 + project_body = "<project><id>#{tracker.project_id}</id><name>TestProject</name></project>"
  34 + stub_request(:get, "https://www.pivotaltracker.com/services/v3/projects/#{tracker.project_id}").
  35 + to_return(:status => 200, :body => project_body )
  36 + story_body = "<errors><error>Requested by can't be blank</error><error>Requested by can't be blank</error></errors>"
  37 + stub_request(:post, "https://www.pivotaltracker.com/services/v3/projects/#{tracker.project_id}/stories").
  38 + to_return(:status => 422, :body => story_body )
  39 +
  40 + lambda { problem.app.issue_tracker.create_issue(problem, user)
  41 + }.should raise_exception(IssueTrackers::IssueTrackerError, "Requested by can't be blank")
  42 + problem.issue_link.should be_nil
28 43 end
29 44 end
30 45  
... ...
spec/views/apps/index.html.haml_spec.rb
... ... @@ -2,11 +2,11 @@ require &#39;spec_helper&#39;
2 2  
3 3 describe "apps/index.html.haml" do
4 4 before do
5   - app = Fabricate(:app, :deploys => [Fabricate(:deploy, :revision => "123456789abcdef")])
  5 + app = stub_model(App, :deploys => [stub_model(Deploy, :created_at => Time.now, :revision => "123456789abcdef")])
6 6 assign :apps, [app]
7 7 assign :problem_counts, {app.id => 0}
8 8 assign :unresolved_counts, {app.id => 0}
9   - controller.stub(:current_user) { Fabricate(:user) }
  9 + controller.stub(:current_user) { stub_model(User) }
10 10 end
11 11  
12 12 describe "deploy column" do
... ...
spec/views/errs/show.html.haml_spec.rb
... ... @@ -13,6 +13,12 @@ describe &quot;errs/show.html.haml&quot; do
13 13 controller.stub(:current_user) { Fabricate(:user) }
14 14 end
15 15  
  16 + def with_issue_tracker(tracker, problem)
  17 + problem.app.issue_tracker = tracker.new :api_token => "token token token", :project_id => "1234"
  18 + assign :problem, problem
  19 + assign :app, problem.app
  20 + end
  21 +
16 22 describe "content_for :action_bar" do
17 23 def action_bar
18 24 view.content_for(:action_bar)
... ... @@ -35,7 +41,7 @@ describe &quot;errs/show.html.haml&quot; do
35 41 Errbit::Config.stub(:confirm_resolve_err).and_return(false)
36 42 render
37 43  
38   - action_bar.should_not have_selector('a.resolve[data-confirm]')
  44 + action_bar.should have_selector('a.resolve[data-confirm="null"]')
39 45 end
40 46  
41 47 it "should link 'up' to HTTP_REFERER if is set" do
... ... @@ -68,6 +74,16 @@ describe &quot;errs/show.html.haml&quot; do
68 74  
69 75 action_bar.should have_selector("span a.github_create.create-issue", :text => 'create issue')
70 76 end
  77 +
  78 + it 'should allow creating issue for github if application has a github tracker' do
  79 + problem = Fabricate(:problem_with_comments, :app => Fabricate(:app, :github_repo => "test_user/test_repo"))
  80 + with_issue_tracker(GithubIssuesTracker, problem)
  81 + assign :problem, problem
  82 + assign :app, problem.app
  83 + render
  84 +
  85 + action_bar.should have_selector("span a.github_create.create-issue", :text => 'create issue')
  86 + end
71 87 end
72 88 end
73 89  
... ... @@ -87,15 +103,9 @@ describe &quot;errs/show.html.haml&quot; do
87 103 end
88 104  
89 105 context "with issue tracker" do
90   - def with_issue_tracker(problem)
91   - problem.app.issue_tracker = PivotalLabsTracker.new :api_token => "token token token", :project_id => "1234"
92   - assign :problem, problem
93   - assign :app, problem.app
94   - end
95   -
96 106 it 'should not display the comments section' do
97 107 problem = Fabricate(:problem)
98   - with_issue_tracker(problem)
  108 + with_issue_tracker(PivotalLabsTracker, problem)
99 109 render
100 110 view.view_flow.get(:comments).should be_blank
101 111 end
... ... @@ -103,7 +113,7 @@ describe &quot;errs/show.html.haml&quot; do
103 113 it 'should display existing comments' do
104 114 problem = Fabricate(:problem_with_comments)
105 115 problem.reload
106   - with_issue_tracker(problem)
  116 + with_issue_tracker(PivotalLabsTracker, problem)
107 117 render
108 118  
109 119 view.content_for(:comments).should include('Test comment')
... ...
spec/views/notices/_backtrace.html.haml_spec.rb
... ... @@ -2,17 +2,15 @@ require &#39;spec_helper&#39;
2 2  
3 3 describe "notices/_backtrace.html.haml" do
4 4 describe 'missing file in backtrace' do
5   - before do
6   - @notice = Fabricate(:notice, :backtrace => [{
7   - 'number' => rand(999),
8   - 'file' => nil,
9   - 'method' => ActiveSupport.methods.shuffle.first
10   - }])
11   - assign :app, @notice.err.app
  5 + let(:notice) do
  6 + backtrace = { 'number' => rand(999), 'file' => nil, 'method' => ActiveSupport.methods.shuffle.first }
  7 + Fabricate(:notice, :backtrace => [backtrace])
12 8 end
13 9  
14 10 it "should replace nil file with [unknown source]" do
15   - render "notices/backtrace", :lines => @notice.backtrace
  11 + assign :app, notice.err.app
  12 +
  13 + render "notices/backtrace", :lines => notice.backtrace
16 14 rendered.should match(/\[unknown source\]/)
17 15 end
18 16 end
... ...
spec/views/notices/_user_attributes.html.haml_spec.rb 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "notices/_user_attributes.html.haml" do
  4 + describe 'autolink' do
  5 + let(:notice) do
  6 + user_attributes = { 'foo' => {'bar' => 'http://example.com'} }
  7 + Fabricate(:notice, :user_attributes => user_attributes)
  8 + end
  9 +
  10 + it "renders table with user attributes" do
  11 + assign :app, notice.err.app
  12 +
  13 + render "notices/user_attributes", :user => notice.user_attributes
  14 + rendered.should have_link('http://example.com')
  15 + end
  16 + end
  17 +end
  18 +
... ...
spec/views/users/show.html.haml_spec.rb
... ... @@ -2,12 +2,12 @@ require &#39;spec_helper&#39;
2 2  
3 3 describe 'users/show.html.haml' do
4 4 let(:user) do
5   - user = stub_model(User, :created_at => Time.now)
  5 + stub_model(User, :created_at => Time.now)
6 6 end
7 7  
8 8 before do
9 9 Errbit::Config.stub(:github_authentication) { true }
10   - controller.stub(:current_user) { Fabricate(:user) }
  10 + controller.stub(:current_user) { stub_model(User) }
11 11 end
12 12  
13 13 context 'with GitHub authentication' do
... ... @@ -39,9 +39,8 @@ describe &#39;users/show.html.haml&#39; do
39 39  
40 40 context 'viewing own user page' do
41 41 before do
42   - @user = Fabricate(:user)
43   - controller.stub!(:current_user).and_return(@user)
44   - assign :user, @user
  42 + controller.stub(:current_user) { user }
  43 + assign :user, user
45 44 end
46 45  
47 46 it 'shows link github button when no login or token' do
... ... @@ -50,8 +49,9 @@ describe &#39;users/show.html.haml&#39; do
50 49 end
51 50  
52 51 it 'shows unlink github button when login and token' do
53   - @user.github_login = 'test_user'
54   - @user.github_oauth_token = 'abcdef'
  52 + user.github_login = 'test_user'
  53 + user.github_oauth_token = 'abcdef'
  54 +
55 55 render
56 56 view.content_for(:action_bar).should include('Unlink GitHub account')
57 57 end
... ...