Comment est-ce que je lance une tâche de râteau de Capistrano?

J’ai déjà un deploy.rb capable de déployer mon application sur mon serveur de production.

Mon application contient une tâche de rake personnalisée (un fichier .rake dans le répertoire lib / tasks).

Je voudrais créer une tâche de cap qui exécutera cette tâche à distance.

Un peu plus explicite, dans votre \config\deploy.rb , ajoutez en dehors de n’importe quelle tâche ou espace de noms:

 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 

Ensuite, à partir de /rails_root/ , vous pouvez exécuter:

 cap staging rake:invoke task=rebuild_table_abc 
 run("cd #{deploy_to}/current && /usr/bin/env rake `` RAILS_ENV=production") 

Trouvé avec Google – http://ananelson.com/said/on/2007/12/30/remote-rake-tasks-with-capistrano/

Le RAILS_ENV=production était un piège – je n’y ai pas pensé au début et je n’arrivais pas à comprendre pourquoi la tâche ne faisait rien.

… quelques années plus tard …

Jetez un coup d’œil au plugin rails de capistrano, que vous pouvez voir sur https://github.com/capistrano/rails/blob/master/lib/capistrano/tasks/migrations.rake#L5-L14 .

 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 

Version générique de Capistrano 3 (exécuter n’importe quelle tâche)

Construire une version générique de la réponse 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 

Exemple d’utilisation: cap staging "invoke[db:migrate]"

Notez que deploy:set_rails_env nécessite provient de la gem capistrano-rails

Utiliser des invocations de rake de style Capistrano

Il existe un moyen courant de “fonctionner” avec require 'bundler/capistrano' et d’autres extensions modifiant le rake. Cela fonctionnera également avec les environnements de pré-production si vous utilisez plusieurs étapes. L’essentiel? Utilisez des vars de configuration si vous le pouvez.

 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 

Utilisez le gemme de capistrano-rake

Installez simplement la gem sans vous soucier des recettes capistrano personnalisées et exécutez les tâches de râteau souhaitées sur des serveurs distants comme ceci:

 cap production invoke:rake TASK=my:rake_task 

Full Disclosure: Je l’ai écrit

Personnellement, j’utilise dans la production une méthode d’assistance comme celle-ci:

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

Cela permet d’exécuter une tâche similaire à celle de la méthode run (command).


NOTE: Il est similaire à ce que Duke a proposé, mais je:

  • utilisez latest_release au lieu de current_release – d’après mon expérience, c’est plus ce à quoi vous vous attendez lors de l’exécution d’une commande rake;
  • suivre la convention de nommage de Rake et Capistrano (au lieu de: cmd -> tâche et rake -> run_rake)
  • ne définissez pas RAILS_ENV = # {rails_env} car le bon endroit pour le définir est la variable default_run_options. Par exemple default_run_options [: env] = {‘RAILS_ENV’ => ‘production’} # -> DRY!

Il existe une cape de gemme intéressante qui rend vos tâches de rake disponibles sous forme de tâches Capistrano, vous pouvez donc les exécuter à distance. cape est bien documenté, mais voici un bref aperçu sur la façon de le configurer.

Après avoir installé le joyau, ajoutez-le simplement à votre fichier config/deploy.rb .

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

Maintenant, vous pouvez exécuter toutes vos tâches de rake localement ou à distance via cap .

Comme un bonus supplémentaire, cape vous permet de définir comment vous voulez exécuter votre tâche de rake localement et à distance (plus de bundle exec rake ), il suffit d’append ceci à votre fichier config/deploy.rb :

 # 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' 
 namespace :rake_task do task :invoke do if ENV['COMMAND'].to_s.ssortingp == '' puts "USAGE: cap rake_task:invoke COMMAND='db:migrate'" else run "cd #{current_path} && RAILS_ENV=production rake #{ENV['COMMAND']}" end end end 

Voici ce que je mets dans mon deploy.rb pour simplifier l’exécution des tâches de râteau. C’est un simple wrapper autour de la méthode 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 

Ensuite, je ne fais que des tâches telles que:

 rake 'app:comstack:jammit' 

Cela a fonctionné pour moi:

 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 

Ensuite, lancez simplement la cap production "invoke[task_name]"

La plupart du temps, il s’agit d’une réponse ci-dessus avec une amélioration mineure pour exécuter une tâche de ratissage de capistrano.

Exécutez n’importe quelle tâche à partir 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 

Cela fonctionne aussi:

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

Plus d’infos: Capistrano Run

Si vous voulez pouvoir passer plusieurs arguments, essayez ceci (basé sur la réponse 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 

Ensuite, vous pouvez exécuter une tâche comme celle-ci: cap production invoke["task","arg1","arg2"]

Donc j’ai travaillé là-dessus. il semble bien fonctionner. Cependant, vous avez besoin d’un formateur pour vraiment tirer parti du code.

Si vous ne souhaitez pas utiliser un formateur, définissez simplement le niveau de journalisation sur le mode débogage. Ces semas à h

 SSHKit.config.output_verbosity = Logger::DEBUG 

Stuff Cap

 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 

C’est le formateur que j’ai construit pour fonctionner avec le code ci-dessus. Il est basé sur le: textesimple intégré dans le sshkit mais ce n’est pas une mauvaise façon d’invoquer des tâches personnalisées. Oh, beaucoup ne fonctionnent pas avec la nouvelle version de sshkit gem. Je sais que cela fonctionne avec 1.7.1. Je dis cela parce que la twig principale a changé les méthodes SSHKit :: Command 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