redmine_airbrake_backend 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +92 -0
- data/MIT-LICENSE +22 -0
- data/README.md +62 -0
- data/Rakefile +34 -0
- data/app/controllers/airbrake_controller.rb +182 -0
- data/app/controllers/airbrake_project_settings_controller.rb +25 -0
- data/app/helpers/airbrake_helper.rb +48 -0
- data/app/models/airbrake_project_setting.rb +15 -0
- data/app/views/airbrake/_issue_description.erb +38 -0
- data/app/views/projects/settings/_airbrake.html.erb +32 -0
- data/app/views/settings/_airbrake.html.erb +9 -0
- data/bin/rails +8 -0
- data/config/locales/en.yml +10 -0
- data/config/routes.rb +9 -0
- data/db/migrate/20130708102357_create_airbrake_project_settings.rb +18 -0
- data/lib/redmine_airbrake_backend.rb +4 -0
- data/lib/redmine_airbrake_backend/engine.rb +28 -0
- data/lib/redmine_airbrake_backend/project_helper_patch.rb +16 -0
- data/lib/redmine_airbrake_backend/version.rb +3 -0
- data/redmine_airbrake_backend.gemspec +27 -0
- metadata +137 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7d8a9f1faf769ff932d95c7c834ee697dac4c686
|
4
|
+
data.tar.gz: 1b6e0a26f4e5587afa7be9fa38bedf892e79c784
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1abf3729e9b38f7d0eb6d195d642632513ba8457cd84b0aa710d10d62725be77af8c25fd272341901033c62d5d5acbf5e8923da33d3ce6862f5c06de744ead16
|
7
|
+
data.tar.gz: 0ced6e811c22c9e5ed5587dad4c359f120edb1df50745c94916da5495fe02cb6acba2353ecb86d536d2fca50024528bf723e5567676fd92aed659ae678f12627
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
redmine_airbrake_backend (0.1.0)
|
5
|
+
hpricot
|
6
|
+
htmlentities
|
7
|
+
rails
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
actionmailer (4.0.0)
|
13
|
+
actionpack (= 4.0.0)
|
14
|
+
mail (~> 2.5.3)
|
15
|
+
actionpack (4.0.0)
|
16
|
+
activesupport (= 4.0.0)
|
17
|
+
builder (~> 3.1.0)
|
18
|
+
erubis (~> 2.7.0)
|
19
|
+
rack (~> 1.5.2)
|
20
|
+
rack-test (~> 0.6.2)
|
21
|
+
activemodel (4.0.0)
|
22
|
+
activesupport (= 4.0.0)
|
23
|
+
builder (~> 3.1.0)
|
24
|
+
activerecord (4.0.0)
|
25
|
+
activemodel (= 4.0.0)
|
26
|
+
activerecord-deprecated_finders (~> 1.0.2)
|
27
|
+
activesupport (= 4.0.0)
|
28
|
+
arel (~> 4.0.0)
|
29
|
+
activerecord-deprecated_finders (1.0.3)
|
30
|
+
activesupport (4.0.0)
|
31
|
+
i18n (~> 0.6, >= 0.6.4)
|
32
|
+
minitest (~> 4.2)
|
33
|
+
multi_json (~> 1.3)
|
34
|
+
thread_safe (~> 0.1)
|
35
|
+
tzinfo (~> 0.3.37)
|
36
|
+
arel (4.0.0)
|
37
|
+
atomic (1.1.10)
|
38
|
+
builder (3.1.4)
|
39
|
+
erubis (2.7.0)
|
40
|
+
hike (1.2.3)
|
41
|
+
hpricot (0.8.6)
|
42
|
+
htmlentities (4.3.1)
|
43
|
+
i18n (0.6.4)
|
44
|
+
mail (2.5.4)
|
45
|
+
mime-types (~> 1.16)
|
46
|
+
treetop (~> 1.4.8)
|
47
|
+
mime-types (1.23)
|
48
|
+
minitest (4.7.5)
|
49
|
+
multi_json (1.7.7)
|
50
|
+
polyglot (0.3.3)
|
51
|
+
rack (1.5.2)
|
52
|
+
rack-test (0.6.2)
|
53
|
+
rack (>= 1.0)
|
54
|
+
rails (4.0.0)
|
55
|
+
actionmailer (= 4.0.0)
|
56
|
+
actionpack (= 4.0.0)
|
57
|
+
activerecord (= 4.0.0)
|
58
|
+
activesupport (= 4.0.0)
|
59
|
+
bundler (>= 1.3.0, < 2.0)
|
60
|
+
railties (= 4.0.0)
|
61
|
+
sprockets-rails (~> 2.0.0)
|
62
|
+
railties (4.0.0)
|
63
|
+
actionpack (= 4.0.0)
|
64
|
+
activesupport (= 4.0.0)
|
65
|
+
rake (>= 0.8.7)
|
66
|
+
thor (>= 0.18.1, < 2.0)
|
67
|
+
rake (10.1.0)
|
68
|
+
sprockets (2.10.0)
|
69
|
+
hike (~> 1.2)
|
70
|
+
multi_json (~> 1.0)
|
71
|
+
rack (~> 1.0)
|
72
|
+
tilt (~> 1.1, != 1.3.0)
|
73
|
+
sprockets-rails (2.0.0)
|
74
|
+
actionpack (>= 3.0)
|
75
|
+
activesupport (>= 3.0)
|
76
|
+
sprockets (~> 2.8)
|
77
|
+
thor (0.18.1)
|
78
|
+
thread_safe (0.1.0)
|
79
|
+
atomic
|
80
|
+
tilt (1.4.1)
|
81
|
+
treetop (1.4.14)
|
82
|
+
polyglot
|
83
|
+
polyglot (>= 0.3.1)
|
84
|
+
tzinfo (0.3.37)
|
85
|
+
|
86
|
+
PLATFORMS
|
87
|
+
ruby
|
88
|
+
|
89
|
+
DEPENDENCIES
|
90
|
+
bundler (~> 1.3)
|
91
|
+
rake
|
92
|
+
redmine_airbrake_backend!
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Florian Schwab
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# Redmine Airbrake Backend
|
2
|
+
|
3
|
+
This plugin provides the necessary API to use Redmine as an Airbrake backend.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Apply this [patch](http://www.redmine.org/issues/14402) to Redmine.
|
8
|
+
|
9
|
+
Add this line to your Redmine Gemfile:
|
10
|
+
|
11
|
+
gem 'redmine_airbrake_backend'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle install
|
16
|
+
$ rake redmine:plugins:migrate
|
17
|
+
|
18
|
+
## Integration
|
19
|
+
|
20
|
+
1. Create the following custom fields for issues:
|
21
|
+
* Airbrake Hash (String)
|
22
|
+
* Number of Occurrences (Integer) (optional)
|
23
|
+
2. Configure the plugin to use these 2 custom fields (Administration -> Plugins -> Configure)
|
24
|
+
3. Enable the project module (Airbrake) in your project settings
|
25
|
+
4. Configure additionale defaults under the settings tab (Airbrake)
|
26
|
+
|
27
|
+
## Client configuration
|
28
|
+
|
29
|
+
For a Rails application, just setup the [Airbrake notifier](https://github.com/airbrake/airbrake) as usual, then modify `config/initializers/airbrake.rb` according to your needs using this template:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
Airbrake.configure do |config|
|
33
|
+
config.api_key = {
|
34
|
+
project: 'redmine_project_identifier', # the identifier you specified for your project in Redmine
|
35
|
+
api_key: 'redmine_api_key', # the api key for a user which has permission to create issues in the project specified in the previous step
|
36
|
+
tracker: 'Bug', # the name or id of your desired tracker (optional if default is configured)
|
37
|
+
category: 'Development', # the name or id of a ticket category, optional
|
38
|
+
priority: 5 # the name or id of the priority for new tickets, optional.
|
39
|
+
assignee: 'admin', # the login or id of a user the ticket should get assigned to by default, optional
|
40
|
+
}.to_json
|
41
|
+
config.host = 'my_redmine_host.com' # the hostname your Redmine runs at
|
42
|
+
config.port = 443 # the port your Redmine runs at
|
43
|
+
config.secure = true # sends data to your server using SSL, optional
|
44
|
+
end
|
45
|
+
```
|
46
|
+
|
47
|
+
## Notes
|
48
|
+
|
49
|
+
Based and inspired by https://github.com/milgner/redmine_airbrake_server
|
50
|
+
|
51
|
+
## Code Status
|
52
|
+
|
53
|
+
* [![Dependencies](https://gemnasium.com/ydkn/redmine_airbrake_backend.png?travis)](https://gemnasium.com/ydkn/redmine_airbrake_backend)
|
54
|
+
* [![Code Climate](https://codeclimate.com/github/ydkn/redmine_airbrake_backend.png)](https://codeclimate.com/github/ydkn/redmine_airbrake_backend)
|
55
|
+
|
56
|
+
## Contributing
|
57
|
+
|
58
|
+
1. Fork it
|
59
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
60
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
61
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
62
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'RedmineAirbrakeBackend'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
Bundler::GemHelper.install_tasks
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.libs << 'lib'
|
28
|
+
t.libs << 'test'
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
30
|
+
t.verbose = false
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
task default: :test
|
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'hpricot'
|
3
|
+
require 'htmlentities'
|
4
|
+
|
5
|
+
class AirbrakeController < ::ApplicationController
|
6
|
+
SUPPORTED_API_VERSIONS = %w(2.4)
|
7
|
+
|
8
|
+
prepend_before_filter :set_api_auth
|
9
|
+
before_filter :parse_notice
|
10
|
+
before_filter :init_vars
|
11
|
+
|
12
|
+
accept_api_auth :notice
|
13
|
+
|
14
|
+
def notice
|
15
|
+
return unless authorize(:issues, :create)
|
16
|
+
|
17
|
+
raise ArgumentError.new("Airbrake version not supported") unless SUPPORTED_API_VERSIONS.include?(@notice[:version])
|
18
|
+
raise ArgumentError.new("Project not found") unless @project
|
19
|
+
raise ArgumentError.new("Tracker not found") unless @tracker
|
20
|
+
raise ArgumentError.new("Tracker does not have a notice hash custom field") unless @tracker.custom_fields.where(id: notice_hash_field.id).first
|
21
|
+
|
22
|
+
# Issue by project, tracker and hash
|
23
|
+
issue_ids = CustomValue.where(customized_type: Issue.name, custom_field_id: notice_hash_field.id, value: notice_hash).select([:customized_id]).collect{|cv| cv.customized_id}
|
24
|
+
@issue = Issue.where(id: issue_ids, project_id: @project.id, tracker_id: @tracker.id).first
|
25
|
+
@issue = Issue.new(
|
26
|
+
subject: subject,
|
27
|
+
project: @project,
|
28
|
+
tracker: @tracker,
|
29
|
+
author: User.current,
|
30
|
+
category: @category,
|
31
|
+
priority: @priority,
|
32
|
+
description: render_to_string(partial: 'issue_description'),
|
33
|
+
assigned_to: @assignee
|
34
|
+
) unless @issue
|
35
|
+
|
36
|
+
custom_field_values = {}
|
37
|
+
|
38
|
+
# Update occurrences
|
39
|
+
occurrences_value = @issue.custom_value_for(occurrences_field.id)
|
40
|
+
custom_field_values[occurrences_field.id] = ((occurrences_value ? occurrences_value.value.to_i : 0) + 1).to_s if occurrences_field.present?
|
41
|
+
|
42
|
+
# Reopen if closed
|
43
|
+
if reopen? && @issue.status.is_closed?
|
44
|
+
@issue.status = IssueStatus.where(is_default: true).order(:position).first
|
45
|
+
@issue.init_journal(User.current, "Issue reopened after occurring again in environment #{@notice[:server_environment][:environment_name]}")
|
46
|
+
end
|
47
|
+
|
48
|
+
# Hash
|
49
|
+
custom_field_values[notice_hash_field.id] = notice_hash if @issue.new_record?
|
50
|
+
|
51
|
+
@issue.custom_field_values = custom_field_values
|
52
|
+
|
53
|
+
if @issue.save
|
54
|
+
render xml: {
|
55
|
+
notice: {
|
56
|
+
id: notice_hash,
|
57
|
+
url: issue_url(@issue)
|
58
|
+
}
|
59
|
+
}
|
60
|
+
else
|
61
|
+
render nothing: true, status: :internal_server_error
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def set_api_auth
|
68
|
+
params[:key] = redmine_params[:api_key] rescue nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def redmine_params
|
72
|
+
JSON.parse(params[:notice][:api_key]).symbolize_keys
|
73
|
+
end
|
74
|
+
helper_method :redmine_params
|
75
|
+
|
76
|
+
def subject
|
77
|
+
if @notice[:error][:message].starts_with?("#{@notice[:error][:class]}:")
|
78
|
+
"[#{notice_hash[0..7]}] #{@notice[:error][:message]}"[0..254]
|
79
|
+
else
|
80
|
+
"[#{notice_hash[0..7]}] #{@notice[:error][:class]} #{@notice[:error][:message]}"[0..254]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def backtrace
|
85
|
+
if @notice[:error][:backtrace][:line].is_a?(Hash)
|
86
|
+
[@notice[:error][:backtrace][:line]]
|
87
|
+
else
|
88
|
+
@notice[:error][:backtrace][:line]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
helper_method :backtrace
|
92
|
+
|
93
|
+
def notice_hash
|
94
|
+
h = []
|
95
|
+
h << @notice[:error][:class]
|
96
|
+
h << @notice[:error][:message]
|
97
|
+
h += backtrace.collect{|element| "#{element[:file]}|#{element[:method].gsub(/_\d+_/, '')}|#{element[:number]}"}
|
98
|
+
|
99
|
+
Digest::MD5.hexdigest(h.compact.join("\n"))
|
100
|
+
end
|
101
|
+
|
102
|
+
def notice_hash_field
|
103
|
+
custom_field(:hash_field)
|
104
|
+
end
|
105
|
+
|
106
|
+
def occurrences_field
|
107
|
+
custom_field(:occurrences_field)
|
108
|
+
end
|
109
|
+
|
110
|
+
def custom_field(key)
|
111
|
+
@project.issue_custom_fields.where(id: setting(key)).first || CustomField.where(id: setting(key), is_for_all: true).first
|
112
|
+
end
|
113
|
+
|
114
|
+
def reopen?
|
115
|
+
return false if project_setting(:reopen_regexp).blank?
|
116
|
+
!!(@notice[:server_environment][:environment_name] =~ /#{project_setting(:reopen_regexp)}/i)
|
117
|
+
end
|
118
|
+
|
119
|
+
def setting(key)
|
120
|
+
Setting.plugin_redmine_airbrake_backend[key]
|
121
|
+
end
|
122
|
+
|
123
|
+
def project_setting(key)
|
124
|
+
return nil if @project.airbrake_settings.blank?
|
125
|
+
@project.airbrake_settings.send(key) if @project.airbrake_settings.respond_to?(key)
|
126
|
+
end
|
127
|
+
|
128
|
+
def parse_notice
|
129
|
+
@notice = params[:notice]
|
130
|
+
|
131
|
+
return @notice if @notice[:request].blank?
|
132
|
+
|
133
|
+
doc = Hpricot::XML(request.body)
|
134
|
+
|
135
|
+
convert_request_vars(:params, :params)
|
136
|
+
convert_request_vars(:cgi_data, :'cgi-data')
|
137
|
+
convert_request_vars(:session, :session)
|
138
|
+
|
139
|
+
unless @notice[:request][:params].blank?
|
140
|
+
@notice[:request][:params].delete(:action) # already known
|
141
|
+
@notice[:request][:params].delete(:controller) # already known
|
142
|
+
end
|
143
|
+
rescue
|
144
|
+
@notice = nil
|
145
|
+
render nothing: true, status: :bad_request
|
146
|
+
end
|
147
|
+
|
148
|
+
def convert_request_vars(type, pathname)
|
149
|
+
unless @notice[:request][type.to_sym].blank?
|
150
|
+
vars = convert_var_elements(doc/"/notice/request/#{pathname}/var")
|
151
|
+
@notice[:request][type.to_sym] = vars
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def convert_var_elements(elements)
|
156
|
+
result = {}
|
157
|
+
elements.each do |elem|
|
158
|
+
result[elem.attributes['key']] = elem.inner_text
|
159
|
+
end
|
160
|
+
result.delete_if{|k,v| k.strip.blank?}
|
161
|
+
result.symbolize_keys
|
162
|
+
end
|
163
|
+
|
164
|
+
def init_vars
|
165
|
+
# Project
|
166
|
+
@project = Project.where(identifier: redmine_params[:project]).first
|
167
|
+
return unless @project
|
168
|
+
|
169
|
+
# Tracker
|
170
|
+
@tracker = @project.trackers.where(id: redmine_params[:tracker]).first || @project.trackers.where(name: redmine_params[:tracker]).first || project_setting(:tracker)
|
171
|
+
|
172
|
+
# Category
|
173
|
+
@category = @project.issue_categories.where(id: redmine_params[:category]).first || @project.issue_categories.where(name: redmine_params[:category]).first || project_setting(:category)
|
174
|
+
|
175
|
+
# Priority
|
176
|
+
@priority = IssuePriority.where(id: redmine_params[:priority]).first || IssuePriority.where(name: redmine_params[:priority]).first || project_setting(:priority) || IssuePriority.default
|
177
|
+
|
178
|
+
# Assignee
|
179
|
+
@assignee = @project.users.where(id: redmine_params[:assignee]).first || @project.users.where(login: redmine_params[:assignee]).first
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class AirbrakeProjectSettingsController < ::ApplicationController
|
2
|
+
before_filter :find_project
|
3
|
+
|
4
|
+
menu_item :settings
|
5
|
+
|
6
|
+
def update
|
7
|
+
@airbrake_project_setting = @project.airbrake_settings || AirbrakeProjectSetting.new(project: @project)
|
8
|
+
|
9
|
+
@airbrake_project_setting.tracker = @project.trackers.where(id: params[:airbrake_project_setting][:tracker_id]).first
|
10
|
+
@airbrake_project_setting.category = @project.issue_categories.where(id: params[:airbrake_project_setting][:category_id]).first
|
11
|
+
@airbrake_project_setting.priority = IssuePriority.where(id: params[:airbrake_project_setting][:priority_id]).first
|
12
|
+
@airbrake_project_setting.reopen_regexp = params[:airbrake_project_setting][:reopen_regexp]
|
13
|
+
|
14
|
+
@airbrake_project_setting.save
|
15
|
+
|
16
|
+
redirect_to settings_project_path(@project, :tab => 'airbrake')
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def find_project
|
22
|
+
@project = Project.find(params[:id])
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module AirbrakeHelper
|
2
|
+
|
3
|
+
def format_table(data)
|
4
|
+
lines = []
|
5
|
+
data.each do |key, value|
|
6
|
+
lines << "|@#{key}@|#{value.strip.blank? ? value : "@#{value}@"}|"
|
7
|
+
end
|
8
|
+
lines.join("\n")
|
9
|
+
end
|
10
|
+
|
11
|
+
def format_list_item(name, value)
|
12
|
+
return '' if value.to_s.strip.blank?
|
13
|
+
"* *#{name}:* #{value}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def format_backtrace(element)
|
17
|
+
@htmlentities ||= HTMLEntities.new
|
18
|
+
|
19
|
+
repository = repository_for_backtrace_element(element)
|
20
|
+
|
21
|
+
if repository.blank?
|
22
|
+
file = "@#{@htmlentities.decode(element[:file])}:#{element[:number]}@"
|
23
|
+
elsif repository.identifier.blank?
|
24
|
+
file = "source:\"#{@htmlentities.decode(element[:file][14..-1])}#L#{element[:number]}\""
|
25
|
+
else
|
26
|
+
file = "source:\"#{repository.identifier}|#{@htmlentities.decode(element[:file][14..-1])}#L#{element[:number]}\""
|
27
|
+
end
|
28
|
+
|
29
|
+
file + " in ??<notextile>#{@htmlentities.decode(element[:method])}</notextile>??"
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def repository_for_backtrace_element(element)
|
35
|
+
if element[:file].start_with?('[PROJECT_ROOT]')
|
36
|
+
file = element[:file][14..-1]
|
37
|
+
if redmine_params.key?(:repository)
|
38
|
+
r = @project.repositories.where(identifier: (redmine_params[:repository] || '')).first
|
39
|
+
return r if r.present? && r.entry(file)
|
40
|
+
else
|
41
|
+
return @project.repositories.select{|r| r.entry(file)}.first
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class AirbrakeProjectSetting < ActiveRecord::Base
|
2
|
+
belongs_to :project
|
3
|
+
Project.has_one :airbrake_settings, class_name: AirbrakeProjectSetting.name, dependent: :destroy
|
4
|
+
|
5
|
+
belongs_to :tracker
|
6
|
+
Tracker.has_many :airbrake_project_settings, dependent: :nullify
|
7
|
+
|
8
|
+
belongs_to :category, class_name: IssueCategory.name
|
9
|
+
IssueCategory.has_many :airbrake_project_settings, dependent: :nullify
|
10
|
+
|
11
|
+
belongs_to :priority, class_name: IssuePriority.name
|
12
|
+
IssuePriority.has_many :airbrake_project_settings, dependent: :nullify
|
13
|
+
|
14
|
+
validates_presence_of :project_id
|
15
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
h1. <%= @notice[:error][:message] %>
|
2
|
+
|
3
|
+
h2. Stacktrace:
|
4
|
+
|
5
|
+
p((. <%=raw backtrace.collect{|e| format_backtrace(e)}.join("\n") %>
|
6
|
+
|
7
|
+
<% if @notice[:request].present? %>
|
8
|
+
h2. Request:
|
9
|
+
|
10
|
+
<%=format_list_item('URL', @notice[:request][:url]) %>
|
11
|
+
<%=format_list_item('Component', @notice[:request][:component]) %>
|
12
|
+
<%=format_list_item('Action', @notice[:request][:action]) %>
|
13
|
+
|
14
|
+
<% if @notice[:request][:params].present? %>
|
15
|
+
h2. Parameters:
|
16
|
+
|
17
|
+
<%=format_table(@notice[:request][:params]) %>
|
18
|
+
<% end %>
|
19
|
+
|
20
|
+
<% if @notice[:request][:cgi_data].present? %>
|
21
|
+
h2. Headers:
|
22
|
+
|
23
|
+
<%=format_table(@notice[:request][:cgi_data]) %>
|
24
|
+
<% end %>
|
25
|
+
|
26
|
+
<% if @notice[:request][:session].present? %>
|
27
|
+
h2. Session:
|
28
|
+
|
29
|
+
<%=format_table(@notice[:request][:session]) %>
|
30
|
+
<% end %>
|
31
|
+
<% end %>
|
32
|
+
|
33
|
+
<% if @notice[:server_environment].present? %>
|
34
|
+
h2. Environment
|
35
|
+
|
36
|
+
<%=format_list_item('Directory', @notice[:server_environment][:project_root]) %>
|
37
|
+
<%=format_list_item('Hostname', @notice[:server_environment][:hostname]) %>
|
38
|
+
<% end %>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<% @airbrake_project_setting ||= (@project.airbrake_settings || AirbrakeProjectSetting.new(project: @project)) %>
|
2
|
+
|
3
|
+
<h2><%= l(:label_airbrake) %></h2>
|
4
|
+
|
5
|
+
<%= labelled_form_for :airbrake_project_setting, @airbrake_project_setting, url: airbrake_settings_project_path(@project), html: {method: :post, id: 'airbrake-form'} do |f| %>
|
6
|
+
<%= error_messages_for 'airbrake_project_setting' %>
|
7
|
+
|
8
|
+
<div class="box tabular">
|
9
|
+
<p>
|
10
|
+
<%=f.label :tracker_id %>
|
11
|
+
<%=f.select :tracker_id, options_from_collection_for_select(@project.trackers, :id, :name, @airbrake_project_setting.tracker_id), include_blank: true %>
|
12
|
+
</p>
|
13
|
+
<p>
|
14
|
+
<%=f.label :category_id %>
|
15
|
+
<%=f.select :category_id, options_from_collection_for_select(@project.issue_categories, :id, :name, @airbrake_project_setting.category_id), include_blank: true %>
|
16
|
+
</p>
|
17
|
+
<p>
|
18
|
+
<%=f.label :priority_id %>
|
19
|
+
<%=f.select :priority_id, options_from_collection_for_select(IssuePriority.order(:position), :id, :name, @airbrake_project_setting.priority_id), include_blank: true %>
|
20
|
+
</p>
|
21
|
+
<p>
|
22
|
+
<%=f.label :reopen_regexp %>
|
23
|
+
<%=f.text_field :reopen_regexp %>
|
24
|
+
<br />
|
25
|
+
(<%=l(:text_regexp_info)%>)
|
26
|
+
</p>
|
27
|
+
</div>
|
28
|
+
|
29
|
+
<p>
|
30
|
+
<%= submit_tag(l(:button_save)) %>
|
31
|
+
</p>
|
32
|
+
<% end %>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<p>
|
2
|
+
<%=label_tag 'settings[hash_field]', l(:settings_airbrake_hash_field) %>
|
3
|
+
<%=select('settings[hash_field]', nil, IssueCustomField.where(field_format: 'string').order(:name).collect{|cf| [cf.name, cf.id]}, selected: @settings[:hash_field]) %>
|
4
|
+
</p>
|
5
|
+
|
6
|
+
<p>
|
7
|
+
<%=label_tag 'settings[occurrences_field]', l(:settings_airbrake_occurrences_field) %>
|
8
|
+
<%=select('settings[occurrences_field]', nil, IssueCustomField.where(field_format: 'int').order(:name).collect{|cf| [cf.name, cf.id]}, selected: @settings[:occurrences_field], include_blank: true) %>
|
9
|
+
</p>
|
data/bin/rails
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application.
|
3
|
+
|
4
|
+
ENGINE_ROOT = File.expand_path('../..', __FILE__)
|
5
|
+
ENGINE_PATH = File.expand_path('../../lib/redmine_airbrake_backend/engine', __FILE__)
|
6
|
+
|
7
|
+
require 'rails/all'
|
8
|
+
require 'rails/engine/commands'
|
@@ -0,0 +1,10 @@
|
|
1
|
+
en:
|
2
|
+
label_airbrake: Airbrake
|
3
|
+
|
4
|
+
field_reopen_regexp: Reopen regular expression
|
5
|
+
|
6
|
+
settings_airbrake_hash_field: Notice Hash Custom Field
|
7
|
+
settings_airbrake_occurrences_field: Occurrences Custom Field
|
8
|
+
|
9
|
+
project_module_airbrake: Airbrake
|
10
|
+
permission_manage_airbrake: Manage Airbrake
|
data/config/routes.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreateAirbrakeProjectSettings < ActiveRecord::Migration
|
2
|
+
|
3
|
+
def up
|
4
|
+
create_table :airbrake_project_settings do |t|
|
5
|
+
t.references :project, index: true, null: false
|
6
|
+
t.references :tracker
|
7
|
+
t.references :category
|
8
|
+
t.references :priority
|
9
|
+
t.string :reopen_regexp
|
10
|
+
t.timestamps
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def down
|
15
|
+
drop_table :airbrake_project_settings
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module RedmineAirbrakeBackend
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
initializer 'redmine_airbrake_backend.register_redmine_plugin', after: :load_config_initializers do |app|
|
4
|
+
register_plugin
|
5
|
+
|
6
|
+
require_dependency 'airbrake_project_setting'
|
7
|
+
require_dependency 'redmine_airbrake_backend/project_helper_patch'
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def register_plugin
|
13
|
+
Redmine::Plugin.register :redmine_airbrake_backend do
|
14
|
+
name 'Airbrake Backend'
|
15
|
+
author 'Florian Schwab'
|
16
|
+
description 'Airbrake Backend for Redmine'
|
17
|
+
version '0.0.1'
|
18
|
+
directory File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
19
|
+
|
20
|
+
project_module :airbrake do
|
21
|
+
permission :manage_airbrake, {airbrake: [:update]}
|
22
|
+
end
|
23
|
+
|
24
|
+
settings default: {hash_field: '', occurrences_field: ''}, partial: 'settings/airbrake'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
::ProjectsHelper.module_eval %Q{
|
2
|
+
alias_method :project_settings_tabs_original_airbrake, :project_settings_tabs
|
3
|
+
|
4
|
+
def project_settings_tabs
|
5
|
+
tabs = project_settings_tabs_original_airbrake
|
6
|
+
|
7
|
+
tabs.push({
|
8
|
+
:name => 'airbrake',
|
9
|
+
:action => :manage_airbrake,
|
10
|
+
:partial => 'projects/settings/airbrake',
|
11
|
+
:label => :project_module_airbrake
|
12
|
+
})
|
13
|
+
|
14
|
+
tabs.select{|tab| User.current.allowed_to?(tab[:action], @project)}
|
15
|
+
end
|
16
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'redmine_airbrake_backend/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'redmine_airbrake_backend'
|
8
|
+
spec.version = RedmineAirbrakeBackend::VERSION
|
9
|
+
spec.authors = ['Florian Schwab']
|
10
|
+
spec.email = ['me@ydkn.de']
|
11
|
+
spec.description = %q{Plugin which adds Airbrake support to Redmine}
|
12
|
+
spec.summary = %q{This plugin provides the necessary API to use Redmine as a Airbrake backend}
|
13
|
+
spec.homepage = 'https://github.com/ydkn/redmine_airbrake_backend'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_dependency 'rails'
|
22
|
+
spec.add_dependency 'hpricot'
|
23
|
+
spec.add_dependency 'htmlentities'
|
24
|
+
|
25
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
26
|
+
spec.add_development_dependency 'rake'
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: redmine_airbrake_backend
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Florian Schwab
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-07-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: hpricot
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: htmlentities
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.3'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Plugin which adds Airbrake support to Redmine
|
84
|
+
email:
|
85
|
+
- me@ydkn.de
|
86
|
+
executables:
|
87
|
+
- rails
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- .gitignore
|
92
|
+
- Gemfile
|
93
|
+
- Gemfile.lock
|
94
|
+
- MIT-LICENSE
|
95
|
+
- README.md
|
96
|
+
- Rakefile
|
97
|
+
- app/controllers/airbrake_controller.rb
|
98
|
+
- app/controllers/airbrake_project_settings_controller.rb
|
99
|
+
- app/helpers/airbrake_helper.rb
|
100
|
+
- app/models/airbrake_project_setting.rb
|
101
|
+
- app/views/airbrake/_issue_description.erb
|
102
|
+
- app/views/projects/settings/_airbrake.html.erb
|
103
|
+
- app/views/settings/_airbrake.html.erb
|
104
|
+
- bin/rails
|
105
|
+
- config/locales/en.yml
|
106
|
+
- config/routes.rb
|
107
|
+
- db/migrate/20130708102357_create_airbrake_project_settings.rb
|
108
|
+
- lib/redmine_airbrake_backend.rb
|
109
|
+
- lib/redmine_airbrake_backend/engine.rb
|
110
|
+
- lib/redmine_airbrake_backend/project_helper_patch.rb
|
111
|
+
- lib/redmine_airbrake_backend/version.rb
|
112
|
+
- redmine_airbrake_backend.gemspec
|
113
|
+
homepage: https://github.com/ydkn/redmine_airbrake_backend
|
114
|
+
licenses:
|
115
|
+
- MIT
|
116
|
+
metadata: {}
|
117
|
+
post_install_message:
|
118
|
+
rdoc_options: []
|
119
|
+
require_paths:
|
120
|
+
- lib
|
121
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - '>='
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
requirements: []
|
132
|
+
rubyforge_project:
|
133
|
+
rubygems_version: 2.0.3
|
134
|
+
signing_key:
|
135
|
+
specification_version: 4
|
136
|
+
summary: This plugin provides the necessary API to use Redmine as a Airbrake backend
|
137
|
+
test_files: []
|