redmine_airbrake_backend 0.1.0 → 0.2.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 +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/app/controllers/airbrake_controller.rb +62 -91
- data/app/controllers/airbrake_project_settings_controller.rb +2 -5
- data/app/helpers/airbrake_helper.rb +3 -3
- data/app/models/airbrake_project_setting.rb +4 -0
- data/app/views/airbrake/_issue_description.erb +17 -15
- data/app/views/projects/settings/_airbrake.html.erb +2 -4
- data/lib/redmine_airbrake_backend/notice.rb +85 -0
- data/lib/redmine_airbrake_backend/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c4df3337dec81bfdd744a3ca6a7b741b29ceb59
|
4
|
+
data.tar.gz: 2e43509ade9e2934870111dbd47e8fe2fa5baf58
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 79d9a308477cf2ee7b1df7c825a9ecde9e10d34278d9656e3c221f6598424f5ae855cbcb6a023c10a6d2fbbf6401b3ee97bb2684b4004c8cf969e7a6f2cdd4c5
|
7
|
+
data.tar.gz: ed5d5aa24babd1201e680fdb5c761ae7e6cc1050e6bb15e6cb189e8a1790b58df9aa447a9ba23e98cf759b45fe258521478711d1b8c870a6fe78cb0d4ccc77e0
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,24 +1,14 @@
|
|
1
|
-
require '
|
2
|
-
require 'hpricot'
|
3
|
-
require 'htmlentities'
|
1
|
+
require 'redmine_airbrake_backend/notice'
|
4
2
|
|
5
3
|
class AirbrakeController < ::ApplicationController
|
6
|
-
|
7
|
-
|
8
|
-
prepend_before_filter :set_api_auth
|
9
|
-
before_filter :parse_notice
|
10
|
-
before_filter :init_vars
|
4
|
+
prepend_before_filter :parse_notice_and_api_auth
|
5
|
+
before_filter :load_records
|
11
6
|
|
12
7
|
accept_api_auth :notice
|
13
8
|
|
14
9
|
def notice
|
15
10
|
return unless authorize(:issues, :create)
|
16
11
|
|
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
12
|
# Issue by project, tracker and hash
|
23
13
|
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
14
|
@issue = Issue.where(id: issue_ids, project_id: @project.id, tracker_id: @tracker.id).first
|
@@ -29,7 +19,7 @@ class AirbrakeController < ::ApplicationController
|
|
29
19
|
author: User.current,
|
30
20
|
category: @category,
|
31
21
|
priority: @priority,
|
32
|
-
description:
|
22
|
+
description: render_description,
|
33
23
|
assigned_to: @assignee
|
34
24
|
) unless @issue
|
35
25
|
|
@@ -42,7 +32,7 @@ class AirbrakeController < ::ApplicationController
|
|
42
32
|
# Reopen if closed
|
43
33
|
if reopen? && @issue.status.is_closed?
|
44
34
|
@issue.status = IssueStatus.where(is_default: true).order(:position).first
|
45
|
-
@issue.init_journal(User.current, "Issue reopened after occurring again in environment #{@notice[:
|
35
|
+
@issue.init_journal(User.current, "Issue reopened after occurring again in environment #{@notice.env[:environment_name]}")
|
46
36
|
end
|
47
37
|
|
48
38
|
# Hash
|
@@ -64,41 +54,73 @@ class AirbrakeController < ::ApplicationController
|
|
64
54
|
|
65
55
|
private
|
66
56
|
|
67
|
-
def
|
68
|
-
|
57
|
+
def parse_notice_and_api_auth
|
58
|
+
@notice = RedmineAirbrakeBackend::Notice.parse(request.body)
|
59
|
+
params[:key] = @notice.params[:api_key]
|
60
|
+
rescue RedmineAirbrakeBackend::Notice::NoticeInvalid, RedmineAirbrakeBackend::Notice::UnsupportedVersion
|
61
|
+
render nothing: true, status: :bad_request
|
69
62
|
end
|
70
63
|
|
71
|
-
def
|
72
|
-
|
64
|
+
def load_records
|
65
|
+
# Project
|
66
|
+
unless @project = Project.where(identifier: @notice.params[:project]).first
|
67
|
+
render nothing: true, status: :bad_request
|
68
|
+
return
|
69
|
+
end
|
70
|
+
|
71
|
+
# Tracker
|
72
|
+
unless (@tracker = record_for(@project.trackers, :tracker)) && @tracker.custom_fields.where(id: notice_hash_field.id).first
|
73
|
+
render nothing: true, status: :bad_request
|
74
|
+
return
|
75
|
+
end
|
76
|
+
|
77
|
+
# Category
|
78
|
+
@category = record_for(@project.issue_categories, :category)
|
79
|
+
|
80
|
+
# Priority
|
81
|
+
@priority = record_for(IssuePriority, :priority) || IssuePriority.default
|
82
|
+
|
83
|
+
# Assignee
|
84
|
+
@assignee = record_for(@project.users, :assignee, [:id, :login])
|
73
85
|
end
|
74
|
-
helper_method :redmine_params
|
75
86
|
|
76
|
-
def
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
"[#{notice_hash[0..7]}] #{@notice[:error][:class]} #{@notice[:error][:message]}"[0..254]
|
87
|
+
def record_for(on, param_key, fields=[:id, :name])
|
88
|
+
fields.each do |field|
|
89
|
+
val = on.where(field => @notice.params[param_key]).first
|
90
|
+
return val if val.present?
|
81
91
|
end
|
92
|
+
|
93
|
+
project_setting(param_key)
|
82
94
|
end
|
83
95
|
|
84
|
-
def
|
85
|
-
if @
|
86
|
-
|
96
|
+
def project_setting(key)
|
97
|
+
return nil if @project.airbrake_settings.blank?
|
98
|
+
@project.airbrake_settings.send(key) if @project.airbrake_settings.respond_to?(key)
|
99
|
+
end
|
100
|
+
|
101
|
+
def subject
|
102
|
+
if @notice.error[:message].starts_with?("#{@notice.error[:class]}:")
|
103
|
+
"[#{notice_hash[0..7]}] #{@notice.error[:message]}"[0..254]
|
87
104
|
else
|
88
|
-
@notice[:error
|
105
|
+
"[#{notice_hash[0..7]}] #{@notice.error[:class]} #{@notice.error[:message]}"[0..254]
|
89
106
|
end
|
90
107
|
end
|
91
|
-
helper_method :backtrace
|
92
108
|
|
93
109
|
def notice_hash
|
94
110
|
h = []
|
95
|
-
h << @notice
|
96
|
-
h << @notice
|
97
|
-
h +=
|
111
|
+
h << @notice.error[:class]
|
112
|
+
h << @notice.error[:message]
|
113
|
+
h += normalized_backtrace if @notice.error[:backtrace].present?
|
98
114
|
|
99
115
|
Digest::MD5.hexdigest(h.compact.join("\n"))
|
100
116
|
end
|
101
117
|
|
118
|
+
def normalized_backtrace
|
119
|
+
@notice.error[:backtrace].collect do |e|
|
120
|
+
"#{e[:file]}|#{e[:method].gsub(/_\d+_/, '')}|#{e[:number]}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
102
124
|
def notice_hash_field
|
103
125
|
custom_field(:hash_field)
|
104
126
|
end
|
@@ -112,71 +134,20 @@ class AirbrakeController < ::ApplicationController
|
|
112
134
|
end
|
113
135
|
|
114
136
|
def reopen?
|
115
|
-
return false if project_setting(:reopen_regexp).blank?
|
116
|
-
!!(@notice[:
|
137
|
+
return false if @notice.env.blank? || @notice.env[:environment_name].blank? || project_setting(:reopen_regexp).blank?
|
138
|
+
!!(@notice.env[:environment_name] =~ /#{project_setting(:reopen_regexp)}/i)
|
117
139
|
end
|
118
140
|
|
119
141
|
def setting(key)
|
120
142
|
Setting.plugin_redmine_airbrake_backend[key]
|
121
143
|
end
|
122
144
|
|
123
|
-
def
|
124
|
-
|
125
|
-
|
126
|
-
|
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
|
145
|
+
def render_description
|
146
|
+
if template_exists?("issue_description_#{@notice.params[:type]}", 'airbrake', true)
|
147
|
+
render_to_string(partial: "issue_description_#{@notice.params[:type]}")
|
148
|
+
else
|
149
|
+
render_to_string(partial: 'issue_description')
|
159
150
|
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
151
|
end
|
181
152
|
|
182
153
|
end
|
@@ -5,14 +5,11 @@ class AirbrakeProjectSettingsController < ::ApplicationController
|
|
5
5
|
|
6
6
|
def update
|
7
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]
|
8
|
+
@airbrake_project_setting.safe_attributes = params[:airbrake_project_setting]
|
13
9
|
|
14
10
|
@airbrake_project_setting.save
|
15
11
|
|
12
|
+
flash[:notice] = l(:notice_successful_update)
|
16
13
|
redirect_to settings_project_path(@project, :tab => 'airbrake')
|
17
14
|
end
|
18
15
|
|
@@ -13,7 +13,7 @@ module AirbrakeHelper
|
|
13
13
|
"* *#{name}:* #{value}"
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def format_backtrace_element(element)
|
17
17
|
@htmlentities ||= HTMLEntities.new
|
18
18
|
|
19
19
|
repository = repository_for_backtrace_element(element)
|
@@ -34,8 +34,8 @@ module AirbrakeHelper
|
|
34
34
|
def repository_for_backtrace_element(element)
|
35
35
|
if element[:file].start_with?('[PROJECT_ROOT]')
|
36
36
|
file = element[:file][14..-1]
|
37
|
-
if
|
38
|
-
r = @project.repositories.where(identifier: (
|
37
|
+
if @notice.params.key?(:repository)
|
38
|
+
r = @project.repositories.where(identifier: (@notice.params[:repository] || '')).first
|
39
39
|
return r if r.present? && r.entry(file)
|
40
40
|
else
|
41
41
|
return @project.repositories.select{|r| r.entry(file)}.first
|
@@ -1,4 +1,6 @@
|
|
1
1
|
class AirbrakeProjectSetting < ActiveRecord::Base
|
2
|
+
include Redmine::SafeAttributes
|
3
|
+
|
2
4
|
belongs_to :project
|
3
5
|
Project.has_one :airbrake_settings, class_name: AirbrakeProjectSetting.name, dependent: :destroy
|
4
6
|
|
@@ -12,4 +14,6 @@ class AirbrakeProjectSetting < ActiveRecord::Base
|
|
12
14
|
IssuePriority.has_many :airbrake_project_settings, dependent: :nullify
|
13
15
|
|
14
16
|
validates_presence_of :project_id
|
17
|
+
|
18
|
+
safe_attributes :tracker_id, :category_id, :priority_id, :reopen_regexp
|
15
19
|
end
|
@@ -1,38 +1,40 @@
|
|
1
|
-
h1. <%= @notice
|
1
|
+
h1. <%= @notice.error[:message] %>
|
2
2
|
|
3
|
+
<% if @notice.error.present? && @notice.error[:backtrace].present? %>
|
3
4
|
h2. Stacktrace:
|
4
5
|
|
5
|
-
p((. <%=raw backtrace.collect{|e|
|
6
|
+
p((. <%=raw @notice.error[:backtrace].collect{|e| format_backtrace_element(e)}.join("\n") %>
|
7
|
+
<% end %>
|
6
8
|
|
7
|
-
<% if @notice
|
9
|
+
<% if @notice.request.present? %>
|
8
10
|
h2. Request:
|
9
11
|
|
10
|
-
<%=format_list_item('URL', @notice
|
11
|
-
<%=format_list_item('Component', @notice
|
12
|
-
<%=format_list_item('Action', @notice
|
12
|
+
<%=format_list_item('URL', @notice.request[:url]) %>
|
13
|
+
<%=format_list_item('Component', @notice.request[:component]) %>
|
14
|
+
<%=format_list_item('Action', @notice.request[:action]) %>
|
13
15
|
|
14
|
-
<% if @notice
|
16
|
+
<% if @notice.request[:params].present? %>
|
15
17
|
h2. Parameters:
|
16
18
|
|
17
|
-
<%=format_table(@notice
|
19
|
+
<%=format_table(@notice.request[:params]) %>
|
18
20
|
<% end %>
|
19
21
|
|
20
|
-
<% if @notice
|
22
|
+
<% if @notice.request[:cgi_data].present? %>
|
21
23
|
h2. Headers:
|
22
24
|
|
23
|
-
<%=format_table(@notice
|
25
|
+
<%=format_table(@notice.request[:cgi_data]) %>
|
24
26
|
<% end %>
|
25
27
|
|
26
|
-
<% if @notice
|
28
|
+
<% if @notice.request[:session].present? %>
|
27
29
|
h2. Session:
|
28
30
|
|
29
|
-
<%=format_table(@notice
|
31
|
+
<%=format_table(@notice.request[:session]) %>
|
30
32
|
<% end %>
|
31
33
|
<% end %>
|
32
34
|
|
33
|
-
<% if @notice
|
35
|
+
<% if @notice.env.present? %>
|
34
36
|
h2. Environment
|
35
37
|
|
36
|
-
<%=format_list_item('Directory', @notice[:
|
37
|
-
<%=format_list_item('Hostname', @notice[:
|
38
|
+
<%=format_list_item('Directory', @notice.env[:project_root]) %>
|
39
|
+
<%=format_list_item('Hostname', @notice.env[:hostname]) %>
|
38
40
|
<% end %>
|
@@ -1,5 +1,7 @@
|
|
1
1
|
<% @airbrake_project_setting ||= (@project.airbrake_settings || AirbrakeProjectSetting.new(project: @project)) %>
|
2
2
|
|
3
|
+
<%= error_messages_for 'airbrake_project_setting' %>
|
4
|
+
|
3
5
|
<h2><%= l(:label_airbrake) %></h2>
|
4
6
|
|
5
7
|
<%= labelled_form_for :airbrake_project_setting, @airbrake_project_setting, url: airbrake_settings_project_path(@project), html: {method: :post, id: 'airbrake-form'} do |f| %>
|
@@ -7,19 +9,15 @@
|
|
7
9
|
|
8
10
|
<div class="box tabular">
|
9
11
|
<p>
|
10
|
-
<%=f.label :tracker_id %>
|
11
12
|
<%=f.select :tracker_id, options_from_collection_for_select(@project.trackers, :id, :name, @airbrake_project_setting.tracker_id), include_blank: true %>
|
12
13
|
</p>
|
13
14
|
<p>
|
14
|
-
<%=f.label :category_id %>
|
15
15
|
<%=f.select :category_id, options_from_collection_for_select(@project.issue_categories, :id, :name, @airbrake_project_setting.category_id), include_blank: true %>
|
16
16
|
</p>
|
17
17
|
<p>
|
18
|
-
<%=f.label :priority_id %>
|
19
18
|
<%=f.select :priority_id, options_from_collection_for_select(IssuePriority.order(:position), :id, :name, @airbrake_project_setting.priority_id), include_blank: true %>
|
20
19
|
</p>
|
21
20
|
<p>
|
22
|
-
<%=f.label :reopen_regexp %>
|
23
21
|
<%=f.text_field :reopen_regexp %>
|
24
22
|
<br />
|
25
23
|
(<%=l(:text_regexp_info)%>)
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'hpricot'
|
3
|
+
require 'htmlentities'
|
4
|
+
|
5
|
+
module RedmineAirbrakeBackend
|
6
|
+
class Notice
|
7
|
+
SUPPORTED_API_VERSIONS = %w(2.4)
|
8
|
+
|
9
|
+
class NoticeInvalid < StandardError; end
|
10
|
+
class UnsupportedVersion < StandardError; end
|
11
|
+
|
12
|
+
attr_reader :version, :params, :notifier, :error, :request, :env
|
13
|
+
|
14
|
+
def initialize(version, params, notifier, options={})
|
15
|
+
@version = version
|
16
|
+
@params = params
|
17
|
+
@notifier = notifier
|
18
|
+
|
19
|
+
@error = options.delete(:error)
|
20
|
+
@request = options.delete(:request)
|
21
|
+
@env = options.delete(:env)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.parse(xml_data)
|
25
|
+
doc = Hpricot::XML(xml_data)
|
26
|
+
|
27
|
+
raise NoticeInvalid if (notice = doc.at('notice')).blank?
|
28
|
+
raise NoticeInvalid if (version = notice.attributes['version']).blank?
|
29
|
+
raise UnsupportedVersion unless SUPPORTED_API_VERSIONS.include?(version)
|
30
|
+
|
31
|
+
params = JSON.parse(notice.at('api-key').inner_text).symbolize_keys rescue nil
|
32
|
+
raise NoticeInvalid if params.blank?
|
33
|
+
|
34
|
+
raise NoticeInvalid if (notifier = convert_element(notice.at('notifier'))).blank?
|
35
|
+
|
36
|
+
raise NoticeInvalid if (error = convert_element(notice.at('error'))).blank?
|
37
|
+
raise NoticeInvalid if error[:class].blank? || error[:message].blank?
|
38
|
+
error[:backtrace] = error[:backtrace][:line].is_a?(Array) ? error[:backtrace][:line] : [error[:backtrace][:line]] unless error[:backtrace].nil?
|
39
|
+
|
40
|
+
request = convert_element(notice.at('request'))
|
41
|
+
env = convert_element(notice.at('server-environment'))
|
42
|
+
|
43
|
+
new(version, params, notifier, error: error, request: request, env: env)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def self.convert_element(elem)
|
49
|
+
return nil if elem.nil?
|
50
|
+
|
51
|
+
return elem.children.first.inner_text if !elem.children.nil? && elem.children.count == 1 && elem.children.first.is_a?(Hpricot::Text)
|
52
|
+
|
53
|
+
return elem.attributes.to_hash.symbolize_keys if elem.children.nil?
|
54
|
+
|
55
|
+
return convert_var_elements(elem.children) if elem.children.count == elem.children.select{|c| c.name == 'var'}.count
|
56
|
+
|
57
|
+
h = {}
|
58
|
+
elem.children.each do |e|
|
59
|
+
key = format_hash_key(e.name)
|
60
|
+
if h.key?(key)
|
61
|
+
h[key] = [h[key]] unless h[key].is_a?(Array)
|
62
|
+
h[key] << convert_element(e)
|
63
|
+
else
|
64
|
+
h[key] = convert_element(e)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
h.delete_if{|k,v| k.strip.blank?}
|
68
|
+
h.symbolize_keys
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.convert_var_elements(elements)
|
72
|
+
vars = {}
|
73
|
+
elements.each do |elem|
|
74
|
+
vars[format_hash_key(elem.attributes['key'])] = elem.inner_text
|
75
|
+
end
|
76
|
+
vars.delete_if{|k,v| k.strip.blank?}
|
77
|
+
vars.symbolize_keys
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.format_hash_key(key)
|
81
|
+
key.to_s.gsub(/-/, '_')
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redmine_airbrake_backend
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Florian Schwab
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-07-
|
11
|
+
date: 2013-07-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -107,6 +107,7 @@ files:
|
|
107
107
|
- db/migrate/20130708102357_create_airbrake_project_settings.rb
|
108
108
|
- lib/redmine_airbrake_backend.rb
|
109
109
|
- lib/redmine_airbrake_backend/engine.rb
|
110
|
+
- lib/redmine_airbrake_backend/notice.rb
|
110
111
|
- lib/redmine_airbrake_backend/project_helper_patch.rb
|
111
112
|
- lib/redmine_airbrake_backend/version.rb
|
112
113
|
- redmine_airbrake_backend.gemspec
|