Comment exécuter une tâche de râteau depuis Capistrano?

105

J'ai déjà un deploy.rb qui peut 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 plafond qui exécutera à distance cette tâche de râteau.

Richard Poirier
la source
2
Quelqu'un peut-il expliquer les avantages / inconvénients de l'utilisation de la propre #{rake}variable de capistrano ? Il semble que ce n'est pas toujours la meilleure option.
lulalala

Réponses:

59

Un peu plus explicite, dans votre \config\deploy.rb, ajoutez en dehors de toute 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
lâche
la source
1
mieux utiliser / usr / bin / env rake pour que les configurations rvm prennent le bon rake.
DGM
8
Avec 'bundle exec' si disponible
Bogdan Gusiev
44

... quelques années plus tard ...

Jetez un œil au plugin rails de capistrano, vous pouvez voir sur https://github.com/capistrano/rails/blob/master/lib/capistrano/tasks/migrations.rake#L5-L14 cela peut ressembler à quelque chose comme:

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
la source
3
Ceci est pour capistrano v3, uniquement.
phillbaker
Beaucoup aidé. Merci! @Mirek Rusin
Nishant Shrivastava
les autres réponses, cette utilisation runfonctionnera sur capistrano jusqu'à la version 2. à partir de la version 3, c'est la voie à suivre.
Don Giulio
44

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

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_envnécessite vient du bijou capistrano-rails

marinosb
la source
1
Cela ne prend en charge qu'un seul argument, si vous remplacez rake args[:command] par, execute :rake, "#{args.command}[#{args.extras.join(",")}]" vous pouvez exécuter une tâche avec plusieurs arguments comme ceci: cap production invoke["task","arg1","arg2"]
Robin Clowers
1
@ Robin Clowers Vous pouvez passer plusieurs arguments, par exemple cap staging invoke['task[arg1\,arg2]']. Je préfère cette approche à celle que vous mentionnez car elle reflète l'invocation réelle de rake. Avec cette approche , vous pouvez également enchaîner des tâches multiples, ce qui est souvent utile: cap staging invoke['task1 task2[arg1] task3[arg2\,arg3]']. Fonctionne pour rake 10.2.0 ou plus récent
marinosb
c'est génial - je voudrais noter que vous devez inclure: app comme l'un de vos rôles de serveur.
lfender6445
Apparemment, cela devait être "invoke [db: migrate]" ... Correction apportée.
Abram
@Abram avec la commande que vous avez suggérée, j'obtiens "Je ne sais pas comment construire la tâche 'invoke"
dc10
41
run("cd #{deploy_to}/current && /usr/bin/env rake `<task_name>` RAILS_ENV=production")

Je l'ai trouvé avec Google - http://ananelson.com/said/on/2007/12/30/remote-rake-tasks-with-capistrano/

Le RAILS_ENV=productionétait un Gotcha - Je ne pensais pas au premier abord et ne pouvait pas comprendre pourquoi la tâche ne faisait rien.

Richard Poirier
la source
2
Une amélioration mineure: si vous remplacez le point-virgule par && alors la deuxième instruction (exécutant la tâche rake) ne s'exécutera pas si la première instruction (changement de répertoire) échoue.
Teflon Ted
2
Cela ne fonctionnera pas si vous déployez sur plusieurs serveurs. Il exécutera la tâche de râteau plusieurs fois.
Mark Redding
4
il faut vraiment respecter le rake de capistrano"cd #{deploy_to}/current && #{rake} <task_name> RAILS_ENV=production"
kares
@Mark Redding: Pourriez-vous placer l'un des serveurs dans son propre rôle pour les tâches de rake et restreindre votre tâche capistrano pour qu'elle ne s'exécute que sur les serveurs avec ce rôle?
mj1531
J'ai fait quelque chose où j'ai créé une tâche dans mon deploy.rb. Cette tâche a un: roles =>: db dessus de telle sorte qu'elle ne s'exécutera que sur le même serveur que j'ai défini comme mon serveur principal pour db: migrate.
Mark Redding
20

Utiliser les invocations de râteau de style Capistrano

