redmine_airbrake_backend 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
* [](https://gemnasium.com/ydkn/redmine_airbrake_backend)
|
54
|
+
* [](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: []
|