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 %>
+
+ <%= key %>: |
+ <%= val %> |
+
+<% end %>
+
+<% 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