Commit e8cd05155a74a59076aa859281cc5bd16b81f0ef

Authored by Nathan Broadbent
1 parent 32964b43
Exists in master and in 1 other branch production

Added Github Issues Tracker.

Gemfile
... ... @@ -12,6 +12,7 @@ gem 'mongoid_rails_migrations'
12 12 gem 'useragent', '~> 0.3.1'
13 13 gem 'pivotal-tracker'
14 14 gem 'ruby-fogbugz', :require => 'fogbugz'
  15 +gem 'octokit'
15 16 gem 'inherited_resources'
16 17  
17 18 group :production do
... ...
Gemfile.lock
... ... @@ -60,10 +60,17 @@ GEM
60 60 factory_girl_rails (1.0.1)
61 61 factory_girl (~> 1.3)
62 62 railties (>= 3.0.0)
  63 + faraday (0.6.1)
  64 + addressable (~> 2.2.4)
  65 + multipart-post (~> 1.1.0)
  66 + rack (>= 1.1.0, < 2)
  67 + faraday_middleware (0.6.5)
  68 + faraday (~> 0.6.0)
63 69 haml (3.0.25)
64 70 happymapper (0.3.2)
65 71 libxml-ruby (~> 1.1.3)
66 72 has_scope (0.5.1)
  73 + hashie (1.0.0)
67 74 hoptoad_notifier (2.4.11)
68 75 activesupport
69 76 builder
... ... @@ -96,7 +103,16 @@ GEM
96 103 bundler (>= 0.9.19)
97 104 rails (~> 3.0.0)
98 105 railties (~> 3.0.0)
  106 + multi_json (1.0.3)
  107 + multipart-post (1.1.3)
99 108 nokogiri (1.4.4)
  109 + octokit (0.6.3)
  110 + addressable (~> 2.2.4)
  111 + faraday (~> 0.6.0)
  112 + faraday_middleware (~> 0.6.0)
  113 + hashie (~> 1.0.0)
  114 + multi_json (~> 1.0.0)
  115 + rash (~> 0.3.0)
100 116 orm_adapter (0.0.5)
101 117 pivotal-tracker (0.2.0)
102 118 builder
... ... @@ -123,6 +139,8 @@ GEM
123 139 rake (>= 0.8.7)
124 140 thor (~> 0.14.4)
125 141 rake (0.8.7)
  142 + rash (0.3.0)
  143 + hashie (~> 1.0.0)
126 144 rbx-require-relative (0.0.5)
127 145 responders (0.6.4)
128 146 rest-client (1.5.1)
... ... @@ -193,6 +211,7 @@ DEPENDENCIES
193 211 mongoid (= 2.0.2)
194 212 mongoid_rails_migrations
195 213 nokogiri
  214 + octokit
196 215 pivotal-tracker
197 216 rails (= 3.0.5)
198 217 redmine_client!
... ...
app/models/issue_trackers/github_tracker.rb 0 → 100644
... ... @@ -0,0 +1,20 @@
  1 +class GithubTracker < IssueTracker
  2 + def self.label; "github"; end
  3 +
  4 + def check_params
  5 + if %w(project_id username api_token ).detect {|f| self[f].blank? }
  6 + errors.add :base, 'You must specify your Github repository, username and API token'
  7 + end
  8 + end
  9 +
  10 + def create_issue(err)
  11 + client = Octokit::Client.new(:login => username, :token => api_token)
  12 + issue = client.create_issue(project_id, issue_title(err), body_template.result(binding), options = {})
  13 + err.update_attribute :issue_link, issue.html_url
  14 + end
  15 +
  16 + def body_template
  17 + @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/github_body.txt.erb").gsub(/^\s*/, ''))
  18 + end
  19 +end
  20 +
