Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env ruby
- require 'yaml'
- class UnsupportedDatabaseAdapter < RuntimeError
- attr_reader :adapter
- def initialize(adapter)
- @adapter = adapter
- end
- def message
- "Adapter `#{adapter}` is not supported."
- end
- end
- # If this was a branch checkout
- if ARGV[2] == '1'
- def dump(env, branch_name)
- print "Saving state of #{env} database on '#{branch_name}' branch..."
- if system(dump_cmd(env, branch_name))
- print "done!\n"
- true
- else
- print "failed!\n"
- false
- end
- rescue UnsupportedDatabaseAdapter => e
- puts "\nERROR: #{e.message}"
- end
- def dump_file(database_name, branch_name)
- # Replace any special characters that may cause file system issues
- branch_name = branch_name.gsub(/[^0-9a-z.\-_]/i, '_')
- "#{@dump_folder}/#{database_name}-#{branch_name}"
- end
- def dump_cmd(env, branch_name)
- adapter = @rails_db_config[env]['adapter']
- case adapter
- when 'postgresql'
- pg_dump_cmd(env, branch_name)
- when 'mysql2'
- mysql_dump_cmd(env, branch_name)
- else
- raise UnsupportedDatabaseAdapter.new(adapter)
- end
- end
- def pg_dump_cmd(env, branch_name)
- database_name = @rails_db_config[env]['database']
- %[pg_dump --file="#{dump_file(database_name, branch_name)}" #{database_name}]
- end
- def mysql_dump_cmd(env, branch_name)
- database_name = @rails_db_config[env]['database']
- username = @rails_db_config[env]['username']
- password = @rails_db_config[env]['password']
- %[mysqldump --add-drop-database --user=#{username} --password="#{password}" --databases #{database_name} > "#{dump_file(database_name, branch_name)}"]
- end
- def restore(env, path)
- system(restore_cmd(env, path))
- end
- def restore_cmd(env, path)
- adapter = @rails_db_config[env]['adapter']
- case adapter
- when 'postgresql'
- pg_restore_cmd(env, path)
- when 'mysql2'
- mysql_restore_cmd(env, path)
- else
- raise UnsupportedDatabaseAdapter.new(adapter)
- end
- end
- def pg_restore_cmd(env, path)
- database_name = @rails_db_config[env]['database']
- drop_existing_pg_connections_to_database(database_name)
- drop_pg_database(database_name)
- create_pg_database(database_name)
- %[psql --file="#{path}" #{database_name} > /dev/null]
- end
- def drop_existing_pg_connections_to_database(database_name)
- system(%[psql --command="SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '#{database_name}' AND pid <> pg_backend_pid();" postgres > /dev/null])
- end
- def drop_pg_database(database_name)
- system(%[dropdb #{database_name}])
- end
- def create_pg_database(database_name)
- system(%[createdb #{database_name}])
- end
- def mysql_restore_cmd(env, path)
- database_name = @rails_db_config[env]['database']
- username = @rails_db_config[env]['username']
- password = @rails_db_config[env]['password']
- %[mysql -u #{username} --password="#{password}" #{database_name} < "#{path}"]
- end
- def branches_from_refhead(ref)
- `git show-ref --heads | grep #{ref} | awk '{print $2}'`.split("\n").map{ |b| b.sub(/^refs\/heads\//, '') }
- end
- # Get the current (destination) branch
- @destination_branch = `git rev-parse --abbrev-ref HEAD`.strip
- # Since we're just given a commit ID referencing the branch head we're coming from,
- # it could be at the head of multiple branches. We can assume the source isn't the same as the
- # destination branch, so we can remove that immediately.
- @source_branches = branches_from_refhead(ARGV[0]).reject{ |b| b == @destination_branch }
- @project_root = %x[git rev-parse --show-toplevel].strip
- @dump_folder = "#{@project_root}/.db_branch_dumps"
- # Load Rails DB config and grab database name
- @rails_db_config = YAML.load_file("#{@project_root}/config/database.yml")
- dev_database_name = @rails_db_config['development']['database']
- # Ensure dump directory exists
- unless Dir.exists?(@dump_folder)
- Dir.mkdir @dump_folder
- end
- # Don't do anything if the source and destination branches are the same or nonexistent
- unless @source_branches.include?(@destination_branch) || @source_branches.empty? || (@source_branches | [@destination_branch]).any?{ |b| b == '' }
- # Dump database for source branches
- if @source_branches.all? { |branch| dump('development', branch) }
- # Restore dump from this branch, if it exists
- dump_path = dump_file(dev_database_name, @destination_branch)
- if File.exists?(dump_path)
- print "Restoring #{dev_database_name} to its previous state on this branch..."
- if restore('development', dump_path)
- print "done!\n"
- # If we have a structure.sql file, restore that to the test database,
- # otherwise fall back to rake
- structure_sql = File.join(@project_root, 'db', 'structure.sql')
- if File.exists?(structure_sql)
- print "Restoring test database from structure.sql..."
- print restore('test', structure_sql) ? "done!\n" : "failed!\n"
- elsif File.exists?("#{@project_root}/Rakefile")
- print "Preparing test database..."
- rake_cmd = "rake db:test:prepare"
- if File.exists?("#{@project_root}/bin/rake")
- rake_cmd = "./bin/#{rake_cmd}"
- elsif File.exists?("#{@project_root}/Gemfile")
- rake_cmd = "bundle exec #{rake_cmd}"
- end
- system rake_cmd
- print "done!\n"
- else
- print "No structure.sql or Rakefile detected, skipping test database restoration\n"
- end
- else
- print "failed!\n"
- end
- else
- print "No DB dump for #{dev_database_name} on the '#{@destination_branch}' branch was found!\n"
- print "The state of your database has been saved for when you return to the '#{@source_branches.join('\' or \'')}' branch, but its current state has been left unchanged. You are now free to make changes to it that are specific to this branch, and they will be saved when you checkout a different branch, then restored when you checkout this one again.\n"
- end
- else
- print "Failed to dump database. Halting.\n"
- end
- end
- end
Add Comment
Please, Sign In to add comment