In Rails 5, I am no longer able to use a rake task that migrates files in a different directory. I get the error ActiveRecord::UnknownMigrationVersionError: No migration with version number 20180209214145.
Any idea how I could do this in Rails 5? I think that the line ActiveRecord::Migrator.migrations_paths = ['db/manual_migrate'] no longer works as expected, but I am not quite sure how to fix it.
desc 'Run db:migrate:up on a task we wish to migrate manually (e.g. not on all servers).' \
'VERSION is required.'
task manual_migrate: [:environment, :load_config] do
raise 'VERSION of migration is required for this task!' unless ENV['VERSION']
default_paths = ActiveRecord::Migrator.migrations_paths
ActiveRecord::Migrator.migrations_paths = ['db/manual_migrate']
begin
Rake::Task['db:migrate:up'].invoke
ensure
ActiveRecord::Migrator.migrations_paths = default_paths
end
end
Related
I'm trying to use Sequel to manage migrations for my database, and sometimes, due to feature-branching, there are moments when the database has migrations applied that don't exist in the filesystem.
By default, when I apply migrations with sequel -m on those situations I get this error:
Error: Sequel::Migrator::Error: Applied migration files not in file system
"Migrations" says there's an option for that:
Ignoring missing migrations
In some cases, you may want to allow a migration in the database that does not exist in the filesystem (deploying to an older version of code without running a down migration when deploy auto-migrates, for example). If required, you can pass :allow_missing_migration_files => true as an option. This will stop errors from being raised if there are migrations in the database that do not exist in the filesystem.
Nice!
How do I pass that allow_missing_migration_files option to sequel -m?
I think you'll have to use the Sequel::Migrator API for that. Something like
Sequel::Migrator.run(DB, '/path/to/migrations/dir', allow_missing_migration_files: true)
You can also wrap this behavior in a Rake task, so you don't have to start a console to run migrations.
namespace :db do
desc "Run migrations ignoring the missing ones"
task :migrate_without_missing, [:version] do |t, args|
require "sequel"
Sequel.extension :migration
db = Sequel.connect(ENV.fetch("DATABASE_URL"))
if args[:version]
puts "Migrating to version #{args[:version]}"
Sequel::Migrator.run(db, "db/migrations", target: args[:version].to_i, allow_missing_migration_files: true)
else
puts "Migrating to latest"
Sequel::Migrator.run(db, "db/migrations", allow_missing_migration_files: true)
end
end
end
Then run rake db:migrate_without_missing.
Okay,
I'm sorry if the title is not descriptive enough, but allow me to explain what I want to achieve:
I have a Rails 3 application
During my deploy, it needs to call pg_dump with the correct parameters to restore a backup
The task needs to be ran after the deploy is done but before the migrations.
The problem I have however, is that for this task, I would like to access Rails specific code, which is not working as Capistrano keeps throwing a lot of errors at me like gems not available or module not defined.
This is my Rake task:
namespace :deploy do
namespace :rm do
desc 'Backup the database'
task :backup do
# Generates the command to invoke the Rails runner
# Used by the cfg method to execute the ActiveRecord configuration in the rails config.
def runner
dir = "#{fetch(:deploy_to)}/current"
bundler = "#{SSHKit.config.command_map.prefix[:bundle].first} bundle exec"
env = fetch(:rails_env)
"cd #{dir}; #{bundler} rails r -e #{env}"
end
def cfg(name)
env = fetch(:rails_env)
command = "\"puts ActiveRecord::Base.configurations['#{env}']['#{name}']\""
"#{runner} #{command}"
end
on roles(:db) do
timestamp = Time.now.strftime('%Y%m%d%H%M%S')
backups = File.expand_path(File.join(fetch(:deploy_to), '..', 'backups'))
execute :mkdir, '-p', backups
dump = "PGPASSWORD=`#{cfg('password')}` pg_dump -h `#{cfg'host')}` -U `#{cfg('username')}` `#{cfg('database')}`"
fn = "#{timestamp}_#{fetch(:stage)}.sql.gz"
path = File.join(backups, fn)
execute "#{dump} | gzip > #{path}"
end
end
end
end
In it's current form, it simply generates a string with the runner method and dumps that inside the cfg method.
I tried rewriting the runner method, but for some reason I keep getting the runner --help output from the remote server, but the command being generated in the output is correct, and works locally just fine.
We are using Ruby 2.2.2 and RVM on the remote server.
Is it even possible to do what we are trying to construct together?
I'd suggest writing a rake task inside your Rails app and invoking that from your Capistrano task. This is how the Capistrano rails tasks work.
within release_path do
with rails_env: fetch(:rails_env) do
execute :rake, "db:migrate"
end
end
I see that the Sequel gem supports migrations here, but I don't see any type of generator documented. Does one exist; or should I be manually creating all of my migrations (or alternately creating my own task to generate migrations)?
From the documentation:
Sequel doesn't come with generators that create migrations for you. However, creating a migration is as simple as creating a file with the appropriate filename in your migrations directory that contains a Sequel.migration call.
The contents of the migration file doesn't have to specify a timestamp or index and it's an extremely simple format.
I generally just copy a previous migration (maybe one similar to the migration I'm creating) and alter the filename. See existing migrations with:
$ ls -1 db/migrate/
20170320075430_check_postgres_extensions.rb
...
For running migrations, I use the rake task that's available here.
Sequel has no migration generator. But you can easily write your own, for example using a rake task. Here is one:
# Rakefile
require 'fileutils'
namespace :db do
MIGRATIONS_DIR = 'db/migrations'
desc "generates a migration file with a timestamp and name"
task :generate_migration, :name do |_, args|
args.with_defaults(name: 'migration')
migration_template = <<~MIGRATION
Sequel.migration do
up do
end
down do
end
end
MIGRATION
file_name = "#{Time.now.strftime('%Y%m%d%H%M%S')}_#{args.name}.rb"
FileUtils.mkdir_p(MIGRATIONS_DIR)
File.open(File.join(MIGRATIONS_DIR, file_name), 'w') do |file|
file.write(migration_template)
end
end
end
Then run the rake task like this rake db:generate_migration[my_migration_name]. This command will create a Sequel migration file 20220720233750_my_migration_name.rb in the directory db/migrations.
I dont want Rails 3 to generate my schema every time I do migration. How to properly disable it?
Thanks
For anyone who is still looking for a way to disable the db dump after migration a configuration is now available in rails 4 which can be set to false like this:
config.active_record.dump_schema_after_migration = false
will prevent it. The configuration has been added in this change - https://github.com/rails/rails/pull/13948
Create an application specific task (as Alex Kaushovik suggested) like so...
Create a file lib\tasks\db_schema_override (actual name doesn't matter, you need a .rake file in lib\tasks) with contents as below (credit to Matthew Bass for remove_task)
Rake::TaskManager.class_eval do
def remove_task(task_name)
#tasks.delete(task_name.to_s)
end
end
Rake.application.remove_task('db:schema:dump')
namespace :db do
namespace :schema do
task :dump do
# Overridden to do nothing
end
end
end
A modification based on David Waller's answer that retains the ability to run db:schema:dump manually. Covers all tasks where schema dumping is automatically invoked. Based on rails 3.2.14 versions of the relevant tasks - when you change rails versions, you'll want to ensure that the task definitions are still valid.
Seriously, there should be a config option to disable automatic schema dumps.
Rake::TaskManager.class_eval do
def remove_task(task_name)
#tasks.delete(task_name.to_s)
end
Rake.application.remove_task("db:migrate")
Rake.application.remove_task("db:rollback")
Rake.application.remove_task("db:forward")
Rake.application.remove_task("db:migrate:up")
Rake.application.remove_task("db:migrate:down")
end
namespace :db do
desc "Migrate the database (options: VERSION=x, VERBOSE=false)."
task :migrate => [:environment, :load_config] do
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) do |migration|
ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope)
end
# db_namespace['_dump'].invoke
end
desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
task :rollback => [:environment, :load_config] do
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step)
# db_namespace['_dump'].invoke
end
# desc 'Pushes the schema to the next version (specify steps w/ STEP=n).'
task :forward => [:environment, :load_config] do
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_paths, step)
# db_namespace['_dump'].invoke
end
namespace :migrate do
# desc 'Runs the "up" for a given migration VERSION.'
task :up => [:environment, :load_config] do
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
raise 'VERSION is required' unless version
ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_paths, version)
# db_namespace['_dump'].invoke
end
# desc 'Runs the "down" for a given migration VERSION.'
task :down => [:environment, :load_config] do
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
raise 'VERSION is required' unless version
ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_paths, version)
# db_namespace['_dump'].invoke
end
end
end
I've tested the following - it works:
You could modify the file "databases.rake" inside of the Rails gem's lib/tasks folder on your server(s). Comment out lines with the following code:
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
By default on Ubuntu (and Ubuntu server) it is here: /var/lib/gems/1.8/gems/rails-x.x.x/lib/tasks/databases.rake.
Tested with Rails 2.3.11, but I'm pretty sure it'll work with Rails 3.x.x too.
Another possible solution (didn't test):
You wouldn't have to modify Rails files, just your application.
A rails plugin called "override_rake_task" could be used to override Rake task "db:schema:dump" which is defined inside if Rails gem.
Apparently if you are not using this plugin and if you define a task in your rails application with the same name, rake would execute both tasks: the default and yours.
The default behavior of Active Record is to dump the schema, but it only does it if the schema dump format is set to ruby (the default).
You can disable the schema dump after migrations by setting the schema format to sql. You can safely reverse it to ruby after the migration finishes without consequences.
You can do it in this way:
1) If you have a line like this one setting explicitly the schema to ruby:
config.active_record.schema_format = :ruby
(it could be inside config/application.rb, on config/environment or in the specific environment you are running the migration in).
Then comment it out:
#config.active_record.schema_format = :ruby
2) Then add a line to set the schema format to SQL, either in the environment you are using, or at config/application.rb, as follows:
config.active_record.schema_format = :sql
Rather than overriding the db:migrate behavior (which is obviously necessary when you're building the migration in development, but unnecessary when deploying) I would suggest creating a separate task like:
# http://stackoverflow.com/questions/13646840/how-to-make-rake-dbmigrate-generate-schema-rb-when-using-sql-schema-format
namespace :db do
desc "Migrate the database, without dumping the schema.rb"
task :deploy => :environment do
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
ActiveRecord::Migrator.migrate("db/migrate/", nil)
end
end
Then during your deployments you can simply do
rake db:deploy
How can I have the tests for my Rails app to be executed in a random order?
Is there a simple solution using rake?
Here you go, define this in lib/tasks/tasks.rb
namespace :test do
namespace :randomize do
desc "Randomize tests"
Rake::TestTask.new(:all => "db:test:prepare") do |t|
t.libs << "test"
t.test_files = Rake::FileList[
'test/unit/**/*_test.rb',
'test/functional/**/*_test.rb',
'test/integration/**/*_test.rb'
].shuffle
t.verbose = true
end
end
end
Run: rake test:randomize:all
Keep in mind that within file tests will still be executed in the order they appear. I guess you could monkey patch test unit to allow for that.
You may wish to check out "ZenTest 3.9.0: now with more Evil" (can't do a direct link, use google's cache)
Added ability to set test execution order, defaults to :random. EVIL!