namespace :psql_backups do
class String def black; "\e[30m#{self}\e[0m" end def red; "\e[31m#{self}\e[0m" end def green; "\e[32m#{self}\e[0m" end def brown; "\e[33m#{self}\e[0m" end def blue; "\e[34m#{self}\e[0m" end def magenta; "\e[35m#{self}\e[0m" end def cyan; "\e[36m#{self}\e[0m" end def gray; "\e[37m#{self}\e[0m" end def dark_gray; "\e[1:30m#{self}\e[0m" end def white; "\e[1;37m#{self}\e[0m" end def bg_black; "\e[40m#{self}\e[0m" end def bg_red; "\e[41m#{self}\e[0m" end def bg_green; "\e[42m#{self}\e[0m" end def bg_brown; "\e[43m#{self}\e[0m" end def bg_blue; "\e[44m#{self}\e[0m" end def bg_magenta; "\e[45m#{self}\e[0m" end def bg_cyan; "\e[46m#{self}\e[0m" end def bg_gray; "\e[47m#{self}\e[0m" end def bold; "\e[1m#{self}\e[22m" end def italic; "\e[3m#{self}\e[23m" end def underline; "\e[4m#{self}\e[24m" end def blink; "\e[5m#{self}\e[25m" end def reverse_color; "\e[7m#{self}\e[27m" end end desc "Backup database" task :backup, [:tag] => [:environment] do |t, args| raise "Can only be run in development environment" unless Rails.env.development? raise "Can only be run if configured for local database" unless ActiveRecord::Base.connection.raw_connection.conninfo_hash[:host].nil? raise "Invalid tag. Tags have to purely alphanumeric (no special characters)" unless args[:tag].nil? || /^[a-z0-9]+$/.match?(args[:tag]) dbname = ActiveRecord::Base.connection.raw_connection.conninfo_hash[:dbname] backup_dbname = "#{ dbname }_#{ args[:tag] || Time.current.strftime("%Y%m%d%H%M%S") }" ActiveRecord::Base.connection.disconnect! `psql -c "alter database #{ dbname } rename to #{ backup_dbname };"` `psql -c "create database #{ dbname } template #{ backup_dbname };"` end desc "Show backups" task list: :environment do raise "Can only be run in development environment" unless Rails.env.development? raise "Can only be run if configured for local database" unless ActiveRecord::Base.connection.raw_connection.conninfo_hash[:host].nil? dbname = ActiveRecord::Base.connection.raw_connection.conninfo_hash[:dbname] lines = `psql -c "\\l" | grep #{ dbname }_`.split("\n") dbnames = lines.map{|l| l.split('|').first.strip } puts "Found #{dbnames.count} backup(s):" dbnames.each do |d| print "[#{dbname}_]".gray.italic puts d.sub("#{dbname}_", "").white end end desc "Restore from backup" task :restore, [:tag] => [:environment] do |t, args| raise "Can only be run in development environment" unless Rails.env.development? raise "Can only be run if configured for local database" unless ActiveRecord::Base.connection.raw_connection.conninfo_hash[:host].nil? raise "No tag specified" if args[:tag].nil? dbname = ActiveRecord::Base.connection.raw_connection.conninfo_hash[:dbname] lines = `psql -c "\\l" | grep #{ dbname }_`.split("\n") tags = lines.map{|l| l.split('|').first.strip.sub("#{dbname}_", '') } raise "Tag not found in backups" unless tags.include?(args[:tag]) puts "Your existing database will be replaced. Are you sure you want to continue? [y/N]" input = STDIN.gets.chomp raise "User abort" unless input.downcase == "y" restore_dbname = "#{ dbname }_#{ args[:tag] }" backup_dbname = "#{ dbname }_auto_#{ Time.current.strftime("%Y%m%d%H%M%S") }" ActiveRecord::Base.connection.disconnect! `psql -c "alter database #{ dbname } rename to #{ backup_dbname };"` `psql -c "create database #{ dbname } template #{ restore_dbname };"` end desc "Clear auto backups" task clear: :environment do raise "Can only be run in development environment" unless Rails.env.development? raise "Can only be run if configured for local database" unless ActiveRecord::Base.connection.raw_connection.conninfo_hash[:host].nil? puts "All your auto-backups will be cleared. Are you sure you want to continue? [y/N]" input = STDIN.gets.chomp raise "User abort" unless input.downcase == "y" dbname = ActiveRecord::Base.connection.raw_connection.conninfo_hash[:dbname] lines = `psql -c "\\l" | grep #{ dbname }_auto_`.split("\n") backups = lines.map{|l| l.split('|').first.strip} ActiveRecord::Base.connection.disconnect! backups.each do |backup_dbname| `psql -c "drop database #{backup_dbname};"` end end
end