Last active
November 13, 2015 17:43
-
-
Save mwpastore/f9fd4d4274a6fa72e8b5 to your computer and use it in GitHub Desktop.
Rakefile task for Puma with Rerun
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'bundler/setup' | |
namespace :server do | |
desc 'Start the Puma webserver in development mode (rerun, debug).' | |
task :debug do | |
# Delay requiring these to avoid Rake failures in production. | |
require 'file-tail' | |
require 'rerun' | |
require 'tempfile' | |
class Rerun::PumaRunner < Rerun::Runner | |
alias_method :stop_without_signal, :stop | |
def stop_with_signal(signal = nil) | |
_signal = @options[:signal] | |
@options[:signal] = signal | |
stop_without_signal | |
@options[:signal] = _signal | |
end | |
alias_method :stop, :stop_with_signal | |
end | |
rerun_options = %w'--restart --signal USR2' # or USR1 for phased restarts | |
rerun_command = 'puma --debug --quiet' \ | |
' --redirect-stdout %<stdout>s' \ | |
' --redirect-stderr %<stderr>s' \ | |
' --redirect-append' | |
fail 'Must run task from top-level directory' \ | |
unless Dir.pwd == __dir__ | |
# Redirect Puma output to prevent errors writing to non-existent TTY. | |
tmpfile = Tempfile.new %w[rerun-puma- .log] | |
last_line = -1 | |
at_exit do | |
# Make sure we show all lines of Puma output if the TTY is still there. | |
File.open(tmpfile) do |logfile| | |
logfile.extend File::Tail | |
logfile.return_if_eof = true | |
logfile.forward last_line + 1 | |
logfile.tail { |line| puts line } | |
end | |
# Close and delete the temporary file. | |
tmpfile.close! | |
end | |
runner = Rerun::PumaRunner.new \ | |
rerun_command % { | |
:stdout => tmpfile.path, | |
:stderr => tmpfile.path | |
}, | |
Rerun::Options.parse(rerun_options) | |
trap 'SIGHUP' do | |
unless $stdin.tty? and not $stdin.closed? | |
# Prevent Ruby from trying to write to the now non-existent TTY. | |
[$stdout, $stderr].each { |fd| fd.reopen(File::NULL, 'w') } | |
end | |
runner.stop | |
exit false | |
end | |
runner.start | |
runner.join # this does not block | |
File.open(tmpfile) do |logfile| | |
logfile.extend File::Tail | |
logfile.interval = 10 | |
logfile.tail do |line| | |
last_line += 1 | |
puts line | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is a lot of code for something ostensibly simple:
SIGUSR2
to tell Puma when to restart the workers to pull in changes.You'd think, "Just
rerun -- puma
, right?" Unfortunately, a lot of these things are mutually exclusive due to the way rerun works with a custom signal, quirks in the way Puma forks processes, spawns threads, and dups file descriptors, and Ruby's tendency to raise exceptions when stdout and/or stderr go away (exceptions that are raised from deep within rerun and Puma and therefore difficult for the humble user to catch and handle while still terminating things gracefully).Again, the above is a lot of code, but it meets all of my requirements. Suggestions welcome.