¿Cómo ejecuto una tarea de rake de Capistrano?

105

Ya tengo un archivo deploy.rb que puede implementar mi aplicación en mi servidor de producción.

Mi aplicación contiene una tarea de rake personalizada (un archivo .rake en el directorio lib / tasks).

Me gustaría crear una tarea de límite que ejecutará de forma remota esa tarea de rake.

Richard Poirier
fuente
2
¿Alguien puede explicar los pros / contras de usar la propia #{rake}variable de capistrano ? Parece que no siempre es la mejor opción.
lulalala

Respuestas:

59

Un poco más explícito, en su \config\deploy.rb, agregue fuera de cualquier tarea o espacio de nombres:

namespace :rake do  
  desc "Run a task on a remote server."  
  # run like: cap staging rake:invoke task=a_certain_task  
  task :invoke do  
    run("cd #{deploy_to}/current; /usr/bin/env rake #{ENV['task']} RAILS_ENV=#{rails_env}")  
  end  
end

Entonces, desde /rails_root/, puede ejecutar:

cap staging rake:invoke task=rebuild_table_abc
Cobarde
fuente
1
es mejor usar / usr / bin / env rake para que las configuraciones de rvm recojan el rake correcto.
DGM
8
Con 'paquete ejecutivo' si está disponible
Bogdan Gusiev
44

... un par de años después ...

Eche un vistazo al complemento de rieles de capistrano, puede ver en https://github.com/capistrano/rails/blob/master/lib/capistrano/tasks/migrations.rake#L5-L14 que puede verse como:

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
Mirek Rusin
fuente
3
Esto es solo para capistrano v3.
phillbaker
Ayudó mucho. ¡Gracias! @Mirek Rusin
Nishant Shrivastava
las otras respuestas, ese uso runfuncionará en capistrano hasta la versión 2. de la versión 3 este es el camino a seguir.
Don Giulio
44

Capistrano 3 Versión genérica (ejecuta cualquier tarea de rastrillo)

Construyendo una versión genérica de la respuesta de Mirek Rusin:

desc 'Invoke a rake command on the remote server'
task :invoke, [:command] => 'deploy:set_rails_env' do |task, args|
  on primary(:app) do
    within current_path do
      with :rails_env => fetch(:rails_env) do
        rake args[:command]
      end
    end
  end
end

Uso de ejemplo: cap staging "invoke[db:migrate]"

Tenga en cuenta que deploy:set_rails_envrequire proviene de la gema capistrano-rails

marinosb
fuente
1
Esto solo admite un único argumento, si reemplaza rake args[:command] con execute :rake, "#{args.command}[#{args.extras.join(",")}]" puede ejecutar una tarea con múltiples argumentos como este: cap production invoke["task","arg1","arg2"]
Robin Clowers
1
@ Robin Clowers Puede pasar varios argumentos, por ejemplo cap staging invoke['task[arg1\,arg2]']. Prefiero este enfoque al que mencionas porque refleja la invocación real de rake. Con este enfoque también se puede encadenar múltiples tareas, lo cual es a menudo útil: cap staging invoke['task1 task2[arg1] task3[arg2\,arg3]']. Funciona para rake 10.2.0 o posterior
marinosb
esto es genial, me gustaría señalar que debes incluir: app como una de tus funciones de servidor.
lfender6445
Aparentemente, esto tenía que ser "invoke [db: migrate]" ... Corrección realizada.
Abram
@Abram con el comando que sugirió obtengo "No sé cómo construir la tarea 'invocar"
dc10
41
run("cd #{deploy_to}/current && /usr/bin/env rake `<task_name>` RAILS_ENV=production")

Lo encontré con Google - http://ananelson.com/said/on/2007/12/30/remote-rake-tasks-with-capistrano/

El RAILS_ENV=productionera un Gotcha - Yo no pienso en ello al principio y no podía entender por qué la tarea no estaba haciendo nada.

Richard Poirier
fuente
2
Una pequeña mejora: si reemplaza el punto y coma con &&, la segunda instrucción (que ejecuta la tarea rake) no se ejecutará si la primera instrucción (cambiar el directorio) falla.
Teflon Ted
2
Esto no funcionará si está implementando en varios servidores. Ejecutará la tarea de rake varias veces.
Mark Redding
4
uno realmente debería respetar la configuración de rake de capistrano"cd #{deploy_to}/current && #{rake} <task_name> RAILS_ENV=production"
kares
@Mark Redding: ¿Podría poner uno de los servidores en su propio rol para tareas de rake y restringir su tarea de capistrano para que solo se ejecute en servidores con ese rol?
mj1531
Hice algo donde creé una tarea en mi deploy.rb. Esa tarea tiene un: roles =>: db de modo que solo se ejecutará en el mismo servidor que definí como mi principal para db: migrate.
Mark Redding
20

