Wie führe ich eine Rechenaufgabe von Capistrano aus aus?

105

Ich habe bereits eine deploy.rb, die meine App auf meinem Produktionsserver bereitstellen kann.

Meine App enthält eine benutzerdefinierte Rake-Task (eine .rake-Datei im Verzeichnis lib / task).

Ich möchte eine Cap-Task erstellen, mit der diese Rake-Task remote ausgeführt wird.

Richard Poirier
quelle
2
Kann jemand die Vor- und Nachteile der Verwendung der eigenen #{rake}Variablen von capistrano erklären ? Es scheint nicht immer die beste Option zu sein.
Lulalala

Antworten:

59

Etwas expliziter in Ihrem \config\deploy.rb, fügen Sie außerhalb einer Aufgabe oder eines Namespace hinzu:

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

Dann können /rails_root/Sie von ausführen:

cap staging rake:invoke task=rebuild_table_abc
Feigling
quelle
1
Verwenden Sie besser den Rake / usr / bin / env, damit die RVM-Setups den richtigen Rake auswählen.
DGM
8
Mit 'bundle exec' falls verfügbar
Bogdan Gusiev
44

... ein paar Jahre später ...

Schauen Sie sich das Rails-Plugin von capistrano an, das Sie unter https://github.com/capistrano/rails/blob/master/lib/capistrano/tasks/migrations.rake#L5-L14 sehen können. Es kann ungefähr so ​​aussehen:

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
quelle
3
Dies gilt nur für Capistrano v3.
Phillipsbaker
Hat viel geholfen. Vielen Dank! @ Mirek Rusin
Nishant Shrivastava
Die anderen Antworten, die verwendet runwerden, funktionieren auf Capistrano bis Version 2. Ab Version 3 ist dies der richtige Weg.
Don Giulio
44

Capistrano 3 Generische Version (jede Rechenaufgabe ausführen)

Erstellen einer generischen Version von Mirek Rusins ​​Antwort:

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

Anwendungsbeispiel: cap staging "invoke[db:migrate]"

Beachten Sie, dass deploy:set_rails_enverfordert, kommt von der Capistrano-Schienen Edelstein

marinosb
quelle
1
Dies unterstützt nur ein einziges Argument, wenn Sie ersetzen rake args[:command] mit execute :rake, "#{args.command}[#{args.extras.join(",")}]" Ihnen können eine Aufgabe mit mehreren Argumenten ausführen wie folgt: cap production invoke["task","arg1","arg2"]
Robin Clowers
1
@ Robin Clowers Sie können mehrere Argumente übergeben, z cap staging invoke['task[arg1\,arg2]']. Ich bevorzuge diesen Ansatz gegenüber dem von Ihnen erwähnten, da er den tatsächlichen Aufruf von Rake widerspiegelt. Mit diesem Ansatz können Sie auch mehrere Aufgaben verketten, was häufig nützlich ist : cap staging invoke['task1 task2[arg1] task3[arg2\,arg3]']. Funktioniert für Rake 10.2.0 oder neuer
Marinosb
Das ist großartig - ich möchte darauf hinweisen, dass Sie Folgendes hinzufügen müssen: App als eine Ihrer Serverrollen.
lfender6445
Anscheinend musste dies "invoke [db: migrate]" sein ... Korrektur vorgenommen.
Abram
@Abram mit dem Befehl, den Sie vorgeschlagen haben Ich bekomme "Weiß nicht, wie man Task 'aufrufen'
dc10
41
run("cd #{deploy_to}/current && /usr/bin/env rake `<task_name>` RAILS_ENV=production")

Gefunden bei Google - http://ananelson.com/said/on/2007/12/30/remote-rake-tasks-with-capistrano/

Das RAILS_ENV=productionwar ein Gotcha - ich habe zuerst nicht daran gedacht und konnte nicht herausfinden, warum die Aufgabe nichts tat.

