Как запустить рейк из Capistrano?


105

У меня уже есть deploy.rb, который может развернуть мое приложение на моем производственном сервере.

Мое приложение содержит настраиваемую задачу rake (файл .rake в каталоге lib / tasks).

Я хотел бы создать задачу ограничения, которая будет удаленно запускать эту задачу рейка.


2
Может ли кто-нибудь объяснить плюсы / минусы использования собственной #{rake}переменной capistrano ? Кажется, это не всегда лучший вариант.
lulalala

Ответы:


59

Немного более явным, в вашем \config\deploy.rb, добавьте вне любой задачи или пространства имен:

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

Затем из /rails_root/, вы можете запустить:

cap staging rake:invoke task=rebuild_table_abc

1
лучше использовать / usr / bin / env rake, чтобы настройки rvm подбирали правильный рейк.
DGM

8
С 'bundle exec', если доступно
Богдан Гусиев

44

... пару лет спустя ...

Взгляните на плагин rails capistrano, вы можете увидеть на https://github.com/capistrano/rails/blob/master/lib/capistrano/tasks/migrations.rake#L5-L14 он может выглядеть примерно так:

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
Только для capistrano v3.
phillbaker

Очень помогли. Спасибо! @Mirek Rusin
Нишант Шривастава

другие ответы, что использование runбудет работать на capistrano до версии 2. начиная с версии 3 это путь.
Дон Джулио

44

Capistrano 3 Generic Version (запускать любую задачу грабли)

Создание общей версии ответа Мирека Русина:

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

Пример использования: cap staging "invoke[db:migrate]"

Обратите внимание, что deploy:set_rails_envтребуется от драгоценного камня capistrano-rails


1
Это поддерживает только один аргумент, если вы замените его rake args[:command] на, execute :rake, "#{args.command}[#{args.extras.join(",")}]" вы можете выполнить задачу с несколькими аргументами, например: cap production invoke["task","arg1","arg2"]
Робин Клоуэрс

1
@ Robin Clowers Вы можете передать несколько аргументов, например cap staging invoke['task[arg1\,arg2]']. Я предпочитаю этот подход тому, который вы упомянули, потому что он отражает фактический вызов rake. При таком подходе вы можете также приковать несколько задач, которые часто полезны: cap staging invoke['task1 task2[arg1] task3[arg2\,arg3]']. Работает для rake 10.2.0 и новее
мариносб

это здорово - я хотел бы отметить, что вам нужно включить: app в качестве одной из ролей сервера.
lfender6445

Очевидно, это нужно было "invoke [db: migrate]" ... Исправление сделано.
Абрам

@Abram с командой, которую вы предложили, я получаю «Не знаю, как создать задачу 'invoke»
dc10

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

Нашел с помощью Google - http://ananelson.com/said/on/2007/12/30/remote-rake-tasks-with-capistrano/

Это RAILS_ENV=productionбыла ошибка - я сначала не подумал об этом и не мог понять, почему задача ничего не делает.


2
Незначительное улучшение: если вы замените точку с запятой на &&, то второй оператор (запускающий задачу rake) не будет запущен, если первый оператор (изменение каталога) завершится неудачно.
Teflon Ted

2
Это не сработает, если вы выполняете развертывание на нескольких серверах. Он будет запускать задачу rake несколько раз.
Марк Реддинг,

4
нужно действительно уважать настройку граблей capistrano"cd #{deploy_to}/current && #{rake} <task_name> RAILS_ENV=production"
kares

@Mark Redding: Не могли бы вы назначить одному из серверов отдельную роль для выполнения задач rake и ограничить выполнение задачи capistrano только на серверах с этой ролью?
mj1531

Я что-то сделал, где создал задачу в своем deploy.rb. В этой задаче есть: role =>: db, поэтому она будет выполняться только на том же сервере, который я определил как основной для db: migrate.
Марк Реддинг

20

Используйте призывы граблей в стиле Капистрано

Есть общий способ, который "просто работает" с require 'bundler/capistrano'другими расширениями, изменяющими рейк. Это также будет работать с предпроизводственной средой, если вы используете многоступенчатую версию. Суть? По возможности используйте переменные конфигурации.

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
Это лучшее решение, где используются значения capistrano, если они доступны
loopj

2
Вероятно, стоит добавить, что если ваша задача имеет пространство имен (то есть определена не в пространстве имен верхнего уровня), вам, возможно, придется использовать top.runвместо простоrun
dolzenko