Usa invocaciones de rake al estilo Capistrano

Hay una forma común con la que "simplemente funcionará" require 'bundler/capistrano'y otras extensiones que modifican el rake. Esto también funcionará con entornos de preproducción si usa varias etapas. ¿La esencia? Use config vars si puede.

desc "Run the super-awesome rake task"
task :super_awesome do
  rake = fetch(:rake, 'rake')
  rails_env = fetch(:rails_env, 'production')

  run "cd '#{current_path}' && #{rake} super_awesome RAILS_ENV=#{rails_env}"
end
capitánpete
fuente
2
Esta es la mejor solución, usa los valores de capistrano donde estén disponibles
loopj
2
Probablemente valga la pena agregar que si su tarea tiene un espacio de nombres (es decir, no está definida en el espacio de nombres de nivel superior), es posible que top.runrun
deba
Gracias @dolzenko. Acabo de encontrar los documentos del topmétodo . En el caso en el que hayamos definido runen el mismo espacio de nombres, top.runes obligatorio; de lo contrario, debería encontrar el nivel superior runincluso cuando la tarea tenga un espacio de nombres. ¿Me he perdido algo? ¿Qué pasó en tu caso?
captainpete
1
Claramente no tenía ningún método de ejecución definido en el mismo espacio de nombres, así que no estoy seguro de por qué lo necesitaba. En cualquier caso, Capistrano 2.0 es una historia y la próxima versión está basada en Rake (haciendo las cosas más predecibles con suerte)
dolzenko
16

Usa la capistrano-rakegema

Simplemente instale la gema sin meterse con recetas personalizadas de capistrano y ejecute las tareas de rake deseadas en servidores remotos como este:

cap production invoke:rake TASK=my:rake_task

Divulgación completa: lo escribí

Sheharyar
fuente
7

Yo personalmente uso en producción un método auxiliar como este:

def run_rake(task, options={}, &block)
  command = "cd #{latest_release} && /usr/bin/env bundle exec rake #{task}"
  run(command, options, &block)
end

Eso permite ejecutar una tarea de rake similar a usar el método de ejecución (comando).


NOTA: Es similar a lo que propuso Duke , pero yo:

  • use latest_release en lugar de current_release; según mi experiencia, es más lo que espera cuando ejecuta un comando de rake;
  • siga la convención de nomenclatura de Rake y Capistrano (en lugar de: cmd -> tarea y rake -> run_rake)
  • no configure RAILS_ENV = # {rails_env} porque el lugar correcto para configurarlo es la variable default_run_options. Ej. Default_run_options [: env] = {'RAILS_ENV' => 'producción'} # -> DRY!
Szymon Jeż
fuente
5

Hay una capa de gemas interesante que hace que sus tareas de rastrillo estén disponibles como tareas de Capistrano, para que pueda ejecutarlas de forma remota. capeestá bien documentado, pero aquí hay una breve descripción general sobre cómo configurarlo.

Después de instalar la gema, simplemente agregue esto a su config/deploy.rbarchivo.

# config/deploy.rb
require 'cape'
Cape do
  # Create Capistrano recipes for all Rake tasks.
  mirror_rake_tasks
end

Ahora, puede ejecutar todas sus raketareas de forma local o remota cap.

Como beneficio adicional, le capepermite configurar cómo desea ejecutar su tarea de rake de manera local y remota (no más bundle exec rake), solo agregue esto a su config/deploy.rbarchivo:

# Configure Cape to execute Rake via Bundler, both locally and remotely.
Cape.local_rake_executable  = '/usr/bin/env bundle exec rake'
Cape.remote_rake_executable = '/usr/bin/env bundle exec rake'
yacc
fuente
Nota: solo funciona para Capistrano v2.x. No es compatible con Capistrano v3.
nayiaw
3
namespace :rake_task do
  task :invoke do
    if ENV['COMMAND'].to_s.strip == ''
      puts "USAGE: cap rake_task:invoke COMMAND='db:migrate'" 
    else
      run "cd #{current_path} && RAILS_ENV=production rake #{ENV['COMMAND']}"
    end
  end                           
end 
DuArme
fuente
1
Bueno. Cambiarlo de RAILS_ENV=productiona RAILS_ENV=#{rails_env}permite que funcione también en mi servidor de ensayo.
evanrmurphy
2

Esto es lo que puse en mi deploy.rb para simplificar la ejecución de tareas de rake. Es una simple envoltura del método run () de capistrano.

def rake(cmd, options={}, &block)
  command = "cd #{current_release} && /usr/bin/env bundle exec rake #{cmd} RAILS_ENV=#{rails_env}"
  run(command, options, &block)
end

Entonces simplemente ejecuto cualquier tarea de rastrillo así:

rake 'app:compile:jammit'
Duque
fuente
esto entra en conflicto ya que capistrano define su propia variable de rake (utilizada para determinar qué rake usar) y, por lo tanto, rompe los recibos incorporados, por ejemplo, el que precompila los activos
Michael
2

Esto funcionó para mí:

task :invoke, :command do |task, args|
  on roles(:app) do
    within current_path do
      with rails_env: fetch(:rails_env) do
        execute :rake, args[:command]
      end
    end
  end
end

Entonces simplemente corre cap production "invoke[task_name]"

Abram
fuente
1

La mayor parte es de la respuesta anterior con una mejora menor para ejecutar cualquier tarea de rake desde capistrano

Ejecute cualquier tarea de rake de capistrano

$ cap rake -s rake_task=$rake_task

# Capfile     
task :rake do
  rake = fetch(:rake, 'rake')
  rails_env = fetch(:rails_env, 'production')

  run "cd '#{current_path}' && #{rake} #{rake_task} RAILS_ENV=#{rails_env}"
end
Sairam
fuente
1

Esto también funciona:

run("cd #{release_path}/current && /usr/bin/rake <rake_task_name>", :env => {'RAILS_ENV' => rails_env})

Más info: Capistrano Run

acw
fuente
1
{deploy_to} / current no funcionará aquí. El enlace simbólico no ha cambiado. Si actualiza la tarea de rake, se ejecutará un código antiguo. Considere usar {release_path} en su lugar.
Mark Redding
¿Cuanta más información es spam?
hcarreras
1

Si desea poder pasar varios argumentos, intente esto (según la respuesta de marinosbern):

task :invoke, [:command] => 'deploy:set_rails_env' do |task, args|
  on primary(:app) do
    within current_path do
      with :rails_env => fetch(:rails_env) do
        execute :rake, "#{args.command}[#{args.extras.join(",")}]"
      end
    end
  end
end

Entonces puedes ejecutar una tarea como esta: cap production invoke["task","arg1","arg2"]

Robin Clowers
fuente
0

Entonces he estado trabajando en esto. parece que funciona bien. Sin embargo, necesita un formateador para aprovechar realmente el código.

Si no desea utilizar un formateador, simplemente configure el nivel de registro en modo de depuración. Estos semas ah

SSHKit.config.output_verbosity = Logger::DEBUG

Cosas de gorra

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 roles(:app), in: :sequence do
      SSHKit.config.format = :supersimple
      execute args[:execute]
    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

Este es el formateador que construí para trabajar con el código anterior. Se basa en: textsimple integrado en el sshkit, pero no es una mala forma de invocar tareas personalizadas. Oh, muchos no funcionan con la versión más reciente de sshkit gem. Sé que funciona con 1.7.1. Digo esto porque la rama maestra ha cambiado los métodos SSHKit :: Command que están disponibles.

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
newdark-it
fuente
0

Las respuestas anteriores no me ayudaron y encontré esto: De http://kenglish.co/run-rake-tasks-on-the-server-with-capistrano-3-and-rbenv/

namespace :deploy do
  # ....
  # @example
  #   bundle exec cap uat deploy:invoke task=users:update_defaults
  desc 'Invoke rake task on the server'
  task :invoke do
    fail 'no task provided' unless ENV['task']

    on roles(:app) do
      within release_path do
        with rails_env: fetch(:rails_env) do
          execute :rake, ENV['task']
        end
      end
    end
  end

end

para ejecutar su tarea use

bundle exec cap uat deploy:invoke task=users:update_defaults

Tal vez sea útil para alguien

A.Miroshnichenko
fuente