From e8cd05155a74a59076aa859281cc5bd16b81f0ef Mon Sep 17 00:00:00 2001 From: Nathan Broadbent Date: Thu, 18 Aug 2011 09:02:43 +0800 Subject: [PATCH] Added Github Issues Tracker. --- Gemfile | 1 + Gemfile.lock | 19 +++++++++++++++++++ app/models/issue_trackers/github_tracker.rb | 20 ++++++++++++++++++++ app/views/apps/_issue_tracker_fields.html.haml | 11 ++++++++++- app/views/issue_trackers/github_body.txt.erb | 45 +++++++++++++++++++++++++++++++++++++++++++++ public/images/github_create.png | Bin 0 -> 2353 bytes public/images/github_goto.png | Bin 0 -> 2353 bytes public/images/github_inactive.png | Bin 0 -> 2080 bytes public/stylesheets/application.css | 15 ++++++++++++--- spec/controllers/apps_controller_spec.rb | 28 ++++++++++++++++++++++++++++ spec/controllers/errs_controller_spec.rb | 47 +++++++++++++++++++++++++++++++++++++++++++++++ spec/factories/issue_tracker_factories.rb | 6 ++++++ 12 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 app/models/issue_trackers/github_tracker.rb create mode 100644 app/views/issue_trackers/github_body.txt.erb create mode 100644 public/images/github_create.png create mode 100644 public/images/github_goto.png create mode 100644 public/images/github_inactive.png diff --git a/Gemfile b/Gemfile index 6ea90c1..711e058 100644 --- a/Gemfile +++ b/Gemfile @@ -12,6 +12,7 @@ gem 'mongoid_rails_migrations' gem 'useragent', '~> 0.3.1' gem 'pivotal-tracker' gem 'ruby-fogbugz', :require => 'fogbugz' +gem 'octokit' gem 'inherited_resources' group :production do diff --git a/Gemfile.lock b/Gemfile.lock index 5fec1a4..e5ea8e1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -60,10 +60,17 @@ GEM factory_girl_rails (1.0.1) factory_girl (~> 1.3) railties (>= 3.0.0) + faraday (0.6.1) + addressable (~> 2.2.4) + multipart-post (~> 1.1.0) + rack (>= 1.1.0, < 2) + faraday_middleware (0.6.5) + faraday (~> 0.6.0) haml (3.0.25) happymapper (0.3.2) libxml-ruby (~> 1.1.3) has_scope (0.5.1) + hashie (1.0.0) hoptoad_notifier (2.4.11) activesupport builder @@ -96,7 +103,16 @@ GEM bundler (>= 0.9.19) rails (~> 3.0.0) railties (~> 3.0.0) + multi_json (1.0.3) + multipart-post (1.1.3) nokogiri (1.4.4) + octokit (0.6.3) + addressable (~> 2.2.4) + faraday (~> 0.6.0) + faraday_middleware (~> 0.6.0) + hashie (~> 1.0.0) + multi_json (~> 1.0.0) + rash (~> 0.3.0) orm_adapter (0.0.5) pivotal-tracker (0.2.0) builder @@ -123,6 +139,8 @@ GEM rake (>= 0.8.7) thor (~> 0.14.4) rake (0.8.7) + rash (0.3.0) + hashie (~> 1.0.0) rbx-require-relative (0.0.5) responders (0.6.4) rest-client (1.5.1) @@ -193,6 +211,7 @@ DEPENDENCIES mongoid (= 2.0.2) mongoid_rails_migrations nokogiri + octokit pivotal-tracker rails (= 3.0.5) redmine_client! diff --git a/app/models/issue_trackers/github_tracker.rb b/app/models/issue_trackers/github_tracker.rb new file mode 100644 index 0000000..2484ba2 --- /dev/null +++ b/app/models/issue_trackers/github_tracker.rb @@ -0,0 +1,20 @@ +class GithubTracker < IssueTracker + def self.label; "github"; end + + def check_params + if %w(project_id username api_token ).detect {|f| self[f].blank? } + errors.add :base, 'You must specify your Github repository, username and API token' + end + end + + def create_issue(err) + client = Octokit::Client.new(:login => username, :token => api_token) + issue = client.create_issue(project_id, issue_title(err), body_template.result(binding), options = {}) + err.update_attribute :issue_link, issue.html_url + end + + def body_template + @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/github_body.txt.erb").gsub(/^\s*/, '')) + end +end + diff --git a/app/views/apps/_issue_tracker_fields.html.haml b/app/views/apps/_issue_tracker_fields.html.haml index 6fad992..4aee4fd 100644 --- a/app/views/apps/_issue_tracker_fields.html.haml +++ b/app/views/apps/_issue_tracker_fields.html.haml @@ -6,6 +6,9 @@ = label_tag :type_none, :for => label_for_attr(w, 'type_issuetracker'), :class => "label_radio none" do = w.radio_button :type, "IssueTracker", 'data-section' => 'none' (None) + = label_tag :type_github, :for => label_for_attr(w, 'type_githubtracker'), :class => "label_radio github" do + = w.radio_button :type, "GithubTracker", 'data-section' => 'github' + Github Issues = label_tag :type_lighthouseapp, :for => label_for_attr(w, 'type_lighthousetracker'), :class => "label_radio lighthouseapp" do = w.radio_button :type, "LighthouseTracker", 'data-section' => 'lighthouse' Lighthouse @@ -15,7 +18,6 @@ = label_tag :type_pivotal, :for => label_for_attr(w, 'type_pivotallabstracker'), :class => "label_radio pivotal" do = w.radio_button :type, "PivotalLabsTracker", 'data-section' => 'pivotal' Pivotal Tracker - %br = label_tag :type_fogbugz, :for => label_for_attr(w, 'type_fogbugztracker'), :class => "label_radio fogbugz" do = w.radio_button :type, "FogbugzTracker", 'data-section' => 'fogbugz' FogBugz @@ -25,6 +27,13 @@ %div.tracker_params.none{:class => (w.object && !(w.object.class < IssueTracker)) ? 'chosen' : nil} %p When no issue tracker has been configured, you will be able to leave comments on errors. + %div.tracker_params.github{:class => w.object.is_a?(GithubTracker) ? 'chosen' : nil} + = w.label :project_id, "Repository" + = w.text_field :project_id, :placeholder => "errbit/errbit from https://github.com/errbit/errbit" + = w.label :username, "Username" + = w.text_field :username, :placeholder => "Your username on Github" + = w.label :api_token, "Token" + = w.text_field :api_token, :placeholder => "Your API Token" %div.tracker_params.lighthouse{:class => w.object.is_a?(LighthouseTracker) ? 'chosen' : nil} = w.label :account, "Account" = w.text_field :account, :placeholder => "abc from abc.lighthouseapp.com" diff --git a/app/views/issue_trackers/github_body.txt.erb b/app/views/issue_trackers/github_body.txt.erb new file mode 100644 index 0000000..8b3297f --- /dev/null +++ b/app/views/issue_trackers/github_body.txt.erb @@ -0,0 +1,45 @@ +[See this exception on Errbit](<%= app_err_url err.app, err %> "See this exception on Errbit") +<% if notice = err.notices.first %> +# <%= notice.message %> # +## Summary ## +<% if notice.request['url'].present? %> + ### URL ### + [<%= notice.request['url'] %>](<%= notice.request['url'] %>)" +<% end %> +### Where ### +<%= notice.err.where %> + +### Occured ### +<%= notice.created_at.to_s(:micro) %> + +### Similar ### +<%= (notice.err.notices_count - 1).to_s %> + +## Params ## +``` +<%= pretty_hash(notice.params) %> +``` + +## Session ## +``` +<%= pretty_hash(notice.session) %> +``` + +## Backtrace ## +``` +<% for line in notice.backtrace %><%= line['number'] %>: <%= line['file'].sub(/^\[PROJECT_ROOT\]/, '') %> -> **<%= line['method'] %>** +<% end %> +``` + +## Environment ## + + +<% for key, val in notice.env_vars %> + + + + +<% end %> +
<%= key %>:<%= val %>
+<% end %> + diff --git a/public/images/github_create.png b/public/images/github_create.png new file mode 100644 index 0000000..a8a8cd7 Binary files /dev/null and b/public/images/github_create.png differ diff --git a/public/images/github_goto.png b/public/images/github_goto.png new file mode 100644 index 0000000..a8a8cd7 Binary files /dev/null and b/public/images/github_goto.png differ diff --git a/public/images/github_inactive.png b/public/images/github_inactive.png new file mode 100644 index 0000000..089891b Binary files /dev/null and b/public/images/github_inactive.png differ diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css index a0ce0b5..348c2f4 100644 --- a/public/stylesheets/application.css +++ b/public/stylesheets/application.css @@ -540,7 +540,7 @@ div.issue_tracker.nested img { /* Icons for Issue Tracker Radio Buttons */ div.issue_tracker.nested label.label_radio { - color: #777777; + color: #929292; padding-left: 33px; margin-bottom: 6px; margin-right: 8px; @@ -551,7 +551,7 @@ div.issue_tracker.nested .label_radio input { } div.issue_tracker.nested label.r_on { - color: #262626; + color: #191919; } @@ -562,6 +562,7 @@ div.issue_tracker.nested label.lighthouseapp { background: url(/images/lighthous div.issue_tracker.nested label.mingle { background: url(/images/mingle_inactive.png) no-repeat; } div.issue_tracker.nested label.fogbugz { background: url(/images/fogbugz_inactive.png) no-repeat; } div.issue_tracker.nested label.pivotal { background: url(/images/pivotal_inactive.png) no-repeat; } +div.issue_tracker.nested label.github { background: url(/images/github_inactive.png) no-repeat; } /* Active icons */ div.issue_tracker.nested label.r_on.none { background: url(/images/none_create.png) no-repeat; } 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 div.issue_tracker.nested label.r_on.mingle { background: url(/images/mingle_create.png) no-repeat; } div.issue_tracker.nested label.r_on.fogbugz { background: url(/images/fogbugz_create.png) no-repeat; } div.issue_tracker.nested label.r_on.pivotal { background: url(/images/pivotal_create.png) no-repeat; } - +div.issue_tracker.nested label.r_on.github { background: url(/images/github_create.png) no-repeat; } /* Apps Table */ table.apps tbody tr:hover td ,table.errs tbody tr:hover td { background-color: #F2F2F2;} @@ -677,6 +678,10 @@ table.tally th.value { background: transparent url(/images/mingle_create.png) 6px 5px no-repeat; } +#action-bar a.github_create { + background: transparent url(/images/github_create.png) 6px 5px no-repeat; +} + #action-bar a.lighthouseapp_goto { background: transparent url(/images/lighthouseapp_goto.png) 6px 5px no-repeat; } @@ -697,6 +702,10 @@ table.tally th.value { background: transparent url(/images/mingle_goto.png) 6px 5px no-repeat; } +#action-bar a.github_goto { + background: transparent url(/images/github_goto.png) 6px 5px no-repeat; +} + /* Notices Pagination */ .notice-pagination { float: left; diff --git a/spec/controllers/apps_controller_spec.rb b/spec/controllers/apps_controller_spec.rb index 0ba2405..5eb6e1c 100644 --- a/spec/controllers/apps_controller_spec.rb +++ b/spec/controllers/apps_controller_spec.rb @@ -402,6 +402,33 @@ describe AppsController do end end + context "github issues" do + context 'with correct params' do + before do + put :update, :id => @app.id, :app => { :issue_tracker_attributes => { + :type => 'GithubTracker', :project_id => 'test', :username => 'user', + :api_token => '123123' + } } + @app.reload + end + + subject {@app.issue_tracker} + its(:type) {should == "GithubTracker"} + its(:project_id) {should == 'test'} + its(:username) {should == 'user'} + its(:api_token) {should == '123123'} + end + + it "should show validation notice when sufficient params are not present" do + put :update, :id => @app.id, :app => { :issue_tracker_attributes => { + :type => 'GithubTracker', :project_id => 'test', :username => 'user' + } } + @app.reload + + @app.issue_tracker_configured?.should == false + response.body.should match(/You must specify your Github repository, username and API token/) + end + end end end @@ -435,3 +462,4 @@ describe AppsController do end end + diff --git a/spec/controllers/errs_controller_spec.rb b/spec/controllers/errs_controller_spec.rb index 5d9a05f..e228931 100644 --- a/spec/controllers/errs_controller_spec.rb +++ b/spec/controllers/errs_controller_spec.rb @@ -395,6 +395,53 @@ describe ErrsController do err.issue_link.should == @issue_link.sub(/\.xml$/, '') end end + + context "github issues tracker" do + let(:notice) { Factory :notice } + let(:tracker) { Factory :github_tracker, :app => notice.err.app } + let(:err) { notice.err } + + before(:each) do + number = 5 + @issue_link = "https://github.com/#{tracker.project_id}/issues/#{number}" + body = < 201, :headers => {'Location' => @issue_link}, :body => body ) + + post :create_issue, :app_id => err.app.id, :id => err.id + err.reload + end + + it "should make request to Github with err params" do + requested = have_requested(:post, "https://#{tracker.username}%2Ftoken:#{tracker.api_token}@github.com/api/v2/json/issues/open/#{tracker.project_id}") + WebMock.should requested.with(:headers => {'Content-Type' => 'application/x-www-form-urlencoded'}) + WebMock.should requested.with(:body => /title=%5Bproduction%5D%5Bfoo%23bar%5D%20FooError%3A%20Too%20Much%20Bar/) + WebMock.should requested.with(:body => /See%20this%20exception%20on%20Errbit/) + end + + it "should redirect to err page" do + response.should redirect_to( app_err_path(err.app, err) ) + end + + it "should create issue link for err" do + err.issue_link.should == @issue_link + end + end end context "absent issue tracker" do diff --git a/spec/factories/issue_tracker_factories.rb b/spec/factories/issue_tracker_factories.rb index 68eb7e8..abc2938 100644 --- a/spec/factories/issue_tracker_factories.rb +++ b/spec/factories/issue_tracker_factories.rb @@ -20,3 +20,9 @@ Factory.define :mingle_tracker, :parent => :issue_tracker, :class => :mingle_tra e.ticket_properties 'card_type = Defect, defect_status = open, priority = essential' end +Factory.define :github_tracker, :parent => :issue_tracker, :class => :github_tracker do |e| + e.project_id 'test_account/test_project' + e.username 'test_username' + e.api_token '12497asfa987' +end + -- libgit2 0.21.2