Il existe une méthode courante qui "fonctionne simplement" avec require 'bundler/capistrano'et d'autres extensions qui modifient rake. Cela fonctionnera également avec les environnements de pré-production si vous utilisez plusieurs étapes. L'essentiel? Utilisez des variables 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
capitainepete
la source
2
C'est la meilleure solution, utilise les valeurs de capistrano là où elles sont disponibles
loopj
2
Cela vaut probablement la peine d'ajouter que si votre tâche est espacée de noms (c'est-à-dire définie non dans l'espace de noms de niveau supérieur), vous devrez peut-être utiliser top.runau lieu de simplementrun
dolzenko
Merci @dolzenko. Je viens de trouver la documentation de la topméthode . Dans le cas où nous avons défini rundans le même espace de noms, top.runest requis, sinon il devrait toujours trouver le niveau supérieur runmême là où la tâche est espacée de noms. Ai-je manqué quelque chose? Que s'est-il passé dans votre cas?
captainpete
1
Je n'avais clairement aucune méthode d'exécution définie dans le même espace de noms, donc je ne sais pas pourquoi j'en avais besoin. Dans tous les cas, Capistrano 2.0 est une histoire et la prochaine version est basée sur Rake (rendant les choses plus prévisibles, espérons-le)
dolzenko
16

Utilisez la capistrano-rakegemme

Installez simplement la gemme sans jouer avec les recettes de 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

Divulgation complète: je l'ai écrit

Sheharyar
la source
7

J'utilise personnellement en production une méthode d'aide 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 rake similaire à l'utilisation de la méthode run (command).


REMARQUE: C'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 que vous attendez lors de l'exécution d'une commande rake;
  • suivez la convention de dénomination de Rake et Capistrano (au lieu de: cmd -> task 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!
Szymon Jeż
la source
5

Il existe une cape de gemme intéressante qui rend vos tâches de râteau disponibles en tant que tâches Capistrano, afin que vous puissiez les exécuter à distance. capeest bien documenté, mais voici un bref aperçu de la façon de configurer i.

Après avoir installé le gem, ajoutez-le simplement à votre config/deploy.rbfichier.

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

Désormais, vous pouvez exécuter toutes vos raketâches localement ou à distance cap.

En prime, capevous permet de définir la manière dont vous souhaitez exécuter votre tâche de rake localement et à distance (pas plus bundle exec rake), ajoutez simplement ceci à votre config/deploy.rbfichier:

# 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
la source
Remarque: ne fonctionne que pour Capistrano v2.x. Non compatible avec 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
la source
1
Bien. Le changer de RAILS_ENV=productionen lui RAILS_ENV=#{rails_env}permet de fonctionner également sur mon serveur de test.
evanrmurphy
2

Voici ce que j'ai mis dans mon deploy.rb pour simplifier l'exécution des tâches de rake. 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 lance n'importe quelle tâche de râteau comme ceci:

rake 'app:compile:jammit'
Duc
la source
cela entre en conflit car capistrano définit sa propre variable de rake (utilisée pour déterminer quel rake utiliser) et rompt ainsi les reçus intégrés, par exemple celui qui précompile les actifs
Michael
2

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

Alors lancez simplement cap production "invoke[task_name]"

Abram
la source
1

La plupart provient de la réponse ci-dessus avec une amélioration mineure pour exécuter n'importe quelle tâche de rake de capistrano

Exécutez n'importe quelle tâche de râteau depuis 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
la source
1

Cela fonctionne également:

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

Plus d'informations: Capistrano Run

acw
la source
1
{deploy_to} / current ne fonctionnera pas ici. Le lien symbolique n'a pas changé. Si vous mettez à jour la tâche de rake, cela exécutera l'ancien code. Pensez à utiliser {release_path} à la place.
Mark Redding
plus d'informations sont du spam?
hcarreras
1

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 ceci: cap production invoke["task","arg1","arg2"]

Robin Clowers
la source
0

J'ai donc 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 de formateur, réglez simplement le niveau de journalisation sur le mode débogage. Ces semas à h

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 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: TextSimple intégré au sshkit mais ce n'est pas une mauvaise façon d'appeler des tâches personnalisées. Oh, beaucoup ne fonctionnent pas avec la dernière version de sshkit gem. Je sais que cela fonctionne avec 1.7.1. Je dis cela parce que la branche principale a changé les méthodes SSHKit :: Command qui sont 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
la source
0

Les réponses précédentes ne m'ont pas aidé et j'ai trouvé ceci: 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

pour exécuter votre tâche, utilisez

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

Peut-être que ce sera utile pour quelqu'un

A.Miroshnichenko
la source