Skip to content

Instantly share code, notes, and snippets.

@HK49
Last active July 7, 2017 14:02
Show Gist options
  • Save HK49/412294c869489012137a18f9fd24d6c2 to your computer and use it in GitHub Desktop.
Save HK49/412294c869489012137a18f9fd24d6c2 to your computer and use it in GitHub Desktop.
Simple Rake task for launching Puma server with Postgresql and Guard.
def puma_pidfile # puma pid file path
@puma_pidfile = ENV['PIDFILE'] || "tmp/pids/puma.pid"
end
def puma_port # port where puma runs
@puma_port = ENV['PORT'] || "8080"
end
namespace :guard do
desc 'Starts guard *with* puma server'
task :start => :environment do
arg = {}
o = OptionParser.new # 'cause namespace:task'[:opt1, :opt2]' is ugly for me
o.banner = "Usage: rake [task] -- [options]"
# options can be given even from parent task(what if two child tasks with same opts?)
o.on("-c", "--clear", "The shell will be cleared after each change") {
arg[:c] = "--clear"
}
o.on("-n f", "--notify false", "System notifications will be disabled") {
arg[:nf] = "--notify false"
}
o.on("-d", "--debug", "Guard will display debug information") {
arg[:d] = "--debug"
} # there are more modes in guard - add if needed
o.on("--trace", "Runs rake task with trace") {
Rails.logger.level = Logger::DEBUG
}
o.on("-h", "--help", "Prints this help") {
puts o
exit(0)
}
args = o.order!(ARGV) {}
o.parse!(args)
trap('INT') {
puts("\nRake: INT signal, handling inside trap...\n")
sleep(5)
exit(0)
} # exit task witch ^C
begin
arg.empty? ? system("bundle exec guard") : sh("bundle exec guard #{arg.values.join(' ')}")
rescue StandardError => error
error.each { |e|
printf(
"\n#{e.class ? (e.message ? ("#{e.class} #{e.message}") : "#{e.class}") : ''}\n"
)
}
raise
else
printf "Rake: 'guard:start' is complited without errors.\n"
ensure
Rake::Task['puma:kill'].execute
printf("Rake: '#{Rake.application.top_level_tasks.join(', ')}' ended.\n")
end
end
desc 'Restarts guard, puma server *and* pg'
task :restart do |t, arg|
begin
Rake::Task['puma:kill'].execute
Rake::Task['psql:restart'].execute
Rake::Task['guard:start'].execute
end
end
end
desc 'Starts guard *with* puma server'
task :guard => ['psql:start', 'puma:kill', 'guard:start']
namespace :puma do
desc 'Starts postgresql service *and* puma server'
task :start do
trap('INT') {
puts("\nRake: INT signal, handling inside trap...\n")
sleep(5)
exit(0)
} # exit task witch ^C
begin
system("bundle exec puma")
rescue StandardError => error
error.each { |e|
printf(
"#{e.class ? (e.message ? ("#{e.class} #{e.message}") : "#{e.class}") : ''}\n"
)
}
raise
else
printf "Rake: 'puma:start' is complited without errors.\n"
ensure
Rake::Task['puma:kill'].execute
printf("Rake: '#{Rake.application.top_level_tasks.join(', ')}' ended.\n")
end
end
desc 'Terminates puma. Port and Pid file path should be defined in taskfile'
task :kill do
if File.exists?("#{puma_pidfile}")
pid = File.open("#{puma_pidfile}", "rb") { |i| i.read(4).to_i }
system("kill -s SIGTERM #{pid}")
File.delete("#{puma_pidfile}")
end # puma will be terminated corresponding to pid file
n = 0
2.times {
break if `lsof -i TCP:#{puma_port} -t`.empty?
n = n + 1
`ps aux | grep puma | grep -v grep | grep -v tail | awk '{print $2}'`.split("\n").map do |i|
if `lsof -i TCP:#{puma_port} -t`.split("\n").include?(i)
puts("[!] Killing puma with #{i}PID on #{puma_port}PORT...")
system("kill -s SIGTERM #{i}")
elsif `lsof -i TCP:#{puma_port} -t`.split("\n").include?(i) && !!(n > 1)
puts("Rake: puma:kill couldn't kill puma procs on #{puma_port}PORT!\nProceeding with puma:overkill task...")
Rake::Task['puma:overkill'].execute
end
end # each puma process on corresponding port will be terminated
sleep(1)
}
puts("[#{Process.pid}] * Rake: #{puma_port}PORT is empty.") if n < 1
end
desc 'Nukes tcp. Port should be defined in taskfile'
task :overkill do # all processes on #{puma_port} will be killed
2.times {
`lsof -i TCP:#{puma_port} -t`.split("\n").each { |i| system("kill -9 #{i}") }
sleep(1)
} # seems like it needs to be executed twice. 'cause sometimes on first run array has only one value
end
end
desc 'Starts postgresql service *and* puma server'
task :puma do
Rake::Task['psql:start'].execute # Prevent PG::ConnectionBad
Rake::Task['puma:kill'].execute # Prevent Errno::EADDRINUSE - Address already in use
STDOUT.puts "Do you want to guard puma server?(\e[42my\e[0m/\e[41mn\e[0m)\n"
input = ""
begin
Timeout.timeout(15) do
input = STDIN.gets.chomp.upcase
puts "Rake: Starting puma server with guard..."
end
rescue Timeout::Error
puts "Rake: Starting puma server without guard..."
end
/Y/ =~ input ? Rake::Task['guard'].invoke : Rake::Task['puma:start'].execute
end
namespace :psql do
desc 'Starts postgresql service'
task :start do
system("sudo service postgresql start") if `service postgresql status`.include?('down')
Rake::Task['psql:connect'].invoke
end
desc 'Connects ActiveRecord to postgres db'
task :connect => :environment do
unless ActiveRecord::Base.connection.active?
ActiveRecord::Base.establish_connection
printf("[#{Process.pid}] * PG::Connection was dead. Reestablished.")
else
system("echo \"[#{Process.pid}] * PG::Connection is online.\"")
end
end
desc 'Restarts postgresql service'
task :restart do
unless psql_on
begin
Timeout.timeout(15) do
STDOUT.puts "You want to retard inactive server, you sure?(\e[42my\e[0m/\e[41mn\e[0m)\n"
/Y/ =~ STDIN.gets.chomp.upcase ?
system("sudo service postgresql restart")\
: abort("Didn't see \"y\". Aborted.")
end
rescue Timeout::Error
system("sudo service postgresql restart")
end
else
printf "Retarding...\t<- saw the joke?\n"
system("sudo service postgresql restart")
end
end
desc 'Stops postgresql service'
task :stop do
system("sudo service postgresql stop") if !!psql_on
end
desc 'Tells postgresql service status'
task :online? do
puts("#{psql_on}")
end
def psql_on
`service postgresql status`.include?('online') ? true : false
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment