How to run custom rake task via capistrano 3? - ruby-on-rails

which way, i can run rake commands via capistrano on remote server.
for example i have a lib/task/reparse.rake with some methods
desc "it's take csv file, makes some changes and fill db with this info"
task :example1 => :environment do
require 'csv'
rows_to_insert = []
# some actions
# ...
end
on local server all is fine - i just run rake reparse:example1
and it's work(fill db correctly).
so question is - how can i run this command on real hosting, after deploy?
i'am using rails 4.1 + capistrano 3.
P.S. examples from site not work for me
How do I run a rake task from Capistrano?
if i try cap production rake:invoke task=reparse:land
it fails with:
cap aborted!
Don't know how to build task 'rake:invoke'
some fixes
namespace :somenamespace do
task :runrake do
on roles(:all), in: :sequence, wait: 5 do
within release_path do
execute :rake, ENV['task'], "RAILS_ENV=production"
end
end
end
end
with such way it begin to execute via
cap production somenamespace:runrake task=custom_task_file:custom_method1

Based on the capistrano/rails gem: https://github.com/capistrano/rails/blob/master/lib/capistrano/tasks/migrations.rake
namespace :somenamespace do
task :runrake do
on roles(:all) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :rake, ask :task
end
end
end
end
end

You can create a corresponding capistrano task to run a specific rake task like that:
namespace :guests do
desc 'Remove guest users older than 7 days'
task :clean do
on roles(:app) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :rake, 'guests:delete_old_guest_users'
end
end
end
end
end

So I have been working on this. it seams to work well. However you need a formater to really take advantage of the code.
If you don't want to use a formatter just set the log level to to debug mode.
SSHKit.config.output_verbosity = Logger::DEBUG
Cap Stuff
namespace :invoke do
desc 'Run a bash task on a remote server. cap environment invoke:bash[\'ls -la\'] '
task :bash, :execute do |_task, args|
on primary :app do
within deploy_to do
with rails_env: fetch(:rails_env) do
SSHKit.config.format = :supersimple
execute args[:execute]
end
end
end
end
desc 'Run a rake task on a remote server. cap environment invoke:rake[\'db:migrate\'] '
task :rake, :task do |_task, args|
on primary :app do
within current_path do
with rails_env: fetch(:rails_env) do
SSHKit.config.format = :supersimple
rake args[:task]
end
end
end
end
end
This is the formatter I built to work with the code above. It is based off the :textsimple built into the sshkit but it is not a bad way to invoke custom tasks. Oh this many not works with the newest version of sshkit gem. I know it works with 1.7.1. I say this because the master branch has changed the SSHKit::Command methods that are available.
module SSHKit
module Formatter
class SuperSimple < SSHKit::Formatter::Abstract
def write(obj)
case obj
when SSHKit::Command then write_command(obj)
when SSHKit::LogMessage then write_log_message(obj)
end
end
alias :<< :write
private
def write_command(command)
unless command.started? && SSHKit.config.output_verbosity == Logger::DEBUG
original_output << "Running #{String(command)} #{command.host.user ? "as #{command.host.user}#" : "on "}#{command.host}\n"
if SSHKit.config.output_verbosity == Logger::DEBUG
original_output << "Command: #{command.to_command}" + "\n"
end
end
unless command.stdout.empty?
command.stdout.lines.each do |line|
original_output << line
original_output << "\n" unless line[-1] == "\n"
end
end
unless command.stderr.empty?
command.stderr.lines.each do |line|
original_output << line
original_output << "\n" unless line[-1] == "\n"
end
end
end
def write_log_message(log_message)
original_output << log_message.to_s + "\n"
end
end
end
end

you need to load a custom rake task in Capistrano config:
# config/deploy.rb || config/deploy/production.rb
load 'lib/task/reparse.rake'
check for new task in console cap -T

Try capistrano-rake
Without messing with custom capistrano tasks or going into much detail, all you need to do is simply install the gem and you can start executing rake tasks on remote servers like this:
$ cap production invoke:rake TASK=some:rake_task

Related

Capistrano 3.0 Rails 5.0.0 database custom task

I have deployed a Rails app on an Ubuntu server with Capistrano. However I am trying to run a custom task I had created and is in 'lib/tasks'. I tried to make it work by executing
cap production db:views
As if it was a custom task, but obviously it did not worked
cap aborted!
Don't know how to build task 'db:views'
The file is sql_views.rake, this task is for create views in the database
namespace :db do
desc "Update and create SQL views"
task :views => :environment do
Dir["#{Rails.root}/db/sql_views/*.sql"].each do |file_name|
STDERR.puts "Applying the SQL view at #{file_name}"
source_file = File.new(file_name, 'r')
if source_file and (sql_content = source_file.read)
ActiveRecord::Base.transaction do
# Each statement ends with a semicolon followed by a newline.
sql_lines = sql_content.split(/;[ \t]*$/)
if sql_lines.respond_to?(:each)
sql_lines.each do |line|
ActiveRecord::Base.connection.execute "#{line};"
end
end
end # transaction
end
end # Dir.each
end # task
end
If you used cap install to generate your Capfile, the following lines should be present:
# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
So it's looking in lib/capistrano/tasks for custom tasks.
Move lib/tasks/sql_views.rake to lib/capistrano/tasks/sql_views.rake
Or just import each rake task:
import 'lib/tasks/sql_views.rake'

