Commit e8cd05155a74a59076aa859281cc5bd16b81f0ef
1 parent
32964b43
Exists in
master
and in
1 other branch
Added Github Issues Tracker.
Showing
12 changed files
with
188 additions
and
4 deletions
Show diff stats
Gemfile
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! | ... | ... |
... | ... | @@ -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" | ... | ... |
... | ... | @@ -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 | + | ... | ... |
2.3 KB
2.3 KB
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 => :issue_tracker, :class => :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 | + | ... | ... |