Run rake task from Capistrano if it exists - ruby-on-rails

I need to a create a Capistrano pre-deploy step that runs a custom rake task.
in deploy.rb:
before 'deploy:starting', 'db:rollback_staging'
namespace :db do
desc 'Rollback staging db only if PR already deployed requires rollback'
task :rollback_staging do
on roles(:master) do
within current_path.to_s do
with rails_env: 'staging' do
execute :rake, 'release:rollback_staging'
end
end
end
end
end
The problem is that when deploying this code the rake task is not yet present on the server and therefore deploy fails with:
rake stdout: rake aborted!
Don't know how to build task 'release:rollback_staging' (See the list of available tasks with `rake --tasks`)
If there a way to check if the rake task exists from Capistrano?
smth like:
with rails_env: 'staging' do
execute :rake, 'release:rollback_staging' if rake_exists? 'release:rollback_staging'
end

I ended up just ignoring not 0 exit code from a rake task using raise_on_non_zero_exit: false:
with rails_env: 'staging' do
execute :rake, 'release:rollback_staging', raise_on_non_zero_exit: false
end

Would this work? Saw this pattern in https://github.com/AgileConsultingLLC/capistrano3-delayed-job
if Rake::Task.task_defined?('release:rollback_staging')

Related

What does the rake task db:full_reset do?

I have the following in my deploy script in which I inherited from other devs. Does any one know what the rake task db:full_reset does? I would think it resets the db but I can't find that actual task anywhere in the code. Running rake -T doesn't give any clues. Would it be located in a gem?
namespace :db do
task :full_reset do
on roles(:app) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :bundle, "exec rake db:full_reset"
end
end
end
end
end
You can use rake -W db:full_reset to see where that task is defined

Don't know how to build task 'environment' When i deploy my app to vps in Capistrano task

I deployed my app to vps with capistrano.
Everything works fine but only :environment task.
This is my code
namespace :deploy do
desc 'Clear memcache'
task clear_memcache: :environment do
on roles(:app) , in: :sequence, wait: 2 do
::Rails.cache.clear
CACHE.flush
end
end
after :finishing, :clear_memcache
end
But i always got this error.
#<RuntimeError: Don't know how to build task 'environment' (see --tasks)>
How can i fix this?
Thanks!
I think you are mixing two concepts. A rake task and a capistrano task. Rake tasks do use the :environment subtask, whereas the capistrano ones don't. Capistrano tasks cannot (AFAIK) directly call ruby code in the context of the rails application on the server, this is what rake tasks do.
What you actually probably want is to define both a rake task to clear the cache and a capistrano task that will call the rake task on the deployment server.
Try these:
The rake task to clear the cache:
# put this in Rakefile or any other rake file under lib/tasks
desc 'Clear memcache'
task clear_memcache: :environment do
::Rails.cache.clear
CACHE.flush
end
The capistrano task to call the rake on the server:
# config/deploy/production.rb
namespace :deploy do
desc 'Clear memcache'
task :clear_memcache do
on roles(:app) , in: :sequence, wait: 2 do
within release_path do
with rails_env: fetch(:rails_env) do
execute 'rake', 'clear_memcache'
end
end
end
end
after :finishing, :clear_memcache
end

Rails defining task

i've got several tasks like this:
desc "Generate and send something"
task(:generate_something => :environment) do
Those were situtated in lib/tasks
Is there any possibility to don't set up the environment? I want that the task find out by it selve which environment is active at the moment?
You can simply test the Rails.env variable as in the app itself:
desc "Test task"
task :blaff do
puts Rails.env
puts Rails.env.production?
end
Now you can run this in any environment you want:
% rake blaff
development
false
% RAILS_ENV=production rake blaff
production
true
% RAILS_ENV=test rake blaff
test
false

Reset database with rake task

I want to use Heroku's scheduler to reset my database once every day.
It's recommended to use rake tasks for the scheduler. This is what I've tried:
task :reset_database => :environment do
`heroku pg:reset MY_DB:URL`
`heroku run rake db:migrate db:seed`
# some other ruby commands
end
But how would I do this correctly, because putting the heroku commands within backticks, which with bash normally works, doesn't work here:
No such file or directory - heroku
Try this rake task:
namespace :reset_database do
desc "Destroy all table entries."
task :all => :environment do
ActiveRecord::Base.connection.tables.each do |table|
if table != 'schema_migrations'
table.singularize.camelize.constantize.destroy_all
end
# Use this if you want to use the normal seeds:
# Rails.application.load_seed
# Use this if you want to run another rake task:
Rake::Task["foo:bar"].invoke
end
end
end

Capistrano 3 - Understand tasks

i'm trying to understand how capistrano 3.1 is working, but because of its lack of documentation (its capistrano, so...), im running below my understanding.
Let me explain.
Here's a snippet taken from capistrano/rails gem
namespace :deploy do
desc 'Runs rake db:migrate if migrations are set'
task :migrate => [:set_rails_env] do
on primary fetch(:migration_role) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :rake, "db:migrate"
end
end
end
end
#[...]
end
when execute cap integration deploy:migrate, it sends the following command:
cd /srv/app/releases/20131106101722 && ( RAILS_ENV=integration /tmp/app/rvm-auto.sh . rake assets:precompile )
I changed a little bit the (non-working) code provided for delayed_job into that
namespace :delayed_job do
def args
fetch(:delayed_job_args, '')
end
def delayed_job_roles
fetch(:delayed_job_server_role, :app)
end
def delayed_job_bin
fetch(:delayed_job_bin, :'bin/delayed_job')
end
desc 'Restart the delayed_job process'
task :restart do
on roles(delayed_job_roles) do
within release_path do
with rails_env: fetch(:rails_env) do
execute delayed_job_bin, 'restart', args
end
end
end
end
end
And i get the following command cd /srv/winddle/current && ( RAILS_ENV=integration bin/delayed_job restart )
Obviously, it misses the bundle exec command.
I dive deeply into capistrano/bundler and capistrano/rails to look for some kind of hook that would add bundle exec automatically to any of these commands (or force the register of ssh kits commands) but couldnt find any.
The only solution i found is to use
execute :bundle, :exec, delayed_job_bin, :start, args which is not acceptable of course.
Anyone proper solution / explanation is welcomed.
Regards
Add the following line in deploy.rb, then use the code provided by delayed_job other than changing script to bin, which I see you already did:
set :bundle_bins, fetch(:bundle_bins, []).push('bin/delayed_job')
For users of RVM, add this instead:
set :rvm_map_bins, fetch(:rvm_map_bins, []).push('bin/delayed_job')
Source: https://github.com/capistrano/bundler#usage.
I'm literally just starting out with Capistrano and also struggling against a lack of documentation, so sorry if this post misses the mark.
v3 relies a lot on sshkit, so reading the documentation for that should be a big help. The readme gives an example that may solve your problem.
SSHKit.config.command_map.prefix[:rake].push("bundle exec")
puts SSHKit.config.command_map[:rake]
# => bundle exec rake
I also found an alternative solution in a Semaphore blog post.
SSHKit.config.command_map[:rake] = "bundle exec rake"
SSHKit.config.command_map[:rails] = "bundle exec rails"

Resources