projectlocker_pulse 0.2.1
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.
- data/CHANGELOG +26 -0
- data/Gemfile +3 -0
- data/Guardfile +6 -0
- data/INSTALL +20 -0
- data/MIT-LICENSE +23 -0
- data/README.md +439 -0
- data/README_FOR_HEROKU_ADDON.md +89 -0
- data/Rakefile +223 -0
- data/SUPPORTED_RAILS_VERSIONS +38 -0
- data/TESTING.md +41 -0
- data/features/metal.feature +18 -0
- data/features/rack.feature +60 -0
- data/features/rails.feature +272 -0
- data/features/rails_with_js_notifier.feature +97 -0
- data/features/rake.feature +27 -0
- data/features/sinatra.feature +29 -0
- data/features/step_definitions/file_steps.rb +10 -0
- data/features/step_definitions/metal_steps.rb +23 -0
- data/features/step_definitions/rack_steps.rb +23 -0
- data/features/step_definitions/rails_application_steps.rb +478 -0
- data/features/step_definitions/rake_steps.rb +17 -0
- data/features/support/env.rb +18 -0
- data/features/support/matchers.rb +35 -0
- data/features/support/projectlocker_pulse_shim.rb.template +16 -0
- data/features/support/rails.rb +201 -0
- data/features/support/rake/Rakefile +68 -0
- data/features/support/terminal.rb +107 -0
- data/features/user_informer.feature +63 -0
- data/generators/pulse/lib/insert_commands.rb +34 -0
- data/generators/pulse/lib/rake_commands.rb +24 -0
- data/generators/pulse/pulse_generator.rb +94 -0
- data/generators/pulse/templates/capistrano_hook.rb +6 -0
- data/generators/pulse/templates/initializer.rb +6 -0
- data/generators/pulse/templates/pulse_tasks.rake +25 -0
- data/install.rb +1 -0
- data/lib/projectlocker_pulse.rb +159 -0
- data/lib/pulse/backtrace.rb +108 -0
- data/lib/pulse/capistrano.rb +43 -0
- data/lib/pulse/configuration.rb +305 -0
- data/lib/pulse/notice.rb +390 -0
- data/lib/pulse/rack.rb +54 -0
- data/lib/pulse/rails/action_controller_catcher.rb +30 -0
- data/lib/pulse/rails/controller_methods.rb +85 -0
- data/lib/pulse/rails/error_lookup.rb +33 -0
- data/lib/pulse/rails/javascript_notifier.rb +47 -0
- data/lib/pulse/rails/middleware/exceptions_catcher.rb +33 -0
- data/lib/pulse/rails.rb +40 -0
- data/lib/pulse/rails3_tasks.rb +99 -0
- data/lib/pulse/railtie.rb +49 -0
- data/lib/pulse/rake_handler.rb +65 -0
- data/lib/pulse/sender.rb +128 -0
- data/lib/pulse/shared_tasks.rb +47 -0
- data/lib/pulse/tasks.rb +83 -0
- data/lib/pulse/user_informer.rb +27 -0
- data/lib/pulse/utils/blank.rb +53 -0
- data/lib/pulse/version.rb +3 -0
- data/lib/pulse_tasks.rb +64 -0
- data/lib/rails/generators/pulse/pulse_generator.rb +100 -0
- data/lib/templates/javascript_notifier.erb +15 -0
- data/lib/templates/rescue.erb +91 -0
- data/pulse.gemspec +39 -0
- data/rails/init.rb +1 -0
- data/resources/README.md +34 -0
- data/resources/ca-bundle.crt +3376 -0
- data/script/integration_test.rb +38 -0
- data/test/backtrace_test.rb +162 -0
- data/test/capistrano_test.rb +34 -0
- data/test/catcher_test.rb +333 -0
- data/test/configuration_test.rb +236 -0
- data/test/helper.rb +263 -0
- data/test/javascript_notifier_test.rb +51 -0
- data/test/logger_test.rb +79 -0
- data/test/notice_test.rb +490 -0
- data/test/notifier_test.rb +276 -0
- data/test/projectlocker_pulse_tasks_test.rb +170 -0
- data/test/pulse.xsd +88 -0
- data/test/rack_test.rb +58 -0
- data/test/rails_initializer_test.rb +36 -0
- data/test/recursion_test.rb +10 -0
- data/test/sender_test.rb +288 -0
- data/test/user_informer_test.rb +29 -0
- metadata +432 -0
data/lib/pulse/rails.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'projectlocker_pulse'
|
2
|
+
require 'pulse/rails/controller_methods'
|
3
|
+
require 'pulse/rails/action_controller_catcher'
|
4
|
+
require 'pulse/rails/error_lookup'
|
5
|
+
require 'pulse/rails/javascript_notifier'
|
6
|
+
|
7
|
+
module Pulse
|
8
|
+
module Rails
|
9
|
+
def self.initialize
|
10
|
+
if defined?(ActionController::Base)
|
11
|
+
ActionController::Base.send(:include, Pulse::Rails::ActionControllerCatcher)
|
12
|
+
ActionController::Base.send(:include, Pulse::Rails::ErrorLookup)
|
13
|
+
ActionController::Base.send(:include, Pulse::Rails::ControllerMethods)
|
14
|
+
ActionController::Base.send(:include, Pulse::Rails::JavascriptNotifier)
|
15
|
+
end
|
16
|
+
|
17
|
+
rails_logger = if defined?(::Rails.logger)
|
18
|
+
::Rails.logger
|
19
|
+
elsif defined?(RAILS_DEFAULT_LOGGER)
|
20
|
+
RAILS_DEFAULT_LOGGER
|
21
|
+
end
|
22
|
+
|
23
|
+
if defined?(::Rails.configuration) && ::Rails.configuration.respond_to?(:middleware)
|
24
|
+
::Rails.configuration.middleware.insert_after 'ActionController::Failsafe',
|
25
|
+
Pulse::Rack
|
26
|
+
::Rails.configuration.middleware.insert_after 'Rack::Lock',
|
27
|
+
Pulse::UserInformer
|
28
|
+
end
|
29
|
+
|
30
|
+
Pulse.configure(true) do |config|
|
31
|
+
config.logger = rails_logger
|
32
|
+
config.environment_name = defined?(::Rails.env) && ::Rails.env || defined?(RAILS_ENV) && RAILS_ENV
|
33
|
+
config.project_root = defined?(::Rails.root) && ::Rails.root || defined?(RAILS_ROOT) && RAILS_ROOT
|
34
|
+
config.framework = defined?(::Rails.version) && "Rails: #{::Rails.version}" || defined?(::Rails::VERSION::STRING) && "Rails: #{::Rails::VERSION::STRING}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
Pulse::Rails.initialize
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'projectlocker_pulse'
|
2
|
+
require File.join(File.dirname(__FILE__), 'shared_tasks')
|
3
|
+
|
4
|
+
puts "\n\nLOADED RAILS3 TASKS\n\n"
|
5
|
+
namespace :pulse do
|
6
|
+
desc "Verify your gem installation by sending a test exception to the pulse service"
|
7
|
+
task :test => [:environment] do
|
8
|
+
Rails.logger = defined?(ActiveSupport::TaggedLogging) ?
|
9
|
+
ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)) :
|
10
|
+
Logger.new(STDOUT)
|
11
|
+
|
12
|
+
def wait_for_threads
|
13
|
+
# if using multiple threads, we have to wait for
|
14
|
+
# them to finish
|
15
|
+
if GirlFriday.status.empty?
|
16
|
+
Thread.list.each do |thread|
|
17
|
+
thread.join unless thread == Thread.current
|
18
|
+
end
|
19
|
+
else
|
20
|
+
GirlFriday.shutdown!
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Rails.logger.level = Logger::DEBUG
|
25
|
+
Pulse.configure(true) do |config|
|
26
|
+
config.logger = Rails.logger
|
27
|
+
end
|
28
|
+
|
29
|
+
require './app/controllers/application_controller'
|
30
|
+
|
31
|
+
class PulseTestingException < RuntimeError; end
|
32
|
+
|
33
|
+
unless Pulse.configuration.api_key
|
34
|
+
puts "Pulse needs an API key configured! Check the README to see how to add it."
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
|
38
|
+
Pulse.configuration.development_environments = []
|
39
|
+
|
40
|
+
puts "Configuration:"
|
41
|
+
Pulse.configuration.to_hash.each do |key, value|
|
42
|
+
puts sprintf("%25s: %s", key.to_s, value.inspect.slice(0, 55))
|
43
|
+
end
|
44
|
+
|
45
|
+
unless defined?(ApplicationController)
|
46
|
+
puts "No ApplicationController found"
|
47
|
+
exit
|
48
|
+
end
|
49
|
+
|
50
|
+
puts 'Setting up the Controller.'
|
51
|
+
class ApplicationController
|
52
|
+
# This is to bypass any filters that may prevent access to the action.
|
53
|
+
prepend_before_filter :test_pulse
|
54
|
+
def test_pulse
|
55
|
+
puts "Raising '#{exception_class.name}' to simulate application failure."
|
56
|
+
raise exception_class.new, 'Testing Pulse via "rake pulse:test". If you can see this, it works.'
|
57
|
+
end
|
58
|
+
|
59
|
+
# def rescue_action(exception)
|
60
|
+
# rescue_action_in_public exception
|
61
|
+
# end
|
62
|
+
|
63
|
+
# Ensure we actually have an action to go to.
|
64
|
+
def verify; end
|
65
|
+
|
66
|
+
# def consider_all_requests_local
|
67
|
+
# false
|
68
|
+
# end
|
69
|
+
|
70
|
+
# def local_request?
|
71
|
+
# false
|
72
|
+
# end
|
73
|
+
|
74
|
+
def exception_class
|
75
|
+
exception_name = ENV['EXCEPTION'] || "PulseTestingException"
|
76
|
+
Object.const_get(exception_name)
|
77
|
+
rescue
|
78
|
+
Object.const_set(exception_name, Class.new(Exception))
|
79
|
+
end
|
80
|
+
|
81
|
+
def logger
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
end
|
85
|
+
class PulseVerificationController < ApplicationController; end
|
86
|
+
|
87
|
+
Rails.application.routes_reloader.execute_if_updated
|
88
|
+
Rails.application.routes.draw do
|
89
|
+
match 'verify' => 'application#verify', :as => 'verify'
|
90
|
+
end
|
91
|
+
|
92
|
+
puts 'Processing request.'
|
93
|
+
env = Rack::MockRequest.env_for("/verify")
|
94
|
+
|
95
|
+
Rails.application.call(env)
|
96
|
+
|
97
|
+
wait_for_threads
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'projectlocker_pulse'
|
2
|
+
require 'rails'
|
3
|
+
|
4
|
+
module Pulse
|
5
|
+
class Railtie < ::Rails::Railtie
|
6
|
+
rake_tasks do
|
7
|
+
require 'pulse/rake_handler'
|
8
|
+
require 'pulse/rails3_tasks'
|
9
|
+
#load "#{File.dirname(__FILE__)}/rails3_tasks.rb"
|
10
|
+
end
|
11
|
+
|
12
|
+
initializer "pulse.use_rack_middleware" do |app|
|
13
|
+
app.config.middleware.insert 0, "Pulse::UserInformer"
|
14
|
+
app.config.middleware.insert_after "Pulse::UserInformer","Pulse::Rack"
|
15
|
+
end
|
16
|
+
|
17
|
+
config.after_initialize do
|
18
|
+
Pulse.configure(true) do |config|
|
19
|
+
config.logger ||= config.async? ? ::Logger.new(STDERR) : ::Rails.logger
|
20
|
+
config.environment_name ||= ::Rails.env
|
21
|
+
config.project_root ||= ::Rails.root
|
22
|
+
config.framework = "Rails: #{::Rails::VERSION::STRING}"
|
23
|
+
end
|
24
|
+
|
25
|
+
ActiveSupport.on_load(:action_controller) do
|
26
|
+
# Lazily load action_controller methods
|
27
|
+
#
|
28
|
+
require 'pulse/rails/javascript_notifier'
|
29
|
+
require 'pulse/rails/controller_methods'
|
30
|
+
|
31
|
+
include Pulse::Rails::JavascriptNotifier
|
32
|
+
include Pulse::Rails::ControllerMethods
|
33
|
+
end
|
34
|
+
|
35
|
+
if defined?(::ActionDispatch::DebugExceptions)
|
36
|
+
# We should catch the exceptions in ActionDispatch::DebugExceptions in Rails 3.2.x.
|
37
|
+
#
|
38
|
+
require 'pulse/rails/middleware/exceptions_catcher'
|
39
|
+
::ActionDispatch::DebugExceptions.send(:include,Pulse::Rails::Middleware::ExceptionsCatcher)
|
40
|
+
elsif defined?(::ActionDispatch::ShowExceptions)
|
41
|
+
# ActionDispatch::DebugExceptions is not defined in Rails 3.0.x and 3.1.x so
|
42
|
+
# catch the exceptions in ShowExceptions.
|
43
|
+
#
|
44
|
+
require 'pulse/rails/middleware/exceptions_catcher'
|
45
|
+
::ActionDispatch::ShowExceptions.send(:include,Pulse::Rails::Middleware::ExceptionsCatcher)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# Patch Rake::Application to handle errors with Pulse
|
2
|
+
module Pulse::RakeHandler
|
3
|
+
def self.included(klass)
|
4
|
+
klass.class_eval do
|
5
|
+
include Rake087Methods unless defined?(Rake::VERSION) && Rake::VERSION >= '0.9.0'
|
6
|
+
alias_method :display_error_message_without_pulse, :display_error_message
|
7
|
+
alias_method :display_error_message, :display_error_message_with_pulse
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def display_error_message_with_pulse(ex)
|
12
|
+
if Pulse.sender && Pulse.configuration &&
|
13
|
+
(Pulse.configuration.rescue_rake_exceptions ||
|
14
|
+
(Pulse.configuration.rescue_rake_exceptions===nil && !self.tty_output?))
|
15
|
+
|
16
|
+
Pulse.notify_or_ignore(ex, :component => reconstruct_command_line, :cgi_data => ENV)
|
17
|
+
end
|
18
|
+
|
19
|
+
display_error_message_without_pulse(ex)
|
20
|
+
end
|
21
|
+
|
22
|
+
def reconstruct_command_line
|
23
|
+
"rake #{ARGV.join( ' ' )}"
|
24
|
+
end
|
25
|
+
|
26
|
+
# This module brings Rake 0.8.7 error handling to 0.9.0 standards
|
27
|
+
module Rake087Methods
|
28
|
+
# Method taken from Rake 0.9.0 source
|
29
|
+
#
|
30
|
+
# Provide standard exception handling for the given block.
|
31
|
+
def standard_exception_handling
|
32
|
+
begin
|
33
|
+
yield
|
34
|
+
rescue SystemExit => ex
|
35
|
+
# Exit silently with current status
|
36
|
+
raise
|
37
|
+
rescue OptionParser::InvalidOption => ex
|
38
|
+
$stderr.puts ex.message
|
39
|
+
exit(false)
|
40
|
+
rescue Exception => ex
|
41
|
+
# Exit with error message
|
42
|
+
display_error_message(ex)
|
43
|
+
exit(false)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Method extracted from Rake 0.8.7 source
|
48
|
+
def display_error_message(ex)
|
49
|
+
$stderr.puts "#{name} aborted!"
|
50
|
+
$stderr.puts ex.message
|
51
|
+
if options.trace
|
52
|
+
$stderr.puts ex.backtrace.join("\n")
|
53
|
+
else
|
54
|
+
$stderr.puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
|
55
|
+
$stderr.puts "(See full trace by running task with --trace)"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
Rake.application.instance_eval do
|
62
|
+
class << self
|
63
|
+
include Pulse::RakeHandler
|
64
|
+
end
|
65
|
+
end
|
data/lib/pulse/sender.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
module Pulse
|
2
|
+
# Sends out the notice to Pulse
|
3
|
+
class Sender
|
4
|
+
|
5
|
+
NOTICES_URI = '/notifier_api/v2/notices/'.freeze
|
6
|
+
HTTP_ERRORS = [Timeout::Error,
|
7
|
+
Errno::EINVAL,
|
8
|
+
Errno::ECONNRESET,
|
9
|
+
EOFError,
|
10
|
+
Net::HTTPBadResponse,
|
11
|
+
Net::HTTPHeaderSyntaxError,
|
12
|
+
Net::ProtocolError,
|
13
|
+
Errno::ECONNREFUSED].freeze
|
14
|
+
|
15
|
+
def initialize(options = {})
|
16
|
+
[ :proxy_host,
|
17
|
+
:proxy_port,
|
18
|
+
:proxy_user,
|
19
|
+
:proxy_pass,
|
20
|
+
:protocol,
|
21
|
+
:host,
|
22
|
+
:port,
|
23
|
+
:secure,
|
24
|
+
:use_system_ssl_cert_chain,
|
25
|
+
:http_open_timeout,
|
26
|
+
:http_read_timeout
|
27
|
+
].each do |option|
|
28
|
+
instance_variable_set("@#{option}", options[option])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Sends the notice data off to Pulse for processing.
|
33
|
+
#
|
34
|
+
# @param [Notice or String] notice The notice to be sent off
|
35
|
+
def send_to_pulse(notice)
|
36
|
+
data = notice.respond_to?(:to_xml) ? notice.to_xml : notice
|
37
|
+
http = setup_http_connection
|
38
|
+
|
39
|
+
response = begin
|
40
|
+
http.post(url.path, data, HEADERS)
|
41
|
+
rescue *HTTP_ERRORS => e
|
42
|
+
log :level => :error,
|
43
|
+
:message => "Unable to contact the Pulse server. HTTP Error=#{e}"
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
|
47
|
+
case response
|
48
|
+
when Net::HTTPSuccess then
|
49
|
+
log :level => :info,
|
50
|
+
:message => "Success: #{response.class}",
|
51
|
+
:response => response
|
52
|
+
else
|
53
|
+
log :level => :error,
|
54
|
+
:message => "Failure: #{response.class}",
|
55
|
+
:response => response,
|
56
|
+
:notice => notice
|
57
|
+
end
|
58
|
+
|
59
|
+
if response && response.respond_to?(:body)
|
60
|
+
error_id = response.body.match(%r{<id[^>]*>(.*?)</id>})
|
61
|
+
error_id[1] if error_id
|
62
|
+
end
|
63
|
+
rescue => e
|
64
|
+
log :level => :error,
|
65
|
+
:message => "[Pulse::Sender#send_to_pulse] Cannot send notification. Error: #{e.class}" +
|
66
|
+
" - #{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}"
|
67
|
+
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
|
71
|
+
attr_reader :proxy_host,
|
72
|
+
:proxy_port,
|
73
|
+
:proxy_user,
|
74
|
+
:proxy_pass,
|
75
|
+
:protocol,
|
76
|
+
:host,
|
77
|
+
:port,
|
78
|
+
:secure,
|
79
|
+
:use_system_ssl_cert_chain,
|
80
|
+
:http_open_timeout,
|
81
|
+
:http_read_timeout
|
82
|
+
|
83
|
+
alias_method :secure?, :secure
|
84
|
+
alias_method :use_system_ssl_cert_chain?, :use_system_ssl_cert_chain
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def url
|
89
|
+
URI.parse("#{protocol}://#{host}:#{port}").merge(NOTICES_URI)
|
90
|
+
end
|
91
|
+
|
92
|
+
def log(opts = {})
|
93
|
+
opts[:logger].send opts[:level], LOG_PREFIX + opts[:message] if opts[:logger]
|
94
|
+
Pulse.report_environment_info
|
95
|
+
Pulse.report_response_body(opts[:response].body) if opts[:response] && opts[:response].respond_to?(:body)
|
96
|
+
Pulse.report_notice(opts[:notice]) if opts[:notice]
|
97
|
+
end
|
98
|
+
|
99
|
+
def logger
|
100
|
+
Pulse.logger
|
101
|
+
end
|
102
|
+
|
103
|
+
def setup_http_connection
|
104
|
+
http =
|
105
|
+
Net::HTTP::Proxy(proxy_host, proxy_port, proxy_user, proxy_pass).
|
106
|
+
new(url.host, url.port)
|
107
|
+
|
108
|
+
http.read_timeout = http_read_timeout
|
109
|
+
http.open_timeout = http_open_timeout
|
110
|
+
|
111
|
+
if secure?
|
112
|
+
http.use_ssl = true
|
113
|
+
|
114
|
+
http.ca_file = Pulse.configuration.ca_bundle_path
|
115
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
116
|
+
else
|
117
|
+
http.use_ssl = false
|
118
|
+
end
|
119
|
+
|
120
|
+
http
|
121
|
+
rescue => e
|
122
|
+
log :level => :error,
|
123
|
+
:message => "[Pulse::Sender#setup_http_connection] Failure initializing the HTTP connection.\n" +
|
124
|
+
"Error: #{e.class} - #{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}"
|
125
|
+
raise e
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
namespace :pulse do
|
2
|
+
desc "Notify Pulse of a new deploy."
|
3
|
+
task :deploy do
|
4
|
+
require 'pulse_tasks'
|
5
|
+
|
6
|
+
if defined?(Rails.root)
|
7
|
+
initializer_file = Rails.root.join('config', 'initializers','pulse.rb')
|
8
|
+
|
9
|
+
if initializer_file.exist?
|
10
|
+
load initializer_file
|
11
|
+
else
|
12
|
+
Rake::Task[:environment].invoke
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
PulseTasks.deploy(:rails_env => ENV['TO'],
|
17
|
+
:scm_revision => ENV['REVISION'],
|
18
|
+
:scm_repository => ENV['REPO'],
|
19
|
+
:local_username => ENV['USER'],
|
20
|
+
:api_key => ENV['API_KEY'],
|
21
|
+
:dry_run => ENV['DRY_RUN'])
|
22
|
+
end
|
23
|
+
|
24
|
+
task :log_stdout do
|
25
|
+
require 'logger'
|
26
|
+
RAILS_DEFAULT_LOGGER = Logger.new(STDOUT)
|
27
|
+
end
|
28
|
+
|
29
|
+
namespace :heroku do
|
30
|
+
desc "Install Heroku deploy notifications addon"
|
31
|
+
task :add_deploy_notification => [:environment] do
|
32
|
+
|
33
|
+
def heroku_var(var)
|
34
|
+
`heroku config | grep -E "#{var.upcase}" | awk '{ print $3; }'`.strip
|
35
|
+
end
|
36
|
+
|
37
|
+
heroku_rails_env = heroku_var("rails_env")
|
38
|
+
heroku_api_key = heroku_var("pulse_api_key").split.find {|x| x unless x.blank?} ||
|
39
|
+
Pulse.configuration.api_key
|
40
|
+
|
41
|
+
command = %Q(heroku addons:add deployhooks:http --url="http://errors.projectlocker.com/deploys.txt?deploy[rails_env]=#{heroku_rails_env}&api_key=#{heroku_api_key}")
|
42
|
+
|
43
|
+
puts "\nRunning:\n#{command}\n"
|
44
|
+
puts `#{command}`
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/pulse/tasks.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'projectlocker_pulse'
|
2
|
+
require File.join(File.dirname(__FILE__), 'shared_tasks')
|
3
|
+
|
4
|
+
namespace :pulse do
|
5
|
+
desc "Verify your gem installation by sending a test exception to the pulse service"
|
6
|
+
task :test => ['pulse:log_stdout', :environment] do
|
7
|
+
RAILS_DEFAULT_LOGGER.level = Logger::DEBUG
|
8
|
+
|
9
|
+
require 'action_controller/test_process'
|
10
|
+
|
11
|
+
Dir["app/controllers/application*.rb"].each { |file| require(File.expand_path(file)) }
|
12
|
+
|
13
|
+
class PulseTestingException < RuntimeError; end
|
14
|
+
|
15
|
+
unless Pulse.configuration.api_key
|
16
|
+
puts "Pulse needs an API key configured! Check the README to see how to add it."
|
17
|
+
exit
|
18
|
+
end
|
19
|
+
|
20
|
+
Pulse.configuration.development_environments = []
|
21
|
+
|
22
|
+
catcher = Pulse::Rails::ActionControllerCatcher
|
23
|
+
in_controller = ApplicationController.included_modules.include?(catcher)
|
24
|
+
in_base = ActionController::Base.included_modules.include?(catcher)
|
25
|
+
if !in_controller || !in_base
|
26
|
+
puts "Rails initialization did not occur"
|
27
|
+
exit
|
28
|
+
end
|
29
|
+
|
30
|
+
puts "Configuration:"
|
31
|
+
Pulse.configuration.to_hash.each do |key, value|
|
32
|
+
puts sprintf("%25s: %s", key.to_s, value.inspect.slice(0, 55))
|
33
|
+
end
|
34
|
+
|
35
|
+
unless defined?(ApplicationController)
|
36
|
+
puts "No ApplicationController found"
|
37
|
+
exit
|
38
|
+
end
|
39
|
+
|
40
|
+
puts 'Setting up the Controller.'
|
41
|
+
class ApplicationController
|
42
|
+
# This is to bypass any filters that may prevent access to the action.
|
43
|
+
prepend_before_filter :test_pulse
|
44
|
+
def test_pulse
|
45
|
+
puts "Raising '#{exception_class.name}' to simulate application failure."
|
46
|
+
raise exception_class.new, 'Testing Pulse via "rake pulse:test". If you can see this, it works.'
|
47
|
+
end
|
48
|
+
|
49
|
+
def rescue_action(exception)
|
50
|
+
rescue_action_in_public exception
|
51
|
+
end
|
52
|
+
|
53
|
+
# Ensure we actually have an action to go to.
|
54
|
+
def verify; end
|
55
|
+
|
56
|
+
def consider_all_requests_local
|
57
|
+
false
|
58
|
+
end
|
59
|
+
|
60
|
+
def local_request?
|
61
|
+
false
|
62
|
+
end
|
63
|
+
|
64
|
+
def exception_class
|
65
|
+
exception_name = ENV['EXCEPTION'] || "PulseTestingException"
|
66
|
+
exception_name.split("::").inject(Object){|klass, name| klass.const_get(name)}
|
67
|
+
rescue
|
68
|
+
Object.const_set(exception_name.gsub(/:+/, "_"), Class.new(Exception))
|
69
|
+
end
|
70
|
+
|
71
|
+
def logger
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
class PulseVerificationController < ApplicationController; end
|
76
|
+
|
77
|
+
puts 'Processing request.'
|
78
|
+
request = ActionController::TestRequest.new("REQUEST_URI" => "/pulse_verification_controller")
|
79
|
+
response = ActionController::TestResponse.new
|
80
|
+
PulseVerificationController.new.process(request, response)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Pulse
|
2
|
+
class UserInformer
|
3
|
+
def initialize(app)
|
4
|
+
@app = app
|
5
|
+
end
|
6
|
+
|
7
|
+
def replacement(with)
|
8
|
+
Pulse.configuration.user_information.gsub(/\{\{\s*error_id\s*\}\}/, with.to_s)
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
status, headers, body = @app.call(env)
|
13
|
+
if env['pulse.error_id'] && Pulse.configuration.user_information
|
14
|
+
new_body = []
|
15
|
+
replace = replacement(env['pulse.error_id'])
|
16
|
+
body.each do |chunk|
|
17
|
+
new_body << chunk.gsub("<!-- PULSE ERROR -->", replace)
|
18
|
+
end
|
19
|
+
body.close if body.respond_to?(:close)
|
20
|
+
headers['Content-Length'] = new_body.sum(&:bytesize).to_s
|
21
|
+
body = new_body
|
22
|
+
end
|
23
|
+
[status, headers, body]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# stolen from ActiveSupport
|
2
|
+
|
3
|
+
class Object
|
4
|
+
def blank?
|
5
|
+
respond_to?(:empty?) ? empty? : !self
|
6
|
+
end
|
7
|
+
|
8
|
+
def present?
|
9
|
+
!blank?
|
10
|
+
end
|
11
|
+
|
12
|
+
def presence
|
13
|
+
self if present?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class NilClass
|
18
|
+
def blank?
|
19
|
+
true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class FalseClass
|
24
|
+
def blank?
|
25
|
+
true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class TrueClass
|
30
|
+
def blank?
|
31
|
+
false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Array
|
36
|
+
alias_method :blank?, :empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
class Hash
|
40
|
+
alias_method :blank?, :empty?
|
41
|
+
end
|
42
|
+
|
43
|
+
class String
|
44
|
+
def blank?
|
45
|
+
self !~ /[^[:space:]]/
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Numeric
|
50
|
+
def blank?
|
51
|
+
false
|
52
|
+
end
|
53
|
+
end
|
data/lib/pulse_tasks.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
# Capistrano tasks for notifying Pulse of deploys
|
5
|
+
module PulseTasks
|
6
|
+
|
7
|
+
# Alerts Pulse of a deploy.
|
8
|
+
#
|
9
|
+
# @param [Hash] opts Data about the deploy that is set to Pulse
|
10
|
+
#
|
11
|
+
# @option opts [String] :api_key Api key of you Pulse application
|
12
|
+
# @option opts [String] :rails_env Environment of the deploy (production, staging)
|
13
|
+
# @option opts [String] :scm_revision The given revision/sha that is being deployed
|
14
|
+
# @option opts [String] :scm_repository Address of your repository to help with code lookups
|
15
|
+
# @option opts [String] :local_username Who is deploying
|
16
|
+
def self.deploy(opts = {})
|
17
|
+
api_key = opts.delete(:api_key) || Pulse.configuration.api_key
|
18
|
+
if api_key.blank?
|
19
|
+
puts "I don't seem to be configured with an API key. Please check your configuration."
|
20
|
+
return false
|
21
|
+
end
|
22
|
+
|
23
|
+
if opts[:rails_env].blank?
|
24
|
+
puts "I don't know to which Rails environment you are deploying (use the TO=production option)."
|
25
|
+
return false
|
26
|
+
end
|
27
|
+
|
28
|
+
dry_run = opts.delete(:dry_run)
|
29
|
+
params = {'api_key' => api_key}
|
30
|
+
opts.each {|k,v| params["deploy[#{k}]"] = v }
|
31
|
+
|
32
|
+
host = Pulse.configuration.host || 'errors.projectlocker.com'
|
33
|
+
port = Pulse.configuration.port
|
34
|
+
|
35
|
+
proxy = Net::HTTP.Proxy(Pulse.configuration.proxy_host,
|
36
|
+
Pulse.configuration.proxy_port,
|
37
|
+
Pulse.configuration.proxy_user,
|
38
|
+
Pulse.configuration.proxy_pass)
|
39
|
+
http = proxy.new(host, port)
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
# Handle Security
|
44
|
+
if Pulse.configuration.secure?
|
45
|
+
http.use_ssl = true
|
46
|
+
http.ca_file = Pulse.configuration.ca_bundle_path
|
47
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
48
|
+
end
|
49
|
+
|
50
|
+
post = Net::HTTP::Post.new("/deploys.txt")
|
51
|
+
post.set_form_data(params)
|
52
|
+
|
53
|
+
if dry_run
|
54
|
+
puts http.inspect, params.inspect
|
55
|
+
return true
|
56
|
+
else
|
57
|
+
response = http.request(post)
|
58
|
+
|
59
|
+
puts response.body
|
60
|
+
return Net::HTTPSuccess === response
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|