Richard Poirier
quelle
2
Eine kleine Verbesserung: Wenn Sie das Semikolon durch && ersetzen, wird die zweite Anweisung (Ausführen der Rake-Task) nicht ausgeführt, wenn die erste Anweisung (Ändern des Verzeichnisses) fehlschlägt.
Teflon Ted
2
Dies funktioniert nicht, wenn Sie auf mehreren Servern bereitstellen. Die Rechenaufgabe wird mehrmals ausgeführt.
Mark Redding
4
man sollte Capistranos Recheneinstellung wirklich respektieren"cd #{deploy_to}/current && #{rake} <task_name> RAILS_ENV=production"
kares
@ Mark Redding: Könnten Sie einen der Server für Rake-Aufgaben in eine eigene Rolle versetzen und Ihre Capistrano-Aufgabe so einschränken, dass sie nur auf Servern mit dieser Rolle ausgeführt wird?
mj1531
Ich habe etwas getan, bei dem ich eine Aufgabe in meiner deploy.rb erstellt habe. Diese Aufgabe hat eine: role =>: db, so dass sie nur auf demselben Server ausgeführt wird, den ich als meine primäre für db: migrate definiert habe.
Mark Redding
20

Verwenden Sie Rake-Aufrufe im Capistrano-Stil

Es gibt einen gängigen Weg, der "nur funktioniert" require 'bundler/capistrano'und andere Erweiterungen, die Rake modifizieren. Dies funktioniert auch in Vorproduktionsumgebungen, wenn Sie mehrstufig arbeiten. Das Wesentliche? Verwenden Sie config vars, wenn Sie können.

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
Captainpete
quelle
2
Dies ist die schönste Lösung, verwendet die Capistrano-Werte,
sofern
2
Es lohnt sich wahrscheinlich hinzuzufügen, dass Sie, wenn Ihre Aufgabe einen Namespace hat (dh nicht im Namespace der obersten Ebene definiert ist), möglicherweise top.runanstelle von nurrun
dolzenko
Danke @dolzenko. Habetop gerade die Dokumente für die Methode gefunden . In dem Fall, in dem wir runim selben Namespace definiert haben, top.runist dies erforderlich, andernfalls sollte die oberste Ebene runauch dann gefunden werden, wenn die Aufgabe einen Namespace hat. Habe ich etwas verpasst Was ist in deinem Fall passiert?
Captainpete
1
Ich hatte offensichtlich keine Ausführungsmethode im selben Namespace definiert, daher bin ich mir nicht sicher, warum ich das brauchte. In jedem Fall ist Capistrano 2.0 eine Geschichte und die nächste Version basiert auf Rake (was die Dinge hoffentlich vorhersehbarer macht)
Dolzenko
16

Benutze den capistrano-rakeEdelstein

Installieren Sie das Juwel einfach, ohne sich mit benutzerdefinierten Capistrano-Rezepten herumzuschlagen, und führen Sie die gewünschten Rake-Aufgaben auf Remote-Servern wie folgt aus:

cap production invoke:rake TASK=my:rake_task

Vollständige Offenlegung: Ich habe es geschrieben

Sheharyar
quelle
7

Ich persönlich verwende in der Produktion eine Hilfsmethode wie diese:

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

Dies ermöglicht das Ausführen einer Rechenaufgabe ähnlich der Verwendung der Methode run (Befehl).


HINWEIS: Es ähnelt dem, was Duke vorgeschlagen hat, aber ich:

  • Verwenden Sie "latest_release" anstelle von "current_release". Nach meiner Erfahrung ist dies eher das, was Sie erwarten, wenn Sie einen Rake-Befehl ausführen.
  • Folgen Sie der Namenskonvention von Rake und Capistrano (anstelle von: cmd -> task und rake -> run_rake)
  • Setzen Sie RAILS_ENV = # {rails_env} nicht, da der richtige Ort zum Festlegen die Variable default_run_options ist. ZB default_run_options [: env] = {'RAILS_ENV' => 'Produktion'} # -> DRY!
