I’ve recently needed a way to migrate multiple databases, all with different connections and migrations, with a single rake task. Our first solution was to just create a new rails project for each database, setup our database.yml file as normal and drop the migrations into db/migrate.
We could have called it there and been done with it. But I’m lazy. Having to go into each directory and run rake db:migrate was not going to let me reach the level of lazy I strive for. Not to mention all of the bloat of having a full rails projects hanging around just for the migrations.
Let’s make it lazy friendly ™.
The Setup
I first created the directory structure that would hold each databases config file and migrations.
migrator
-> db
-> configs
-> migrations
-> logs
For each database that needed to be migrated I created the following files:
migrator/configs/sample_database_1.yml
adapter: mysql
database: sample_database_name
username: supersecret
password: plaintext
host: localhost
Just the standard database.yml file without the development, production, etc top level nodes since this would only be run in one environment.
Folder in migrator/migrations named after the config yml
Using the example above then the folder would be /migrator/migrations/sample_database_1. In that folder we could drop all of our migration files (001_first_migration.rb, 002_second_migration, etc).
The meat of our lazy sandwich
A rake file to run all these migrations.
migrator/Rakefile
require 'active_record'
require 'yaml'
task :default => :migrate
desc "Migrate the database(s) in db/migrations."
task :migrate do
migration_dir = Dir.new(File.join(File.dirname(__FILE__),"db","migrations"))
config_dir = Dir.new(File.join(File.dirname(__FILE__),"db","configs")).path
logs_dir = Dir.new(File.join(File.dirname(__FILE__), "logs")).path
migration_dir.each do |d|
next if d.first == '.'
puts "Migrating '#{d}'..."
begin
ActiveRecord::Base.establish_connection(YAML::load(File.open(File.join(config_dir, "#{d}.yml"))))
ActiveRecord::Base.logger = Logger.new(File.open(File.join(logs_dir, "#{d}.log"), 'a'))
ActiveRecord::Migrator.migrate(File.join(migration_dir.path, d), ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
puts "Done"
rescue Exception => e
puts "\n\n#{e.to_s}\n"
end
end
end
Run It
Now to run our new migrator just go into the migrator directory and run
> rake
You can also pass it a version if you want to run migrate to a certain version. Just keep in mind it will migrate all of the databases to the same version. Great for the solution I needed maybe not great for yours.
For all the lazies out there (unite!) I’ve put together a tar that has the rake file and directory structure already setup. Grab it from here.