Jak uruchomić zadanie prowizji od Capistrano?


105

Mam już plik deploy.rb, który może wdrożyć moją aplikację na serwerze produkcyjnym.

Moja aplikacja zawiera niestandardowe zadanie prowizji (plik .rake w katalogu lib / jobs).

Chciałbym utworzyć zadanie cap, które będzie zdalnie uruchamiało to zadanie rake.


2
Czy ktoś może wyjaśnić zalety / wady używania własnej #{rake}zmiennej Capistrano ? Wydaje się, że nie zawsze jest to najlepsza opcja.
lulalala

Odpowiedzi:


59

Nieco bardziej wyraźne, w swoim \config\deploy.rb, dodaj poza jakimkolwiek zadaniem lub przestrzenią nazw:

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

Następnie /rails_root/możesz uruchomić:

cap staging rake:invoke task=rebuild_table_abc

1
lepiej użyć / usr / bin / env rake, aby setup rvm pobierał właściwą rake.
DGM

8
Z „bundle exec”, jeśli jest dostępne
Bogdan Gusiev

44

... kilka lat później ...

Spójrz na wtyczkę rails capistrano, możesz zobaczyć na https://github.com/capistrano/rails/blob/master/lib/capistrano/tasks/migrations.rake#L5-L14 może wyglądać mniej więcej tak:

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
Dotyczy to tylko Capistrano v3.
phillbaker

Pomógł dużo. Dzięki! @Mirek Rusin
Nishant Shrivastava

inne odpowiedzi, że użycie runbędzie działać na capistrano do wersji 2. od wersji 3 to jest droga.
Don Giulio

44

Capistrano 3 Generic Version (uruchom dowolne zadanie rake)

Tworzenie ogólnej wersji odpowiedzi Mirka Rusina:

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

Przykładowe użycie: cap staging "invoke[db:migrate]"

Zauważ, że deploy:set_rails_envwymagania pochodzą z klejnotu capistrano-rails


1
To urządzenie obsługuje tylko jeden argument, jeśli zastąpić rake args[:command] z execute :rake, "#{args.command}[#{args.extras.join(",")}]" was może wykonywać zadania z wieloma argumentami tak: cap production invoke["task","arg1","arg2"]
Robin Clowers

1
@ Robin Clowers Możesz przekazać wiele argumentów, np cap staging invoke['task[arg1\,arg2]']. Wolę to podejście od tego, o którym wspominasz, ponieważ odzwierciedla faktyczne wywołanie rake. Dzięki takiemu podejściu można również łańcuch wielu zadań, które często przydatne: cap staging invoke['task1 task2[arg1] task3[arg2\,arg3]']. Działa dla rake'a 10.2.0 lub nowszego
marinosb

to świetnie - chciałbym zauważyć, że musisz uwzględnić: aplikację jako jedną z ról serwera.
lfender6445

Najwyraźniej musiało to być „invoke [db: migrate]”… Dokonano korekty.
Abram

@Abram z poleceniem zasugerował otrzymuję „Nie wiem, jak zbudować zadanie„powołaniem”
DC10

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

Znalazłem go w Google - http://ananelson.com/said/on/2007/12/30/remote-rake-tasks-with-capistrano/

To RAILS_ENV=productionbył problem - na początku o tym nie pomyślałem i nie mogłem zrozumieć, dlaczego zadanie nic nie robi.


2
Drobne ulepszenie: jeśli zamienisz średnik na &&, druga instrukcja (uruchamiająca zadanie rake) nie zostanie uruchomiona, jeśli pierwsza instrukcja (zmiana katalogu) zawiedzie.
Teflon Ted

2
To nie zadziała, jeśli wdrażasz na wielu serwerach. Będzie wielokrotnie uruchamiać zadanie prowizji.
Mark Redding

4
należy naprawdę uszanować ustawienie prowizji "cd #{deploy_to}/current && #{rake} <task_name> RAILS_ENV=production"
Capistrano

@Mark Redding: Czy mógłbyś przypisać jednemu z serwerów jego własną rolę do zadań rake'u i ograniczyć swoje zadanie capistrano do uruchamiania tylko na serwerach z tą rolą?
mj1531

Zrobiłem coś, gdzie utworzyłem zadanie w pliku deploy.rb. To zadanie ma: roles =>: db tak, że będzie wykonywane tylko na tym samym serwerze, który zdefiniowałem jako mój podstawowy dla db: migrate.
Mark Redding

20

Użyj wywołań prowizji w stylu Capistrano

Istnieje powszechny sposób, który „po prostu działa” z require 'bundler/capistrano'innymi rozszerzeniami, które modyfikują rake. Będzie to również działać w środowiskach przedprodukcyjnych, jeśli korzystasz z wielu etapów. Istota? Jeśli możesz, użyj zmiennych konfiguracyjnych.

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
To jest najładniejsze rozwiązanie, używa wartości
kapistrano

2
Prawdopodobnie warto dodać, że jeśli twoje zadanie ma przestrzeń nazw (tj. Zdefiniowane nie w przestrzeni nazw najwyższego poziomu), być może będziesz musiał użyć top.runzamiast po prosturun
dolzenko

