Come eseguo un'attività di rake da Capistrano?


105

Ho già un deploy.rb che può distribuire la mia app sul mio server di produzione.

La mia app contiene un'attività rake personalizzata (un file .rake nella directory lib / tasks).

Vorrei creare un'attività cap che eseguirà in remoto tale attività rake.


2
Qualcuno può spiegare i pro / contro dell'utilizzo della #{rake}variabile di capistrano ? Sembra che non sia sempre l'opzione migliore.
lulalala

Risposte:


59

Un po 'più esplicito, nel tuo \config\deploy.rb, aggiungi al di fuori di qualsiasi attività o spazio dei nomi:

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

Quindi, da /rails_root/, puoi eseguire:

cap staging rake:invoke task=rebuild_table_abc

1
meglio usare / usr / bin / env rake in modo che le impostazioni di rvm raccolgano il rake corretto.
DGM

8
Con 'bundle exec' se disponibile
Bogdan Gusiev

44

... un paio d'anni dopo ...

Dai un'occhiata al plug-in rails di capistrano, puoi vedere su https://github.com/capistrano/rails/blob/master/lib/capistrano/tasks/migrations.rake#L5-L14 può assomigliare a:

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
Questo è solo per capistrano v3.
phillbaker

Molto aiutato. Grazie! @Mirek Rusin
Nishant Shrivastava

le altre risposte, che l'uso runfunzionerà su capistrano fino alla versione 2. dalla versione 3 questa è la strada da percorrere.
Don Giulio

44

Capistrano 3 Generic Version (esegui qualsiasi attività rake)

Costruire una versione generica della risposta di 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

Utilizzo di esempio: cap staging "invoke[db:migrate]"

Nota che deploy:set_rails_envrichiede proviene dalla gemma capistrano-rails


1
Questo supporta solo un singolo argomento, se sostituisci rake args[:command] con execute :rake, "#{args.command}[#{args.extras.join(",")}]" puoi eseguire un'attività con più argomenti in questo modo: cap production invoke["task","arg1","arg2"]
Robin Clowers

1
@ Robin Clowers Puoi passare più argomenti, ad es cap staging invoke['task[arg1\,arg2]']. Preferisco questo approccio a quello che hai menzionato perché rispecchia l'effettiva invocazione di rake. Con questo approccio è anche possibile concatenare più attività, che è spesso utile: cap staging invoke['task1 task2[arg1] task3[arg2\,arg3]']. Funziona per rake 10.2.0 o più recente
marinosb

questo è fantastico - vorrei sottolineare che devi includere: app come uno dei tuoi ruoli server.
lfender6445

Apparentemente questo doveva essere "invoke [db: migrate]" ... Correzione fatta.
Abram

@ Abram con il comando che mi hai suggerito di ottenere "Non so come costruire l'attività 'invoca"
dc10

41
run("cd #{deploy_to}/current && /usr/bin/env rake `<task_name>` RAILS_ENV=production")

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

L' RAILS_ENV=productionera un Gotcha - non pensavo di esso in un primo momento e non riusciva a capire perché il compito non stava facendo niente.


2
Un piccolo miglioramento: se sostituisci il punto e virgola con &&, la seconda istruzione (che esegue il rake task) non verrà eseguita se la prima istruzione (cambiando la directory) fallisce.
Teflon Ted

2
Questo non funzionerà se stai distribuendo su più server. Verrà eseguito più volte l'attività rake.
Mark Redding

4
si dovrebbe davvero rispettare l'impostazione del rake di capistrano"cd #{deploy_to}/current && #{rake} <task_name> RAILS_ENV=production"
kares

@Mark Redding: potresti mettere uno dei server nel proprio ruolo per le attività di rake e limitare la tua attività di capistrano in modo che venga eseguita solo sui server con quel ruolo?
mj1531

Ho fatto qualcosa in cui ho creato un'attività nel mio deploy.rb. Quell'attività ha un: roles =>: db su di essa in modo tale che verrà eseguita solo sullo stesso server che ho definito come mio principale per db: migrate.
Mark Redding

20

Usa invocazioni rake in stile Capistrano

C'è un modo comune con cui "funzionerà" require 'bundler/capistrano'e altre estensioni che modificano rake. Funzionerà anche con gli ambienti di pre-produzione se utilizzi il multistadio. Il succo? Usa config vars se puoi.

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
Questa è la soluzione più
carina

2
Probabilmente vale la pena aggiungere che se la tua attività ha uno spazio dei nomi (cioè definito non nello spazio dei nomi di livello superiore) potresti dover usare top.runinvece di solorun
dolzenko