Szymon Jeż
quelle
5

Es ist ein interessantes Juwel Umhang , die Ihre Rake Aufgaben als Capistrano Aufgaben macht, so können Sie sie remote ausgeführt. capeist gut dokumentiert, aber hier ist eine kurze Übersicht über die Einrichtung von i.

Fügen Sie dies nach der Installation des Edelsteins einfach Ihrer config/deploy.rbDatei hinzu.

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

Jetzt können Sie alle Ihre rakeAufgaben lokal oder remote ausführen cap.

Als zusätzlichen Bonus capekönnen Sie festlegen, wie Sie Ihre Rake-Aufgabe lokal und remote ausführen möchten (nicht mehr bundle exec rake). Fügen Sie dies einfach zu Ihrer config/deploy.rbDatei hinzu:

# 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
quelle
Hinweis: Funktioniert nur für Capistrano v2.x. Nicht kompatibel mit 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
quelle
1
Gut. Wenn Sie es von RAILS_ENV=productionauf RAILS_ENV=#{rails_env}ändern, funktioniert es auch auf meinem Staging-Server.
evanrmurphy
2

Folgendes habe ich in meine deploy.rb eingefügt, um das Ausführen von Rake-Aufgaben zu vereinfachen. Es ist ein einfacher Wrapper um die run () -Methode von 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

Dann führe ich einfach eine Rechenaufgabe wie folgt aus:

rake 'app:compile:jammit'
Herzog
quelle
Dies steht in Konflikt, da Capistrano seine eigene Rake-Variable definiert (die verwendet wird, um zu bestimmen, welcher Rake verwendet werden soll) und somit eingebaute Belege aufbricht, beispielsweise diejenige, die Assets vorkompiliert
Michael,
2

Das hat bei mir funktioniert:

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

Dann einfach laufen cap production "invoke[task_name]"

Abram
quelle
1

Das meiste davon kommt von oben mit einer kleinen Verbesserung, um jede Rechenaufgabe von Capistrano aus auszuführen

Führen Sie eine beliebige Rechenaufgabe von Capistrano aus

$ 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
quelle
1

Das funktioniert auch:

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

Weitere Infos: Capistrano Run

acw
quelle
1
{deploy_to} / current funktioniert hier nicht. Die symbolische Verknüpfung hat sich nicht geändert. Wenn Sie die Rake-Task aktualisieren, wird alter Code ausgeführt. Verwenden Sie stattdessen {release_path}.
Mark Redding
Je mehr Infos ist Spam?
Hcarreras
1

Wenn Sie mehrere Argumente übergeben möchten, versuchen Sie Folgendes (basierend auf der Antwort von 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

Dann können Sie eine Aufgabe wie folgt ausführen: cap production invoke["task","arg1","arg2"]

Robin Clowers
quelle
0

Also habe ich daran gearbeitet. es scheint gut zu funktionieren. Sie benötigen jedoch einen Formatierer, um den Code wirklich nutzen zu können.

Wenn Sie keinen Formatierer verwenden möchten, setzen Sie die Protokollebene einfach auf den Debug-Modus. Diese semas zu 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

Dies ist der Formatierer, den ich erstellt habe, um mit dem obigen Code zu arbeiten. Es basiert auf dem in das sshkit integrierten: Textsimple, ist jedoch keine schlechte Methode, um benutzerdefinierte Aufgaben aufzurufen. Oh, so viele funktionieren nicht mit der neuesten Version von sshkit gem. Ich weiß, dass es mit 1.7.1 funktioniert. Ich sage dies, weil der Hauptzweig die verfügbaren SSHKit :: Command-Methoden geändert hat.

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
quelle
0

Frühere Antworten haben mir nicht geholfen und ich fand dies: Von 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

um deine Aufgabe auszuführen, benutze

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

Vielleicht ist es für jemanden nützlich

A. Miroshnichenko
quelle