Last active
October 20, 2023 09:49
-
-
Save joelmoss/3574cef393a68a41efdbebe5110c1ad5 to your computer and use it in GitHub Desktop.
Maintain branch specific databases and switch on checkout
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
# frozen_string_literal: true | |
# rubocop:disable Metrics/LineLength | |
require 'yaml' | |
require 'digest' | |
require 'erb' | |
return if ENV['NO_DB_SWITCH'] == '1' | |
# Return if this is not a branch checkout. | |
return if ARGV[2] != '1' | |
def drop_existing_connections_to_database(database_name, port: 5432) | |
system(%[psql -p #{port} --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 branches_from_refhead(ref) | |
`git show-ref --heads | grep #{ref} | awk '{print $2}'`.split("\n").map do |b| | |
b.sub(%r{^refs/heads/}, '') | |
end | |
end | |
def safe_name(name) = Digest::MD5.hexdigest(name) | |
# Get the current (destination) branch | |
dest_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 == dest_branch } | |
project_root = `git rev-parse --show-toplevel`.strip | |
# Load Rails DB config and grab database name | |
tpl = ERB.new(File.read("#{project_root}/config/database.yml")) | |
rails_db_config = YAML.load(tpl.result, aliases: true) | |
dev_db = rails_db_config['development']['database'] | |
dev_db_port = rails_db_config['development'].fetch('port', 5432) | |
# Don't do anything if the source and destination branches are the same or nonexistent | |
return if source_branches.include?(dest_branch) || source_branches.empty? || | |
(source_branches | [dest_branch]).any?('') | |
puts "Switching development database from #{source_branches.join(', ')} to #{dest_branch}..." | |
# Drop connections | |
drop_existing_connections_to_database dev_db, port: dev_db_port | |
# Copy dev DB to source branches | |
source_branches.each do |br| | |
sbr = safe_name(br) | |
print " -- Copying #{dev_db} to #{dev_db}_#{sbr} ..." | |
if `psql -p #{dev_db_port} -tAc "SELECT 1 FROM pg_database WHERE datname='#{dev_db}_#{sbr}'"`.strip == '1' | |
print ' (dropping existing one first)' | |
system %(dropdb -p #{dev_db_port} -f #{dev_db}_#{sbr}) | |
end | |
system "createdb -p #{dev_db_port} -T #{dev_db} #{dev_db}_#{sbr}" | |
puts ' DONE' | |
end | |
s_dest_branch = safe_name(dest_branch) | |
if `psql -p #{dev_db_port} -tAc "SELECT 1 FROM pg_database WHERE datname='#{dev_db}_#{s_dest_branch}'"`.strip == '1' | |
print " -- Dropping #{dev_db} ..." | |
system %(dropdb -p #{dev_db_port} -f #{dev_db}) | |
puts ' DONE' | |
# Rename destination branch DB to dev DB | |
print " -- Renaming #{dev_db}_#{s_dest_branch} to #{dev_db} ..." | |
system %(psql -p #{dev_db_port} --command="ALTER DATABASE #{dev_db}_#{s_dest_branch} RENAME TO #{dev_db};" postgres > /dev/null) | |
puts ' DONE' | |
end | |
# rubocop:enable Metrics/LineLength |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Just call this as part of a post-checkout git hook, and it will maintain independent copies of your Postgresql database for each branch you switch to.