Grazie @dolzenko. Ho appena trovato i documenti per il topmetodo . Nel caso in cui abbiamo definito runlo stesso spazio dei nomi, top.runè obbligatorio, altrimenti dovrebbe comunque trovare il livello superiore runanche dove l'attività è spaziata dei nomi. Mi sono perso qualcosa? Cosa è successo nel tuo caso?
capitanpete

1
Chiaramente non avevo alcun metodo di esecuzione definito nello stesso spazio dei nomi, quindi non sono sicuro del motivo per cui ne avevo bisogno. In ogni caso Capistrano 2.0 è una storia e la prossima versione è basata su Rake (rendendo le cose più prevedibili, si spera)
dolzenko

16

Usa la capistrano-rakegemma

Basta installare la gemma senza perdere tempo con le ricette personalizzate di capistrano ed eseguire le attività di rake desiderate su server remoti come questo:

cap production invoke:rake TASK=my:rake_task

Full Disclosure: l'ho scritto


7

Personalmente utilizzo in produzione un metodo di supporto come questo:

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

Ciò consente di eseguire attività rake in modo simile all'utilizzo del metodo run (comando).


NOTA: è simile a quanto proposto da Duke , ma io:

  • usa latest_release invece di current_release - dalla mia esperienza è più quello che ti aspetti quando esegui un comando rake;
  • segui la convenzione di denominazione di Rake e Capistrano (invece di: cmd -> task e rake -> run_rake)
  • non impostare RAILS_ENV = # {rails_env} perché il posto giusto per impostarlo è la variabile default_run_options. Ad esempio default_run_options [: env] = {'RAILS_ENV' => 'production'} # -> DRY!

5

C'è un interessante mantello di gemme che rende le tue attività di rastrello disponibili come attività di Capistrano, quindi puoi eseguirle da remoto. capeè ben documentato, ma ecco una breve panoramica su come configurarlo.

Dopo aver installato la gemma, aggiungila al tuo config/deploy.rbfile.

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

Ora puoi eseguire tutte le rakeattività in locale o in remoto tramite cap.

Come bonus aggiuntivo, capeti consente di impostare il modo in cui desideri eseguire l'attività di rake localmente e da remoto (non di più bundle exec rake), aggiungilo al tuo config/deploy.rbfile:

# 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'

Nota: funziona solo per Capistrano v2.x. Non compatibile 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 

1
Buona. Cambiarlo da RAILS_ENV=productiona RAILS_ENV=#{rails_env}consente di funzionare anche sul mio server di staging.
evanrmurphy

2

Ecco cosa inserisco nel mio deploy.rb per semplificare l'esecuzione delle attività di rake. È un semplice involucro attorno al metodo run () di 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

Quindi eseguo qualsiasi attività di rake in questo modo:

rake 'app:compile:jammit'

questo è in conflitto poiché capistrano definisce la propria variabile di rake (utilizzata per determinare quale rake utilizzare) e quindi rompe le ricevute incorporate, ad esempio quella che precompila le risorse
Michael

2

Questo ha funzionato per me:

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

Quindi corri semplicemente cap production "invoke[task_name]"


1

La maggior parte è da sopra risposta con un piccolo miglioramento per eseguire qualsiasi attività di rake da capistrano

Esegui qualsiasi attività di rake da 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

1

Funziona anche:

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

Maggiori informazioni: Capistrano Run


1
{deploy_to} / current non funzionerà qui. Il collegamento simbolico non è cambiato. Se aggiorni l'attività rake, verrà eseguito il vecchio codice. Considera invece di utilizzare {release_path}.
Mark Redding

più informazioni sono spam?
hcarreras

1

Se vuoi essere in grado di passare più argomenti, prova questo (basato sulla risposta di 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

Quindi puoi eseguire un'attività in questo modo: cap production invoke["task","arg1","arg2"]


0

Quindi ci ho lavorato. sembra funzionare bene. Tuttavia è necessario un formatore per sfruttare davvero il codice.

Se non si desidera utilizzare un formattatore, è sufficiente impostare il livello di registro in modalità di debug. Questi sema alle 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

Questo è il formattatore che ho creato per funzionare con il codice sopra. Si basa sul: textimple integrato in sshkit ma non è un brutto modo per invocare attività personalizzate. Oh, molti non funzionano con la versione più recente di sshkit gem. So che funziona con 1.7.1. Dico questo perché il ramo master ha cambiato i metodi SSHKit :: Command che sono disponibili.

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

Le risposte precedenti non mi hanno aiutato e ho trovato questo: da 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

per eseguire la tua attività usa

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

Forse sarà utile per qualcuno

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.