airbrakeV4rails5 4.3.8
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/CHANGELOG +1716 -0
- data/Gemfile +3 -0
- data/Guardfile +6 -0
- data/INSTALL +20 -0
- data/LICENSE +61 -0
- data/README.md +148 -0
- data/README_FOR_HEROKU_ADDON.md +102 -0
- data/Rakefile +179 -0
- data/TESTED_AGAINST +7 -0
- data/airbrake.gemspec +41 -0
- data/bin/airbrake +12 -0
- data/features/metal.feature +34 -0
- data/features/rack.feature +60 -0
- data/features/rails.feature +324 -0
- data/features/rake.feature +33 -0
- data/features/sinatra.feature +126 -0
- data/features/step_definitions/file_steps.rb +14 -0
- data/features/step_definitions/rack_steps.rb +27 -0
- data/features/step_definitions/rails_application_steps.rb +267 -0
- data/features/step_definitions/rake_steps.rb +22 -0
- data/features/support/airbrake_shim.rb.template +11 -0
- data/features/support/aruba.rb +5 -0
- data/features/support/env.rb +39 -0
- data/features/support/matchers.rb +35 -0
- data/features/support/rails.rb +156 -0
- data/features/support/rake/Rakefile +77 -0
- data/features/user_informer.feature +57 -0
- data/generators/airbrake/airbrake_generator.rb +94 -0
- data/generators/airbrake/lib/insert_commands.rb +34 -0
- data/generators/airbrake/lib/rake_commands.rb +24 -0
- data/generators/airbrake/templates/airbrake_tasks.rake +25 -0
- data/generators/airbrake/templates/capistrano_hook.rb +6 -0
- data/generators/airbrake/templates/initializer.rb +4 -0
- data/install.rb +1 -0
- data/lib/airbrake.rb +191 -0
- data/lib/airbrake/backtrace.rb +103 -0
- data/lib/airbrake/capistrano.rb +103 -0
- data/lib/airbrake/capistrano3.rb +3 -0
- data/lib/airbrake/cli/client.rb +76 -0
- data/lib/airbrake/cli/options.rb +45 -0
- data/lib/airbrake/cli/printer.rb +33 -0
- data/lib/airbrake/cli/project.rb +17 -0
- data/lib/airbrake/cli/project_factory.rb +33 -0
- data/lib/airbrake/cli/runner.rb +49 -0
- data/lib/airbrake/cli/validator.rb +8 -0
- data/lib/airbrake/configuration.rb +366 -0
- data/lib/airbrake/jobs/send_job.rb +7 -0
- data/lib/airbrake/notice.rb +411 -0
- data/lib/airbrake/rack.rb +64 -0
- data/lib/airbrake/rails.rb +45 -0
- data/lib/airbrake/rails/action_controller_catcher.rb +32 -0
- data/lib/airbrake/rails/controller_methods.rb +146 -0
- data/lib/airbrake/rails/error_lookup.rb +35 -0
- data/lib/airbrake/rails/middleware.rb +63 -0
- data/lib/airbrake/rails3_tasks.rb +126 -0
- data/lib/airbrake/railtie.rb +44 -0
- data/lib/airbrake/rake_handler.rb +75 -0
- data/lib/airbrake/response.rb +29 -0
- data/lib/airbrake/sender.rb +213 -0
- data/lib/airbrake/shared_tasks.rb +59 -0
- data/lib/airbrake/sidekiq.rb +8 -0
- data/lib/airbrake/sinatra.rb +40 -0
- data/lib/airbrake/tasks.rb +81 -0
- data/lib/airbrake/tasks/airbrake.cap +28 -0
- data/lib/airbrake/user_informer.rb +36 -0
- data/lib/airbrake/utils/params_cleaner.rb +141 -0
- data/lib/airbrake/utils/rack_filters.rb +45 -0
- data/lib/airbrake/version.rb +3 -0
- data/lib/airbrake_tasks.rb +62 -0
- data/lib/rails/generators/airbrake/airbrake_generator.rb +155 -0
- data/lib/templates/rescue.erb +91 -0
- data/rails/init.rb +1 -0
- data/resources/README.md +34 -0
- data/resources/airbrake_2_4.xsd +89 -0
- data/resources/airbrake_3_0.json +52 -0
- data/resources/ca-bundle.crt +3376 -0
- data/script/integration_test.rb +35 -0
- data/test/airbrake_tasks_test.rb +161 -0
- data/test/backtrace_test.rb +215 -0
- data/test/capistrano_test.rb +44 -0
- data/test/configuration_test.rb +303 -0
- data/test/controller_methods_test.rb +230 -0
- data/test/helper.rb +233 -0
- data/test/integration.rb +13 -0
- data/test/integration/catcher_test.rb +371 -0
- data/test/logger_test.rb +79 -0
- data/test/notice_test.rb +494 -0
- data/test/notifier_test.rb +288 -0
- data/test/params_cleaner_test.rb +204 -0
- data/test/rack_test.rb +62 -0
- data/test/rails_initializer_test.rb +36 -0
- data/test/recursion_test.rb +10 -0
- data/test/response_test.rb +18 -0
- data/test/sender_test.rb +335 -0
- data/test/support/response_shim.xml +4 -0
- data/test/user_informer_test.rb +29 -0
- metadata +469 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
# Mostly pinched from http://github.com/ryanb/nifty-generators/tree/master
|
2
|
+
|
3
|
+
Rails::Generator::Commands::Base.class_eval do
|
4
|
+
def file_contains?(relative_destination, line)
|
5
|
+
File.read(destination_path(relative_destination)).include?(line)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
Rails::Generator::Commands::Create.class_eval do
|
10
|
+
def append_to(file, line)
|
11
|
+
logger.insert "#{line} appended to #{file}"
|
12
|
+
unless options[:pretend] || file_contains?(file, line)
|
13
|
+
File.open(file, "a") do |file|
|
14
|
+
file.puts
|
15
|
+
file.puts line
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Rails::Generator::Commands::Destroy.class_eval do
|
22
|
+
def append_to(file, line)
|
23
|
+
logger.remove "#{line} removed from #{file}"
|
24
|
+
unless options[:pretend]
|
25
|
+
gsub_file file, "\n#{line}", ''
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
Rails::Generator::Commands::List.class_eval do
|
31
|
+
def append_to(file, line)
|
32
|
+
logger.insert "#{line} appended to #{file}"
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Rails::Generator::Commands::Create.class_eval do
|
2
|
+
def rake(cmd, opts = {})
|
3
|
+
logger.rake "rake #{cmd}"
|
4
|
+
unless system("rake #{cmd}")
|
5
|
+
logger.rake "#{cmd} failed. Rolling back"
|
6
|
+
command(:destroy).invoke!
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
Rails::Generator::Commands::Destroy.class_eval do
|
12
|
+
def rake(cmd, opts = {})
|
13
|
+
unless opts[:generate_only]
|
14
|
+
logger.rake "rake #{cmd}"
|
15
|
+
system "rake #{cmd}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Rails::Generator::Commands::List.class_eval do
|
21
|
+
def rake(cmd, opts = {})
|
22
|
+
logger.rake "rake #{cmd}"
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Don't load anything when running the gems:* tasks.
|
2
|
+
# Otherwise, airbrake will be considered a framework gem.
|
3
|
+
# https://thoughtbot.lighthouseapp.com/projects/14221/tickets/629
|
4
|
+
unless ARGV.any? {|a| a =~ /^gems/}
|
5
|
+
|
6
|
+
Dir[File.join(Rails.root, 'vendor', 'gems', 'airbrake-*')].each do |vendored_notifier|
|
7
|
+
$: << File.join(vendored_notifier, 'lib')
|
8
|
+
end
|
9
|
+
|
10
|
+
begin
|
11
|
+
require 'airbrake/tasks'
|
12
|
+
rescue LoadError => exception
|
13
|
+
namespace :airbrake do
|
14
|
+
%w(deploy test log_stdout).each do |task_name|
|
15
|
+
desc "Missing dependency for airbrake:#{task_name}"
|
16
|
+
task task_name do
|
17
|
+
$stderr.puts "Failed to run airbrake:#{task_name} because of missing dependency."
|
18
|
+
$stderr.puts "You probably need to run `rake gems:install` to install the airbrake gem"
|
19
|
+
abort exception.inspect
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
puts IO.read(File.join(File.dirname(__FILE__), 'INSTALL'))
|
data/lib/airbrake.rb
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
begin
|
2
|
+
require "girl_friday"
|
3
|
+
rescue LoadError
|
4
|
+
end
|
5
|
+
|
6
|
+
begin
|
7
|
+
require "sucker_punch"
|
8
|
+
rescue LoadError
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'net/http'
|
12
|
+
require 'net/https'
|
13
|
+
require 'rubygems'
|
14
|
+
require 'logger'
|
15
|
+
|
16
|
+
require 'airbrake/version'
|
17
|
+
require 'airbrake/jobs/send_job'
|
18
|
+
require 'airbrake/utils/rack_filters'
|
19
|
+
require 'airbrake/utils/params_cleaner'
|
20
|
+
require 'airbrake/configuration'
|
21
|
+
require 'airbrake/notice'
|
22
|
+
require 'airbrake/sender'
|
23
|
+
require 'airbrake/response'
|
24
|
+
require 'airbrake/backtrace'
|
25
|
+
require 'airbrake/rack'
|
26
|
+
require 'airbrake/sinatra'
|
27
|
+
require 'airbrake/user_informer'
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'airbrake/sidekiq'
|
31
|
+
rescue LoadError
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'airbrake/railtie' if defined?(Rails::Railtie)
|
35
|
+
|
36
|
+
module Airbrake
|
37
|
+
API_VERSION = "2.4"
|
38
|
+
LOG_PREFIX = "** [Airbrake] "
|
39
|
+
|
40
|
+
class << self
|
41
|
+
# The sender object is responsible for delivering formatted data to the Airbrake server.
|
42
|
+
# Must respond to #send_to_airbrake. See Airbrake::Sender.
|
43
|
+
attr_accessor :sender
|
44
|
+
|
45
|
+
# A Airbrake configuration object. Must act like a hash and return sensible
|
46
|
+
# values for all Airbrake configuration options. See Airbrake::Configuration.
|
47
|
+
attr_writer :configuration
|
48
|
+
|
49
|
+
# Tell the log that the Notifier is good to go
|
50
|
+
def report_ready
|
51
|
+
write_verbose_log("Notifier #{VERSION} ready to catch errors")
|
52
|
+
end
|
53
|
+
|
54
|
+
# Prints out the environment info to the log for debugging help
|
55
|
+
def report_environment_info
|
56
|
+
write_verbose_log("Environment Info: #{environment_info}")
|
57
|
+
end
|
58
|
+
|
59
|
+
# Prints out the response body from Airbrake for debugging help
|
60
|
+
def report_response_body(response)
|
61
|
+
write_verbose_log("Response from Airbrake: \n#{Response.pretty_format(response)}")
|
62
|
+
end
|
63
|
+
|
64
|
+
# Prints out the details about the notice that wasn't sent to server
|
65
|
+
def report_notice(notice)
|
66
|
+
write_verbose_log("Notice details: \n#{notice}")
|
67
|
+
end
|
68
|
+
|
69
|
+
def report_notice_not_sent_for_configuration
|
70
|
+
write_verbose_log("Notice was not sent due to configuration: \
|
71
|
+
\n Environment Monitored? #{configuration.public?} \
|
72
|
+
\n API key set? #{configuration.configured?}")
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns the Ruby version, Rails version, and current Rails environment
|
76
|
+
def environment_info
|
77
|
+
info = "[Ruby: #{RUBY_VERSION}]"
|
78
|
+
info << " [#{configuration.framework}]" if configuration.framework
|
79
|
+
info << " [Env: #{configuration.environment_name}]" if configuration.environment_name
|
80
|
+
info
|
81
|
+
end
|
82
|
+
|
83
|
+
# Writes out the given message to the #logger
|
84
|
+
def write_verbose_log(message)
|
85
|
+
logger.debug LOG_PREFIX + message if logger
|
86
|
+
end
|
87
|
+
|
88
|
+
# Look for the Rails logger currently defined
|
89
|
+
def logger
|
90
|
+
self.configuration.logger ||
|
91
|
+
Logger.new(nil)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Call this method to modify defaults in your initializers.
|
95
|
+
#
|
96
|
+
# @example
|
97
|
+
# Airbrake.configure do |config|
|
98
|
+
# config.api_key = '1234567890abcdef'
|
99
|
+
# config.secure = false
|
100
|
+
# end
|
101
|
+
def configure(silent = false)
|
102
|
+
yield(configuration)
|
103
|
+
self.sender = if configuration.test_mode?
|
104
|
+
CollectingSender.new(configuration)
|
105
|
+
else
|
106
|
+
Sender.new(configuration)
|
107
|
+
end
|
108
|
+
|
109
|
+
report_ready unless silent
|
110
|
+
self.sender
|
111
|
+
end
|
112
|
+
|
113
|
+
# The configuration object.
|
114
|
+
# @see Airbrake.configure
|
115
|
+
def configuration
|
116
|
+
@configuration ||= Configuration.new
|
117
|
+
end
|
118
|
+
|
119
|
+
# Sends an exception manually using this method, even when you are not in a controller.
|
120
|
+
#
|
121
|
+
# @param [Exception] exception The exception you want to notify Airbrake about.
|
122
|
+
# @param [Hash] opts Data that will be sent to Airbrake.
|
123
|
+
#
|
124
|
+
# @option opts [String] :api_key The API key for this project. The API key is a unique identifier that Airbrake uses for identification.
|
125
|
+
# @option opts [String] :error_message The error returned by the exception (or the message you want to log).
|
126
|
+
# @option opts [String] :backtrace A backtrace, usually obtained with +caller+.
|
127
|
+
# @option opts [String] :rack_env The Rack environment.
|
128
|
+
# @option opts [String] :session The contents of the user's session.
|
129
|
+
# @option opts [String] :environment_name The application environment name.
|
130
|
+
# @option opts [String] :parameters Additional parameters.
|
131
|
+
def notify(exception, opts = {})
|
132
|
+
send_notice(build_notice_for(exception, opts))
|
133
|
+
end
|
134
|
+
|
135
|
+
# Sends the notice unless it is one of the default ignored exceptions
|
136
|
+
# @see Airbrake.notify
|
137
|
+
def notify_or_ignore(exception, opts = {})
|
138
|
+
notice = build_notice_for(exception, opts)
|
139
|
+
send_notice(notice) unless notice.ignore?
|
140
|
+
end
|
141
|
+
|
142
|
+
def build_lookup_hash_for(exception, options = {})
|
143
|
+
notice = build_notice_for(exception, options)
|
144
|
+
|
145
|
+
result = {}
|
146
|
+
result[:action] = notice.action rescue nil
|
147
|
+
result[:component] = notice.component rescue nil
|
148
|
+
result[:error_class] = notice.error_class if notice.error_class
|
149
|
+
result[:environment_name] = 'production'
|
150
|
+
|
151
|
+
unless notice.backtrace.lines.empty?
|
152
|
+
result[:file] = notice.backtrace.lines.first.file
|
153
|
+
result[:line_number] = notice.backtrace.lines.first.number
|
154
|
+
end
|
155
|
+
|
156
|
+
result
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def send_notice(notice)
|
162
|
+
if configuration.configured? && configuration.public?
|
163
|
+
if configuration.async?
|
164
|
+
configuration.async.call(notice)
|
165
|
+
nil # make sure we never set env["airbrake.error_id"] for async notices
|
166
|
+
else
|
167
|
+
sender.send_to_airbrake(notice)
|
168
|
+
end
|
169
|
+
else
|
170
|
+
report_notice_not_sent_for_configuration
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def build_notice_for(exception, opts = {})
|
175
|
+
exception_classes = [exception.class.to_s]
|
176
|
+
exception = unwrap_exception(exception)
|
177
|
+
opts = opts.merge(:exception => exception) if exception.is_a?(Exception)
|
178
|
+
opts = opts.merge(exception.to_hash) if exception.respond_to?(:to_hash)
|
179
|
+
opts = opts.merge(:exception_classes => exception_classes)
|
180
|
+
Notice.new(configuration.merge(opts))
|
181
|
+
end
|
182
|
+
|
183
|
+
def unwrap_exception(exception)
|
184
|
+
if exception.respond_to?(:original_exception)
|
185
|
+
exception.original_exception
|
186
|
+
elsif exception.respond_to?(:continued_exception)
|
187
|
+
exception.continued_exception
|
188
|
+
end || exception
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Airbrake
|
2
|
+
# Front end to parsing the backtrace for each notice
|
3
|
+
class Backtrace
|
4
|
+
|
5
|
+
# Handles backtrace parsing line by line
|
6
|
+
class Line
|
7
|
+
|
8
|
+
# regexp (optionnally allowing leading X: for windows support)
|
9
|
+
INPUT_FORMAT = %r{^((?:[a-zA-Z]:)?[^:]+):(\d+)(?::in `([^']+)')?$}.freeze
|
10
|
+
|
11
|
+
# The file portion of the line (such as app/models/user.rb)
|
12
|
+
attr_reader :file
|
13
|
+
|
14
|
+
# The line number portion of the line
|
15
|
+
attr_reader :number
|
16
|
+
|
17
|
+
# The method_name of the line (such as index)
|
18
|
+
attr_reader :method_name
|
19
|
+
|
20
|
+
# Parses a single line of a given backtrace
|
21
|
+
# @param [String] unparsed_line The raw line from +caller+ or some backtrace
|
22
|
+
# @return [Line] The parsed backtrace line
|
23
|
+
def self.parse(unparsed_line)
|
24
|
+
_, file, number, method_name = unparsed_line.match(INPUT_FORMAT).to_a
|
25
|
+
new(file, number, method_name)
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(file, number, method_name)
|
29
|
+
@file = file
|
30
|
+
@number = number
|
31
|
+
@method_name = method_name
|
32
|
+
end
|
33
|
+
|
34
|
+
# Reconstructs the line in a readable fashion
|
35
|
+
def to_s
|
36
|
+
"#{file}:#{number}:in `#{method_name}'"
|
37
|
+
end
|
38
|
+
|
39
|
+
def ==(other)
|
40
|
+
to_s == other.to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
def inspect
|
44
|
+
"<Line:#{to_s}>"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# holder for an Array of Backtrace::Line instances
|
49
|
+
attr_reader :lines
|
50
|
+
|
51
|
+
def self.parse(ruby_backtrace, opts = {})
|
52
|
+
ruby_lines = split_multiline_backtrace(ruby_backtrace)
|
53
|
+
|
54
|
+
filters = opts[:filters] || []
|
55
|
+
filtered_lines = ruby_lines.to_a.map do |line|
|
56
|
+
filters.inject(line) do |l, proc|
|
57
|
+
proc.call(l)
|
58
|
+
end
|
59
|
+
end.compact
|
60
|
+
|
61
|
+
lines = filtered_lines.collect do |unparsed_line|
|
62
|
+
Line.parse(unparsed_line)
|
63
|
+
end
|
64
|
+
|
65
|
+
new(lines)
|
66
|
+
end
|
67
|
+
|
68
|
+
def initialize(lines)
|
69
|
+
@lines = lines
|
70
|
+
end
|
71
|
+
|
72
|
+
def inspect
|
73
|
+
"<Backtrace: " + lines.collect { |line| line.inspect }.join(", ") + ">"
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_s
|
77
|
+
content = []
|
78
|
+
lines.each do |line|
|
79
|
+
content << line
|
80
|
+
end
|
81
|
+
content.join("\n")
|
82
|
+
end
|
83
|
+
|
84
|
+
def ==(other)
|
85
|
+
if other.respond_to?(:lines)
|
86
|
+
lines == other.lines
|
87
|
+
else
|
88
|
+
false
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def self.split_multiline_backtrace(backtrace)
|
95
|
+
backtrace = [backtrace] unless backtrace.respond_to?(:to_a)
|
96
|
+
if backtrace.to_a.size == 1
|
97
|
+
backtrace.to_a.first.split(/\n\s*/)
|
98
|
+
else
|
99
|
+
backtrace
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# Defines deploy:notify_airbrake which will send information about the deploy to Airbrake.
|
2
|
+
require 'capistrano'
|
3
|
+
|
4
|
+
module Airbrake
|
5
|
+
module Capistrano
|
6
|
+
# What follows is a copy-paste backport of the shellescape method
|
7
|
+
# included in Ruby 1.9 and greater. The FSF's guidance on a snippet
|
8
|
+
# of this size indicates that such a small function is not subject
|
9
|
+
# to copyright and as such there is no risk of a license conflict:
|
10
|
+
# See www.gnu.org/prep/maintain/maintain.html#Legally-Significant
|
11
|
+
#
|
12
|
+
# Escapes a string so that it can be safely used in a Bourne shell
|
13
|
+
# command line. +str+ can be a non-string object that responds to
|
14
|
+
# +to_s+.
|
15
|
+
#
|
16
|
+
# Note that a resulted string should be used unquoted and is not
|
17
|
+
# intended for use in double quotes nor in single quotes.
|
18
|
+
#
|
19
|
+
# argv = Shellwords.escape("It's better to give than to receive")
|
20
|
+
# argv #=> "It\\'s\\ better\\ to\\ give\\ than\\ to\\ receive"
|
21
|
+
#
|
22
|
+
# String#shellescape is a shorthand for this function.
|
23
|
+
#
|
24
|
+
# argv = "It's better to give than to receive".shellescape
|
25
|
+
# argv #=> "It\\'s\\ better\\ to\\ give\\ than\\ to\\ receive"
|
26
|
+
#
|
27
|
+
# # Search files in lib for method definitions
|
28
|
+
# pattern = "^[ \t]*def "
|
29
|
+
# open("| grep -Ern #{pattern.shellescape} lib") { |grep|
|
30
|
+
# grep.each_line { |line|
|
31
|
+
# file, lineno, matched_line = line.split(':', 3)
|
32
|
+
# # ...
|
33
|
+
# }
|
34
|
+
# }
|
35
|
+
#
|
36
|
+
# It is the caller's responsibility to encode the string in the right
|
37
|
+
# encoding for the shell environment where this string is used.
|
38
|
+
#
|
39
|
+
# Multibyte characters are treated as multibyte characters, not bytes.
|
40
|
+
#
|
41
|
+
# Returns an empty quoted String if +str+ has a length of zero.
|
42
|
+
def self.shellescape(str)
|
43
|
+
str = str.to_s
|
44
|
+
|
45
|
+
# An empty argument will be skipped, so return empty quotes.
|
46
|
+
return "''" if str.empty?
|
47
|
+
|
48
|
+
str = str.dup
|
49
|
+
|
50
|
+
# Treat multibyte characters as is. It is caller's responsibility
|
51
|
+
# to encode the string in the right encoding for the shell
|
52
|
+
# environment.
|
53
|
+
str.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/, "\\\\\\1")
|
54
|
+
|
55
|
+
# A LF cannot be escaped with a backslash because a backslash + LF
|
56
|
+
# combo is regarded as line continuation and simply ignored.
|
57
|
+
str.gsub!(/\n/, "'\n'")
|
58
|
+
|
59
|
+
return str
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.load_into(configuration)
|
63
|
+
configuration.load do
|
64
|
+
after "deploy", "airbrake:deploy"
|
65
|
+
after "deploy:migrations", "airbrake:deploy"
|
66
|
+
after "deploy:cold", "airbrake:deploy"
|
67
|
+
|
68
|
+
namespace :airbrake do
|
69
|
+
desc <<-DESC
|
70
|
+
Notify Airbrake of the deployment by running the notification on the REMOTE machine.
|
71
|
+
- Run remotely so we use remote API keys, environment, etc.
|
72
|
+
DESC
|
73
|
+
task :deploy, :except => { :no_release => true } do
|
74
|
+
rack_env = fetch(:rack_env, nil)
|
75
|
+
rails_env = fetch(:rails_env, nil)
|
76
|
+
airbrake_env = fetch(:airbrake_env, rack_env || rails_env || "production")
|
77
|
+
local_user = ENV['USER'] || ENV['USERNAME']
|
78
|
+
executable = RUBY_PLATFORM.downcase.include?('mswin') ? fetch(:rake, 'rake.bat') : fetch(:rake, 'bundle exec rake ')
|
79
|
+
directory = configuration.release_path
|
80
|
+
notify_command = "cd #{directory}; #{executable}"
|
81
|
+
notify_command << " RACK_ENV=#{rack_env}" if rack_env
|
82
|
+
notify_command << " RAILS_ENV=#{rails_env}" if rails_env
|
83
|
+
notify_command << " airbrake:deploy TO=#{airbrake_env} REVISION=#{current_revision} REPO=#{repository} USER=#{Airbrake::Capistrano::shellescape(local_user)}"
|
84
|
+
notify_command << " DRY_RUN=true" if dry_run
|
85
|
+
logger.info "Notifying Airbrake of Deploy (#{notify_command})"
|
86
|
+
if configuration.dry_run
|
87
|
+
logger.info "DRY RUN: Notification not actually run."
|
88
|
+
else
|
89
|
+
result = ""
|
90
|
+
run(notify_command, :once => true) { |ch, stream, data| result << data }
|
91
|
+
# TODO: Check if SSL is active on account via result content.
|
92
|
+
end
|
93
|
+
logger.info "Airbrake Notification Complete."
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
if Capistrano::Configuration.instance
|
102
|
+
Airbrake::Capistrano.load_into(Capistrano::Configuration.instance)
|
103
|
+
end
|