我如何从Capistrano运行抽佣任务?


105

我已经有一个deploy.rb,可以将我的应用程序部署到生产服务器上。

我的应用程序包含一个自定义的rake任务(lib / tasks目录中的.rake文件)。

我想创建一个上限任务,该任务将远程运行该rake任务。


2
有人可以解释使用capistrano自己的#{rake}变量的利弊吗?似乎并不总是最好的选择。
lulalala

Answers:


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设置可以获取正确的rake。
DGM 2010年

8
如果有的话,可以使用“捆绑执行程序”
Bogdan Gusiev 2011年

44

几年后

看看capistrano的rails插件,您可以在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 2014年

帮助了很多。谢谢!@Mirek Rusin
Nishant Shrivastava

其他答复是,该用法run将在capistrano上有效,直到版本2。从版本3开始,这是必须的方法。
Don Giulio

44

Capistrano 3通用版本(运行任何rake任务)

构建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

用法示例: cap staging "invoke[db:migrate]"

请注意,deploy:set_rails_env需求来自capistrano-rails gem


1
这仅支持一个参数,如果更换 rake args[:command]execute :rake, "#{args.command}[#{args.extras.join(",")}]" 您可以执行与多个参数的任务,像这样: cap production invoke["task","arg1","arg2"]
罗宾Clowers

1
@ Robin Clowers您可以传递多个参数,例如cap staging invoke['task[arg1\,arg2]']。我喜欢您提到的这种方法,因为它反映了rake的实际调用。通过这种方法,您还可以链接多个任务,这通常很有用:cap staging invoke['task1 task2[arg1] task3[arg2\,arg3]']。适用于rake 10.2.0或更高版本
marinosb,2014年

这很棒-我想指出,您需要包括:app作为您的服务器角色之一。
lfender6445

显然,这需要“调用[db:migrate]” ...进行更正。
亚伯兰

@Abram用命令你的建议,我得到“不知道如何建设任务“调用”
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任务)将不会运行。
铁氟龙泰德

2
如果要部署到多台服务器,则无法使用。它将多次运行rake任务。
Mark Redding

4
一个真的应该尊重Capistrano酒店的耙设置"cd #{deploy_to}/current && #{rake} <task_name> RAILS_ENV=production"
kares

@Mark Redding:您能否将其中一台服务器置于自己的角色,以执行rake任务,并限制您的capistrano任务仅在具有该角色的服务器上运行?
mj1531 2011年

我做了一些我在deploy.rb中创建任务的事情。该任务上带有:roles =>:db,因此它将仅在我定义为db:migrate主要服务器的同一服务器上执行。
Mark Redding

20

使用Capistrano样式的rake调用

有一种通用的方法可以“正常工作”,require 'bundler/capistrano'并且可以修改rake的其他扩展。如果您使用的是多阶段,这也将与预生产环境一起使用。要点?如果可以,请使用config vars。

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 2012年

2
可能值得补充的是,如果您的任务是命名空间的(即不在顶级命名空间中定义),则可能必须使用top.run而不是run
dolzenko

谢谢@dolzenko。刚刚找到top方法文档。如果我们run在同一个名称空间中定义,top.run则是必需的,否则run即使任务已命名空间,它仍应找到顶层。我错过了什么吗?您的情况如何?
船长

1
我显然没有在同一个名称空间中定义任何运行方法,因此不确定为什么需要它。无论如何,Capistrano 2.0是一个历史,下一个版本是基于Rake的(希望使事情更可预测)
dolzenko

16

使用capistrano-rake宝石

只需安装gem,而不会弄乱自定义的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)方法。


注意:它类似于Duke提出的内容,但我:

  • 使用latest_release而不是current_release-根据我的经验,这是您在执行rake命令时所期望的;
  • 遵循Rake和Capistrano的命名约定(而不是:cmd-> task and rake-> run_rake)
  • 不要设置RAILS_ENV =#{rails_env},因为设置它的正确位置是default_run_options变量。例如default_run_options [:env] = {'RAILS_ENV'=>'production'}#->干!

5

有一个有趣的宝石斗篷,可以将耙任务用作Capistrano任务,因此您可以远程运行它们。cape有充分的记录,但是这里是有关如何设置我的简短概述。

安装gem之后,只需将其添加到您的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'

注意:仅适用于Capistranov2.x。与Capistrano v3不兼容。
nayiaw 2015年

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=productionRAILS_ENV=#{rails_env}也可以在我的登台服务器上使用。
evanrmurphy

2

这是我放入deploy.rb中以简化运行rake任务的内容。这是围绕capistrano的run()方法的简单包装。

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任务:

rake 'app:compile:jammit'

由于capistrano定义了自己的rake变量(用于确定要使用哪个rake),因此会发生冲突,从而破坏了收据中内置的内容,例如预编译资产的收据
Michael

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的任何rake任务

从capistrano运行任何rake任务

$ 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}。
Mark Redding

更多信息是垃圾邮件?
hcarreras 2014年

1

如果您希望能够传递多个参数,请尝试以下操作(基于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

然后,您可以运行以下任务: 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

这是我为使用上面的代码而构建的格式化程序。它基于sshkit中内置的:textsimple,但这并不是调用自定义任务的不错方法。哦,这许多不适用于最新版本的sshkit gem。我知道它适用于1.7.1。我之所以这样说,是因为master分支更改了可用的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

也许对某人有用

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.