rake tasks dry up namespace with namespace

I setup a series of custom rake tasks for a project that I am building that are name spaced to placewise. These tasks can be viewed by running rake tasks from the console.
desc 'List all available placewise rake tasks for this application.'
task :tasks do
result = %x[rake -T | sed -n '/placewise:/{/grep/!p;}']
result.each_line do |task|
puts task
end
end
All of these tasks are stored in lib/tasks/placewise and built like so:
namespace :placewise do
namespace :db do
desc "Drop and create the current database, the argument [env] = environment."
task :recreate, [:env] do |t,args|
env = environments(args.env)
msg("Dropping the #{env} database")
shell("RAILS_ENV=#{env} rake db:drop", step: "1/3")
msg("Creating the #{env} database")
shell("RAILS_ENV=#{env} rake db:create", step: "2/3")
msg("Running the #{env} database migrations")
shell("RAILS_ENV=#{env} rake db:migrate", step: "3/3")
end
end
end
A new task, for example may start with the base setup as follows:
namespace :placewise do
namespace :example do
desc "example"
task :example do
end
end
end
As you can see the namespace :placewise do will be replicated each time. I want to keep all of our custom rake tasks in the same group, however, I am curious if there is a way to avoid having to add that namespace to each .rake file?
Cheers.
Unfortunately I was advised against this strategy and in the process of discovery I found out my helper methods were not setup correctly. So here goes.
I created a new modules folder in lib/modules with the new helper_functions.rb file inside that directory. Here are my helpers:
module HelperFunctions
# ------------------------------------------------------------------------------------
# Permitted environments
# ------------------------------------------------------------------------------------
def environments(arg)
arg = arg || "development"
environments = ["development", "test", "production"]
if environments.include?(arg)
puts
msg("Environment parameter is valid")
return arg
else
error("Invalid environment parameter")
exit
end
end
# ------------------------------------------------------------------------------------
# Console message handler
# ------------------------------------------------------------------------------------
def msg(txt, periods: "yes", new_line: "yes")
txt = txt + "..." if periods == "yes"
puts "===> " + txt
puts if new_line == "yes"
end
def error(reason)
puts "**** ERROR! ABORTING: " + reason + "!"
end
# ------------------------------------------------------------------------------------
# Execute Shell Commands
# ------------------------------------------------------------------------------------
def shell(cmd, step: nil)
msg("Starting step #{step}", new_line: "no") if step.present?
if ENV['TRY']
puts "-->> " + cmd
else
sh %{#{cmd}}
end
msg("#{step} completed!", periods: "no")
end
end
Then in the Rakefile add:
# Shared Ruby functions used in rake tasks
require File.expand_path('../lib/modules/helper_functions', __FILE__)
include HelperFunctions
Rails.application.load_tasks
# Do not add other tasks to this file, make files in the primary lib/tasks dir ending in .rake
# All placewise tasks should be under the lib/tasks/placewise folder and end in .rake
desc 'List all available placewise rake tasks for this application.'
task :tasks do
result = %x[rake -T | sed -n '/placewise:/{/grep/!p;}']
result.each_line do |task|
puts task
end
end
And finally my .rake tasks look like:
namespace :placewise do
# ------------------------------------------------------------------------------------
namespace :db do
# ------------------------------------------------------------------------------------
desc "Drop and create the current database, the argument [env] = environment."
task :recreate, [:env] do |t,args|
env = HelperFunctions::environments(args.env)
HelperFunctions::msg("Dropping the #{env} database")
HelperFunctions::shell("RAILS_ENV=#{env} rake db:drop", step: "1/3")
HelperFunctions::msg("Creating the #{env} database")
HelperFunctions::shell("RAILS_ENV=#{env} rake db:create", step: "2/3")
HelperFunctions::msg("Running the #{env} database migrations")
HelperFunctions::shell("RAILS_ENV=#{env} rake db:migrate", step: "3/3")
end
# ------------------------------------------------------------------------------------
end
# ------------------------------------------------------------------------------------
end

Create my own rake test:foo task

I have a bunch of tests that aren't unit or functional tests, they're of the format test/foo/special_test.rb
I want to create a rake task like rake test:units that will run all the tests in the foo folder. How do I do this?
Edit: I'd actually like rake test:foo to be a little different from rake test:units, in that I do not want it to run when I do simply rake test.
I don't remember where this is from, so unfortunately I can't give proper acknowledgement, but this should work. I say "should" because I've stopped using it, but grabbed it from my git history.
# First, 'reopen' the default :test namespace and create your custom task.
namespace :test do
Rake::TestTask.new(:foo_tests => ["test:prepare", "other_dependent_rake_tasks"] ) do |t|
DatabaseCleaner.strategy = :transaction # If using this.
t.libs << "test"
# Will also get subfolders within test/foo
t.test_files = FileList['test/foo/**/*_test.rb', 'test/foo/*_test.rb']
end
end
You can remove the default "test" task and redefine it so that when you run rake test it will automatically also run rake test:foo_tests.
remove_task "test"
desc 'Adding onto Rails regular tests'
task :test do
# Add all the names of tests you want run here.
errors = %w(test:units test:functionals test:integration test:foo_tests).collect do |task|
begin
puts "Running: #{task}"
Rake::Task[task].invoke
nil
rescue => e
task
end
end.compact
abort "Errors running #{errors * ', '}!" if errors.any?
end

Rails app Rake task doesn't know how to build other Rake task

I'm trying to create a Rake task that invokes two other rake tasks. I've found people with related questions here and here, but it hasn't been very useful. This is what I've cobbled together so far. Any idea what I'm doing wrong?
task :cron => :environment do
#if Time.now.hour % 2 == 0
Rake::Task["robots:update_robots"].reenable
Rake::Task["robots:update_robots"].invoke
#end
end
As you can see, it's a cron job that's meant for Heroku to do. But I've commented out what I don't need so I can test that it's working.
I keep getting this error:
Don't know how to build task 'robots:update_robots'
But I have no idea why.
UPDATE: So I it turns out I wasn't able to run the original task that was being called by my cron rake task. I had it running ok for a while, buy somewhere along the line, I deleted the "d" in "update". So this command
Rake::Task["robots:upate_robots"].execute
didn't because the robots rake task was "upate", not "update".
Tl;dr: typos.
In general, your solution should work:
require 'rake'
task :environment do
puts 'task environment'
end
namespace :robots do
task :update_robots do
puts "task robots:update_robots"
end
end
task cron: :environment do
puts 'task cron'
Rake::Task['robots:update_robots'].reenable
Rake::Task['robots:update_robots'].invoke
end
Rake::Task['robots:update_robots'].invoke
Rake::Task[:cron].invoke
# >> robots:update_robots was invoked
# >> task robots:update_robots
# >> task environment
# >> task cron
# >> task robots:update_robots
My first thought is that you must have the rake task wrong (are you sure it's "robots:update_robots" ?)
It's unusual to me that you need to reenable it, this implies that what you want is not Rake, but just plain old Ruby. Move the contents of the update_robots task out to a method which you can then invoke directly instead of trying to treat tasks like methods (tasks are for handling dependencies, they only invoke once on purpose, and your need to bend them around that implies you're using the wrong tool for the job). Then, both your code and the robots:update_robots can just call the same method:
require 'rake'
def update_robots
puts "method update_robots"
end
task :environment do
puts 'task environment'
end
namespace :robots do
task :update_robots do
update_robots
puts "task robots:update_robots"
end
end
task cron: :environment do
puts 'task cron'
update_robots
end
Rake::Task['robots:update_robots'].invoke
Rake::Task[:cron].invoke
# >> method update_robots
# >> task robots:update_robots
# >> task environment
# >> task cron
# >> method update_robots

How can I run a rake task from a delayed_job

I'd like to run a rake task (apn:notifications:deliver from the apn_on_rails gem) from a delayed_job. In other words, I'd like enqueue a delayed job which will call the apn:notifications:deliver rake task.
I found this code http://pastie.org/157390 from http://geminstallthat.wordpress.com/2008/02/25/run-rake-tasks-with-delayedjob-dj/.
I added this code as DelayedRake.rb to my lib directory:
require 'rake'
require 'fileutils'
class DelayedRake
def initialize(task, options = {})
#task = task
#options = options
end
##
# Called by Delayed::Job.
def perform
FileUtils.cd RAILS_ROOT
#rake = Rake::Application.new
Rake.application = #rake
### Load all the Rake Tasks.
Dir[ "./lib/tasks/**/*.rake" ].each { |ext| load ext }
#options.stringify_keys!.each do |key, value|
ENV[key] = value
end
begin
#rake[#task].invoke
rescue => e
RAILS_DEFAULT_LOGGER.error "[ERROR]: task \"#{#task}\" failed. #{e}"
end
end
end
Everything runs fine until the delayed_job runs and it complains:
[ERROR]: task "apn:notifications:deliver" failed. Don't know how to build task 'apn:notifications:deliver'
How do I let it know about apn_on_rails? I'd tried require 'apn_on_rails_tasks' at the top of DelayedRake which didn't do anything. I also tried changing the directory of rake tasks to ./lib/tasks/*.rake
I'm somewhat new to Ruby/Rails. This is running on 2.3.5 on heroku.
Why don't do just a system call ?
system "rake apn:notifications:deliver"
I believe it's easier if you call it as a separate process. See 5 ways to run commands from Ruby.
def perform
`rake -f #{Rails.root.join("Rakefile")} #{#task}`
end
If you want to capture any errors, you should capture STDERR as shown in the article.

Resources