Dzięki @dolzenko. Właśnie znalazłem dokumentację dla topmetody . W przypadku, gdy zdefiniowaliśmy runw tej samej przestrzeni nazw, top.runjest to wymagane, w przeciwnym razie nadal powinno znaleźć najwyższy poziom, runnawet jeśli zadanie ma przestrzeń nazw. Czy coś przegapiłem? Co się stało w twoim przypadku?
captainpete

1
Najwyraźniej nie miałem zdefiniowanej żadnej metody uruchamiania w tej samej przestrzeni nazw, więc nie jestem pewien, dlaczego tego potrzebowałem. W każdym razie Capistrano 2.0 to już historia, a następna wersja jest oparta na
Rake'u (

16

Użyj capistrano-rakeklejnotu

Po prostu zainstaluj klejnot bez mieszania niestandardowych receptur Capistrano i wykonaj żądane zadania rake na zdalnych serwerach, takie jak ten:

cap production invoke:rake TASK=my:rake_task

Pełne ujawnienie: napisałem to


7

Osobiście używam w produkcji takiej metody pomocniczej:

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

Pozwala to na uruchomienie zadania rake podobnie jak przy użyciu metody run (polecenie).


UWAGA: jest podobny do tego, co zaproponował Duke , ale ja:

  • użyj funkcji latest_release zamiast current_release - z mojego doświadczenia wynika, że ​​jest to więcej, czego oczekujesz, wykonując polecenie rake;
  • postępuj zgodnie z konwencją nazewnictwa Rake i Capistrano (zamiast: cmd -> task and rake -> run_rake)
  • nie ustawiaj RAILS_ENV = # {rails_env}, ponieważ właściwym miejscem do ustawienia jest zmienna default_run_options. Np. Default_run_options [: env] = {'RAILS_ENV' => 'produkcja'} # -> SUCHA!

5

Istnieje interesująca peleryna z klejnotami, która sprawia, że ​​zadania rake są dostępne jako zadania Capistrano, więc możesz je uruchamiać zdalnie. capejest dobrze udokumentowany, ale oto krótki przegląd tego, jak go skonfigurować.

Po zainstalowaniu klejnotu po prostu dodaj go do swojego config/deploy.rbpliku.

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

Teraz możesz uruchamiać wszystkie swoje rakezadania lokalnie lub zdalnie cap.

Jako dodatkowy bonus capepozwala ustawić, w jaki sposób chcesz uruchamiać zadanie prowizji lokalnie i zdalnie (nie więcej bundle exec rake), po prostu dodaj to do swojego config/deploy.rbpliku:

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

Uwaga: działa tylko dla Capistrano v2.x. Nie jest kompatybilny z 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
Dobry. Zmiana z RAILS_ENV=productionna RAILS_ENV=#{rails_env}umożliwia działanie również na moim serwerze pomostowym.
evanrmurphy

2

Oto, co umieściłem w pliku deploy.rb, aby uprościć wykonywanie zadań rake. Jest to proste opakowanie wokół metody run () 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

Następnie po prostu uruchamiam takie zadanie rake'u:

rake 'app:compile:jammit'

to jest sprzeczne, ponieważ capistrano definiuje swoją własną zmienną rake (używaną do określenia, której prowizji użyć), a tym samym łamie wbudowane wpływy, na przykład tę, która prekompiluje aktywa
Michael

2

To zadziałało dla mnie:

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

Następnie po prostu biegnij cap production "invoke[task_name]"


1

Większość z nich pochodzi z powyższej odpowiedzi z niewielkim ulepszeniem, aby uruchomić dowolne zadanie rake z capistrano

Uruchom dowolne zadanie prowizji od 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

Działa to również:

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

Więcej informacji: Capistrano Run


1
{deploy_to} / current nie będzie tutaj działać. Dowiązanie symboliczne nie uległo zmianie. Jeśli zaktualizujesz zadanie rake, uruchomi to stary kod. Zamiast tego rozważ użycie {release_path}.
Mark Redding

im więcej informacji to spam?
hcarreras

1

Jeśli chcesz mieć możliwość przekazywania wielu argumentów, spróbuj tego (w oparciu o odpowiedź 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

Następnie możesz uruchomić takie zadanie: cap production invoke["task","arg1","arg2"]


0

Więc pracowałem nad tym. wydaje się działać dobrze. Jednak potrzebujesz formatera, aby naprawdę wykorzystać kod.

Jeśli nie chcesz używać programu formatującego, po prostu ustaw poziom dziennika na tryb debugowania. Te semestry do 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

To jest formater, który zbudowałem do pracy z powyższym kodem. Opiera się na: textimple wbudowanym w sshkit, ale nie jest to zły sposób wywoływania zadań niestandardowych. Och, to wiele nie działa z najnowszą wersją klejnotu sshkit. Wiem, że działa z 1.7.1. Mówię to, ponieważ gałąź główna zmieniła dostępne metody SSHKit :: Command.

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

Poprzednie odpowiedzi nie pomogły mi i znalazłem to: Z 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

do uruchomienia zadania użyj

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

Może komuś się przyda

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.