... ...
app/views/apps/_issue_tracker_fields.html.haml
... ... @@ -6,6 +6,9 @@
6 6 = label_tag :type_none, :for => label_for_attr(w, 'type_issuetracker'), :class => "label_radio none" do
7 7 = w.radio_button :type, "IssueTracker", 'data-section' => 'none'
8 8 (None)
  9 + = label_tag :type_github, :for => label_for_attr(w, 'type_githubtracker'), :class => "label_radio github" do
  10 + = w.radio_button :type, "GithubTracker", 'data-section' => 'github'
  11 + Github Issues
9 12 = label_tag :type_lighthouseapp, :for => label_for_attr(w, 'type_lighthousetracker'), :class => "label_radio lighthouseapp" do
10 13 = w.radio_button :type, "LighthouseTracker", 'data-section' => 'lighthouse'
11 14 Lighthouse
... ... @@ -15,7 +18,6 @@
15 18 = label_tag :type_pivotal, :for => label_for_attr(w, 'type_pivotallabstracker'), :class => "label_radio pivotal" do
16 19 = w.radio_button :type, "PivotalLabsTracker", 'data-section' => 'pivotal'
17 20 Pivotal Tracker
18   - %br
19 21 = label_tag :type_fogbugz, :for => label_for_attr(w, 'type_fogbugztracker'), :class => "label_radio fogbugz" do
20 22 = w.radio_button :type, "FogbugzTracker", 'data-section' => 'fogbugz'
21 23 FogBugz
... ... @@ -25,6 +27,13 @@
25 27  
26 28 %div.tracker_params.none{:class => (w.object && !(w.object.class < IssueTracker)) ? 'chosen' : nil}
27 29 %p When no issue tracker has been configured, you will be able to leave comments on errors.
  30 + %div.tracker_params.github{:class => w.object.is_a?(GithubTracker) ? 'chosen' : nil}
  31 + = w.label :project_id, "Repository"
  32 + = w.text_field :project_id, :placeholder => "errbit/errbit from https://github.com/errbit/errbit"
  33 + = w.label :username, "Username"
  34 + = w.text_field :username, :placeholder => "Your username on Github"
  35 + = w.label :api_token, "Token"
  36 + = w.text_field :api_token, :placeholder => "Your API Token"
28 37 %div.tracker_params.lighthouse{:class => w.object.is_a?(LighthouseTracker) ? 'chosen' : nil}
29 38 = w.label :account, "Account"
30 39 = w.text_field :account, :placeholder => "abc from abc.lighthouseapp.com"
... ...
app/views/issue_trackers/github_body.txt.erb 0 → 100644
... ... @@ -0,0 +1,45 @@
  1 +[See this exception on Errbit](<%= app_err_url err.app, err %> "See this exception on Errbit")
  2 +<% if notice = err.notices.first %>
  3 +# <%= notice.message %> #
  4 +## Summary ##
  5 +<% if notice.request['url'].present? %>
  6 + ### URL ###
  7 + [<%= notice.request['url'] %>](<%= notice.request['url'] %>)"
  8 +<% end %>
  9 +### Where ###
  10 +<%= notice.err.where %>
  11 +
  12 +### Occured ###
  13 +<%= notice.created_at.to_s(:micro) %>
  14 +
  15 +### Similar ###
  16 +<%= (notice.err.notices_count - 1).to_s %>
  17 +
  18 +## Params ##
  19 +```
  20 +<%= pretty_hash(notice.params) %>
  21 +```
  22 +
  23 +## Session ##
  24 +```
  25 +<%= pretty_hash(notice.session) %>
  26 +```
  27 +
  28 +## Backtrace ##
  29 +```
  30 +<% for line in notice.backtrace %><%= line['number'] %>: <%= line['file'].sub(/^\[PROJECT_ROOT\]/, '') %> -> **<%= line['method'] %>**
  31 +<% end %>
  32 +```
  33 +
  34 +## Environment ##
  35 +
  36 +<table>
  37 +<% for key, val in notice.env_vars %>
  38 + <tr>
  39 + <td><%= key %>:</td>
  40 + <td><%= val %></td>
  41 + </tr>
  42 +<% end %>
  43 +</table>
  44 +<% end %>
  45 +