Спасибо @dolzenko. Только что нашел документацию по topметоду . В случае, когда мы определили runв том же пространстве имен, top.runтребуется, иначе он все равно должен найти верхний уровень, runдаже если задача находится в пространстве имен. Я что-то упустил? Что случилось в вашем случае?
captainpete 03

1
У меня явно не было метода запуска, определенного в том же пространстве имен, поэтому я не уверен, зачем мне это нужно. В любом случае Capistrano 2.0 - это история, а следующая версия основана на Rake (надеюсь, что делает вещи более предсказуемыми)
dolzenko

16

Используйте capistrano-rakeдрагоценный камень

Просто установите драгоценный камень, не вмешиваясь в пользовательские рецепты капистрано, и выполните желаемые задачи по рейку на удаленных серверах, например:

cap production invoke:rake TASK=my:rake_task

Полное раскрытие: я написал это


7

Я лично использую в производстве такой вспомогательный метод:

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

Это позволяет запускать задачу rake аналогично использованию метода run (command).


ПРИМЕЧАНИЕ: это похоже на то, что предложил Дюк , но я:

  • используйте latest_release вместо current_release - по моему опыту, это больше, чем вы ожидаете при запуске команды rake;
  • следуйте соглашению об именах Rake и Capistrano (вместо: cmd -> task и rake -> run_rake)
  • не устанавливайте RAILS_ENV = # {rails_env}, потому что правильное место для установки - это переменная default_run_options. Например, default_run_options [: env] = {'RAILS_ENV' => 'production'} # -> DRY!

5

Есть интересная жемчужная накидка, которая делает ваши рейк-задачи доступными как задачи Capistrano, так что вы можете запускать их удаленно. capeхорошо документирован, но вот краткий обзор того, как настроить i.

После установки драгоценного камня просто добавьте его в свой config/deploy.rbфайл.

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

Теперь вы можете запускать все свои rakeзадачи локально или удаленно cap.

В качестве дополнительного бонуса capeвы можете указать, как вы хотите запускать задачу rake локально и удаленно (не более bundle exec rake), просто добавьте это в свой config/deploy.rbфайл:

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

Примечание: работает только для Capistrano v2.x. Не совместим с 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
Хорошо. Изменение его с RAILS_ENV=productionна RAILS_ENV=#{rails_env}позволяет ему работать и на моем промежуточном сервере.
evanrmurphy

2

Вот что я вложил в свой deploy.rb, чтобы упростить выполнение задач rake. Это простая оболочка метода 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

Затем я просто запускаю любую задачу с граблями, например:

rake 'app:compile:jammit'

это конфликтует, поскольку capistrano определяет свою собственную переменную рейка (используется для определения того, какой рейк использовать) и, таким образом, нарушает встроенные квитанции, например, тот, который предварительно компилирует активы
Майкл

2

Это сработало для меня:

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

Тогда просто запустите cap production "invoke[task_name]"


1

По большей части это ответ сверху с незначительным улучшением для запуска любой задачи по рейку из capistrano.

Запустите любую задачу по граблям от 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

Это тоже работает:

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

Более подробная информация: Capistrano Run


1
{deploy_to} / current здесь не работает. Символьная ссылка не изменилась. Если вы обновите задачу rake, будет запущен старый код. Вместо этого рассмотрите возможность использования {release_path}.
Марк Реддинг

чем больше инфо спам?
hcarreras 08

1

Если вы хотите передать несколько аргументов, попробуйте следующее (на основе ответа мариносберна):

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

Затем вы можете запустить такую ​​задачу: cap production invoke["task","arg1","arg2"]


0

Итак, я работал над этим. швы работают хорошо. Однако вам нужен форматировщик, чтобы действительно воспользоваться преимуществами кода.

Если вы не хотите использовать форматировщик, просто установите уровень журнала в режим отладки. Эти семас к ч

SSHKit.config.output_verbosity = Logger::DEBUG

Шапка

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

Это средство форматирования, которое я построил для работы с приведенным выше кодом. Он основан на :textimple, встроенном в sshkit, но это неплохой способ вызывать пользовательские задачи. О, это много не работает с последней версией sshkit gem. Я знаю, что он работает с 1.7.1. Я говорю это потому, что главная ветвь изменила доступные методы 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

Предыдущие ответы мне не помогли, и я нашел это: Из 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

для запуска вашей задачи используйте

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

Может кому будет полезно

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.