To avoid an accidental 'rake db:reset' on our production environments, I was thinking about disabling 'rake db:reset' and related tasks that drop the database in the production environment. Is there an easy way to do this, or do I have to redefine the rake task?
Is there a better alternative?
In your Rake file you can add
Rake.application.instance_variable_get('#tasks').delete('db:reset')
and the command is not available any more. If you want to disable multiple commands, put it in a remove_task method for readability.
But a better alternative seem to just not type the rake db:reset command, which is not something you'd accidentally type.
Having a nice backup of your (production) database is also a better solution I suppose.
Put this in lib/tasks/db.rake:
if Rails.env == 'production'
tasks = Rake.application.instance_variable_get '#tasks'
tasks.delete 'db:reset'
tasks.delete 'db:drop'
namespace :db do
desc 'db:reset not available in this environment'
task :reset do
puts 'db:reset has been disabled'
end
desc 'db:drop not available in this environment'
task :drop do
puts 'db:drop has been disabled'
end
end
end
Found here.
Append this on your Rakefile.
namespace :db do
task :drop => :abort_on_production
end
task :abort_on_production do
abort "Don't drop production database. aborted. " if Rails.env.production?
end
It also blocks rake db:reset and rake db:migrate:reset because they call db:drop
You could always overwrite the db:reset task with something like this in lib/db.rake:
namespace :db do
desc 'Resets your database using your migrations for the current environment'
task :reset do
if RAILS_ENV == 'production'
Rake::Task["db:drop"].invoke
Rake::Task["db:create"].invoke
Rake::Task["db:migrate"].invoke
end
end
end
For the record, We have an application linking into our live production database, we use test login details and from there shard's to out test DB.
The last thing we wanted was for rails to wipe out or database schema when running rspec/unit tests.
use the information is this question and here: Is it possible to get a list of all available rake tasks in a namespace?
I was able to come up with the following solution:
Rake.application.in_namespace(:db){|x|
x.tasks.map{|t|
Rake.application.instance_variable_get('#tasks').delete(t.name)
}
}
This placed into the end of our Rakefile enabled us to remove all db: rake tasks as can be seen here:
[davidc#david-macbookp app1{master}]$ rake -T db
[davidc#david-macbookp app1{master}]$
With a little tweaking this could be done to disable on a per environment basis
Update:
Word of warning doing this for db namespace broke testunit and was required to add the addison:
namespace :db do
task 'test:prepare' do
end
end
You could also use the rails_db_protect gem.
You just add the gem and it automatically prevents you from running the following dangerous tasks in production:
db:setup db:reset db:drop db:create db:schema:load
If you have a production environment, like a staging environment, where you want to be able to run these tasks, you can configure the environment to allow it:
ENV['ALLOW_DANGEROUS_TASKS'] = 'true'
There is also the gem rails-safe-tasks which allows more configuration but does not appear to have any tests supporting it.
Related
I have the following 2 rake tasks:
task :clone => :environment do |t, args|
Rake::Task["db:drop"].invoke
Rake::Task["db:create"].invoke
system "pg_restore -O -d database_name last_dump"
Rake::Task["db:migrate"].invoke
Rake::Task["db:test:prepare"].invoke
# Try to force the rails env to reload, but this doesn't solve the problem
Rake::Task["environment"].execute
Rake::Task["db:company_count"].invoke
end
task :company_count => :environment do
puts Company.count
end
When I run rake db:clone the output the Company.count is 0 indicating there are no Companies in the database, but when I run rake db:clone && rake db:company_count the output is 2.
How do I get the correct Company.count after loading the database in the first task?
The Company.count is correct if I remove Rake::Task["db:test:prepare"].invoke from the clone task, but I'm not sure why
My guess is first task is not using the console environment because it creates its own terminal session for the rake, and the second one is.
Try printenv and compare the variables
Also try prefixing the commands with RAILS_ENV=development or whatever environment you want.
guides.rubyonrails.org indicates that the task db:test:prepare is used to do the following to your test database:
drop the database
create the database
run the migrations
After this task completes, you will not have any data in there. Here are stack overflow answers that explains this:
PG undefinedtable error relation users does not exist
What does rake db:test:prepare actually do
I believe the behavior you are seeing has nothing to do with the environment, but a misunderstanding in the intention of this rake task.
I'm working on a personal project and I have a strange doubt. At the localhost, I do rake db:reset almost every time and I always need to run another task named newsworker:create (in other words, I run rake newsworker:create).
My questions are:
How can I execute a specific rake task after every rake db:reset?
How can I generalize the above question to work with rake db:create and other tasks?
Thanks!
In my opinion, the best way is to create your own rake task calling all the tasks needed (to avoid changing Rails/Rake basic tasks):
# lib/tasks/reset_and_create
namespace :database do
desc 'Reset the database to a fresh and clean DB ready for use'
task :reset_and_create do
Rake::Task['db:reset'].invoke
Rake::Task['newsworker:create'].invoke
# if you need to pass arguments to your tasks, use:
# Rake::Task['your_task'].invoke(your_arg, another_arg)
end
end
And use it like this:
rake database:reset_and_create
How can I pass arguments to a rake task so that it executes the rake task on a different schema? For example I have rake code such as the one below:
namespace :update_persons_table do
task :import => :environment do
config = Rails.configuration.database_configuration
ActiveRecord::Base.connection.schema_search_path = "my, public, data_master_reports"
# do stuff make updates to table....
end
end
I call this rake task from the command line like this:
RAILS_ENV='production' rake update_persons_table:import
BTW, does the above RAILS_ENV call I am using have to do with the :environment do statement I am using in the second line? Because in my database.yml file i do have a production: database entry. Im trying to figure out how the whole plumbing for this works. This rake task updates a table in a database. But I want to be able to call it on another clone table in a different database. How can I do that with passing parameters in the command line?
How can I pass arguments to a rake task so that it executes the rake task on a different schema?
I think what you are asking is
How can I pass arguments to a rake task so that it executes the rake task on a different environment?
The schema is the same for all environments of your app
does the above RAILS_ENV call I am using have to do with the :environment do statement I am using in the second line?
Yes :environment do will take whatever environment(:development, :test, :production) you are specifying. In your example, its RAILS_ENV='production'
Now to run the rake task in a different environment, like say the development environment
`RAILS_ENV='development' rake update_persons_table:import'
Now the same code that was executed on the production environment db when you ran `RAILS_ENV='production' rake update_persons_table:import' has been run in the development environment db
Hope this is clear enough to get you started
I am using Ruby on Rails 3.0.9 and I would like to seed the production database in order to add some record without re-building all the database (that is, without delete all existing records but just adding some of those not existing yet). I would like to do that because the new data is needed to make the application to work.
So, since I am using the Capistrano gem, I run the cap -T command in the console in order to list all available commands and to know how I can accomplish what I aim:
$ cap -T
=> ...
=> cap deploy:seed # Reload the database with seed data.
=> ...
I am not sure on the word "Reload" present in the "Reload the database with seed data." sentence. So, my question is: if I run the cap deploy:seed command in the console on my local machine will the seeding process delete all existing data in the production database and then populate it or will that command just add the new data in that database as I aim to do?
If you are using bundler, then the capistrano task should be:
namespace :deploy do
desc "reload the database with seed data"
task :seed do
run "cd #{current_path}; bundle exec rake db:seed RAILS_ENV=#{rails_env}"
end
end
and it might be placed in a separate file, such as lib/deploy/seed.rb and included in your deploy.rb file using following command:
load 'lib/deploy/seed'
This worked for me:
task :seed do
puts "\n=== Seeding Database ===\n"
on primary :db do
within current_path do
with rails_env: fetch(:stage) do
execute :rake, 'db:seed'
end
end
end
end
capistrano 3, Rails 4
Using Capistrano 3, Rails 4, and SeedMigrations, I created a Capistrano seed.rb task under /lib/capistrano/tasks:
namespace :deploy do
desc 'Runs rake db:seed for SeedMigrations data'
task :seed => [:set_rails_env] do
on primary fetch(:migration_role) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :rake, "db:seed"
end
end
end
end
after 'deploy:migrate', 'deploy:seed'
end
My seed migrations are now completely separate from my schema migrations, and ran following the db:migrate process. What a joy! :)
Try adding something like this in your deploy.rb:
namespace :deploy do
desc "reload the database with seed data"
task :seed do
run "cd #{current_path}; rake db:seed RAILS_ENV=#{rails_env}"
end
end
cap deploy:seed should basically be a reference to rake db:seed. It should not delete existing data, unless you specified it to do so in your seed.rb.
Best assumption for the word "Reload" is that :seed is a stateless command, I does not automatically know where it left off, like regular rails migrations. So technically you would always be "reloading" the seed, every time you run it. ...A wild guess, but it sounds good, no?
Please view Javier Vidal answer below
After a discussion with capistrano-rails gem authors I decided to implement this kind of tasks in a separate gem. I think this helps to follow the DRY idea and not implementing the same task over and over again.
I hope it helps you: https://github.com/dei79/capistrano-rails-collection
I have the following rake task defined in my lib/tasks folder:
namespace :db do
namespace :test do
task :prepare => :environment do
Rake::Task["db:seed"].invoke
end
end
end
Now, what this does is seed the test DB when I run rake db:test:prepare. I do this because I have some basic records that must exist in order for the app to function, so they're not optional and can't really be mocked.
Separately, I have a model that uses S3 for asset storage in development and production, but I don't want it to use S3 for testing. I have set up a method in the model that changes the storage path from S3 to local if Rails.env.test?
However, this isn't working. I was wondering if the rake task was aware of what environment it was being called from, and it turns out it is NOT. I put this at the top of my seeds.rb file:
puts "Environment Check: Rails Environment = #{Rails.env}"
Sure enough, when the task runs this prints: Environment Check: Rails Environment = development
So, how can I redo this rake task so that when it's seeding the test DB it knows that it's seeding the test DB??
I was having this problem too; in my db/seeds.rb file I have a block that creates user accounts in the development environment, but they were also being created when preparing the test environment to run rake for RSpec or Cucumber testing, which resulted in a wall of red.
Updated: I've found that the best way to specify the environment for rake tasks is to specify the environment within the task, above all statements that need the environment to be set. So in this case:
Rails.env = 'test'
Rake::Task["db:seed"].invoke
does the job.
From reading the db:test tasks's source, it looks like they only care about grabbing the test db info from database.yml, but don't care which actual environment they're doing it under.
You might need to run rake db:test:prepare RAILS_ENV=test to ensure you're under the test environment.