... ...
public/images/github_create.png 0 → 100644

2.3 KB

public/images/github_goto.png 0 → 100644

2.3 KB

public/images/github_inactive.png 0 → 100644

2.03 KB

public/stylesheets/application.css
... ... @@ -540,7 +540,7 @@ div.issue_tracker.nested img {
540 540  
541 541 /* Icons for Issue Tracker Radio Buttons */
542 542 div.issue_tracker.nested label.label_radio {
543   - color: #777777;
  543 + color: #929292;
544 544 padding-left: 33px;
545 545 margin-bottom: 6px;
546 546 margin-right: 8px;
... ... @@ -551,7 +551,7 @@ div.issue_tracker.nested .label_radio input {
551 551 }
552 552  
553 553 div.issue_tracker.nested label.r_on {
554   - color: #262626;
  554 + color: #191919;
555 555 }
556 556  
557 557  
... ... @@ -562,6 +562,7 @@ div.issue_tracker.nested label.lighthouseapp { background: url(/images/lighthous
562 562 div.issue_tracker.nested label.mingle { background: url(/images/mingle_inactive.png) no-repeat; }
563 563 div.issue_tracker.nested label.fogbugz { background: url(/images/fogbugz_inactive.png) no-repeat; }
564 564 div.issue_tracker.nested label.pivotal { background: url(/images/pivotal_inactive.png) no-repeat; }
  565 +div.issue_tracker.nested label.github { background: url(/images/github_inactive.png) no-repeat; }
565 566 /* Active icons */
566 567 div.issue_tracker.nested label.r_on.none { background: url(/images/none_create.png) no-repeat; }
567 568 div.issue_tracker.nested label.r_on.redmine { background: url(/images/redmine_create.png) no-repeat; }
... ... @@ -569,7 +570,7 @@ div.issue_tracker.nested label.r_on.lighthouseapp { background: url(/images/ligh
569 570 div.issue_tracker.nested label.r_on.mingle { background: url(/images/mingle_create.png) no-repeat; }
570 571 div.issue_tracker.nested label.r_on.fogbugz { background: url(/images/fogbugz_create.png) no-repeat; }
571 572 div.issue_tracker.nested label.r_on.pivotal { background: url(/images/pivotal_create.png) no-repeat; }
572   -
  573 +div.issue_tracker.nested label.r_on.github { background: url(/images/github_create.png) no-repeat; }
573 574  
574 575 /* Apps Table */
575 576 table.apps tbody tr:hover td ,table.errs tbody tr:hover td { background-color: #F2F2F2;}
... ... @@ -677,6 +678,10 @@ table.tally th.value {
677 678 background: transparent url(/images/mingle_create.png) 6px 5px no-repeat;
678 679 }
679 680  
  681 +#action-bar a.github_create {
  682 + background: transparent url(/images/github_create.png) 6px 5px no-repeat;
  683 +}
  684 +
680 685 #action-bar a.lighthouseapp_goto {
681 686 background: transparent url(/images/lighthouseapp_goto.png) 6px 5px no-repeat;
682 687 }
... ... @@ -697,6 +702,10 @@ table.tally th.value {
697 702 background: transparent url(/images/mingle_goto.png) 6px 5px no-repeat;
698 703 }
699 704  
  705 +#action-bar a.github_goto {
  706 + background: transparent url(/images/github_goto.png) 6px 5px no-repeat;
  707 +}
  708 +
700 709 /* Notices Pagination */
701 710 .notice-pagination {
702 711 float: left;
... ...
spec/controllers/apps_controller_spec.rb
... ... @@ -402,6 +402,33 @@ describe AppsController do
402 402 end
403 403 end
404 404  
  405 + context "github issues" do
  406 + context 'with correct params' do
  407 + before do
  408 + put :update, :id => @app.id, :app => { :issue_tracker_attributes => {
  409 + :type => 'GithubTracker', :project_id => 'test', :username => 'user',
  410 + :api_token => '123123'
  411 + } }
  412 + @app.reload
  413 + end
  414 +
  415 + subject {@app.issue_tracker}
  416 + its(:type) {should == "GithubTracker"}
  417 + its(:project_id) {should == 'test'}
  418 + its(:username) {should == 'user'}
  419 + its(:api_token) {should == '123123'}
  420 + end
  421 +
  422 + it "should show validation notice when sufficient params are not present" do
  423 + put :update, :id => @app.id, :app => { :issue_tracker_attributes => {
  424 + :type => 'GithubTracker', :project_id => 'test', :username => 'user'
  425 + } }
  426 + @app.reload
  427 +
  428 + @app.issue_tracker_configured?.should == false
  429 + response.body.should match(/You must specify your Github repository, username and API token/)
  430 + end
  431 + end
405 432  
406 433 end
407 434 end
... ... @@ -435,3 +462,4 @@ describe AppsController do
435 462 end
436 463  
437 464 end
  465 +
... ...
spec/controllers/errs_controller_spec.rb
... ... @@ -395,6 +395,53 @@ describe ErrsController do
395 395 err.issue_link.should == @issue_link.sub(/\.xml$/, '')
396 396 end
397 397 end
  398 +
  399 + context "github issues tracker" do
  400 + let(:notice) { Factory :notice }
  401 + let(:tracker) { Factory :github_tracker, :app => notice.err.app }
  402 + let(:err) { notice.err }
  403 +
  404 + before(:each) do
  405 + number = 5
  406 + @issue_link = "https://github.com/#{tracker.project_id}/issues/#{number}"
  407 + body = <<EOF
  408 +{
  409 + "issue": {
  410 + "position": 1.0,
  411 + "number": #{number},
  412 + "votes": 0,
  413 + "created_at": "2010/01/21 13:45:59 -0800",
  414 + "comments": 0,
  415 + "body": "Test Body",
  416 + "title": "Test Issue",
  417 + "user": "test_user",
  418 + "state": "open",
  419 + "html_url": "#{@issue_link}"
  420 + }
  421 +}
  422 +EOF
  423 + stub_request(:post, "https://#{tracker.username}%2Ftoken:#{tracker.api_token}@github.com/api/v2/json/issues/open/#{tracker.project_id}").
  424 + to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body )
  425 +
  426 + post :create_issue, :app_id => err.app.id, :id => err.id
  427 + err.reload
  428 + end
  429 +
  430 + it "should make request to Github with err params" do
  431 + requested = have_requested(:post, "https://#{tracker.username}%2Ftoken:#{tracker.api_token}@github.com/api/v2/json/issues/open/#{tracker.project_id}")
  432 + WebMock.should requested.with(:headers => {'Content-Type' => 'application/x-www-form-urlencoded'})
  433 + WebMock.should requested.with(:body => /title=%5Bproduction%5D%5Bfoo%23bar%5D%20FooError%3A%20Too%20Much%20Bar/)
  434 + WebMock.should requested.with(:body => /See%20this%20exception%20on%20Errbit/)
  435 + end
  436 +
  437 + it "should redirect to err page" do
  438 + response.should redirect_to( app_err_path(err.app, err) )
  439 + end
  440 +
  441 + it "should create issue link for err" do
  442 + err.issue_link.should == @issue_link
  443 + end
  444 + end
398 445 end
399 446  
400 447 context "absent issue tracker" do
... ...
spec/factories/issue_tracker_factories.rb
... ... @@ -20,3 +20,9 @@ Factory.define :mingle_tracker, :parent =&gt; :issue_tracker, :class =&gt; :mingle_tra
20 20 e.ticket_properties 'card_type = Defect, defect_status = open, priority = essential'
21 21 end
22 22  
  23 +Factory.define :github_tracker, :parent => :issue_tracker, :class => :github_tracker do |e|
  24 + e.project_id 'test_account/test_project'
  25 + e.username 'test_username'
  26 + e.api_token '12497asfa987'
  27 +end
  28 +
... ...