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.


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

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

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


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.


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

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


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!

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'

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 

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'

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]"


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

1

Das funktioniert auch:

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

Weitere Infos: Capistrano Run


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"]


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

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.