Как запустить рейк из 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'
yacc
источник
Примечание: работает только для 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 
DuArme
источник
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

acw
источник
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

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

А.Мирошниченко
источник