【问题标题】:How do I run a rake task from Capistrano?如何从 Capistrano 运行 rake 任务?
【发布时间】:2010-09-23 15:44:59
【问题描述】:

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

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

我想创建一个远程运行该 rake 任务的 cap 任务。

【问题讨论】:

  • 有人能解释一下使用 capistrano 自己的 #{rake} 变量的优缺点吗?似乎它并不总是最好的选择。

标签: ruby rake capistrano


【解决方案1】:

以前的答案对我没有帮助,我发现了这个: 来自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

也许对某人有用

【讨论】:

    【解决方案2】:

    使用capistrano-rake gem

    只需安装 gem 而不会弄乱自定义 capistrano 配方并在远程服务器上执行所需的 rake 任务,如下所示:

    cap production invoke:rake TASK=my:rake_task
    

    完全披露:我写的

    【讨论】:

      【解决方案3】:

      更明确一点,在您的\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
      

      【讨论】:

      • 最好使用 /usr/bin/env rake,这样 rvm 设置将获得正确的 rake。
      • 如果有'bundle exec'
      【解决方案4】:

      这对我有用:

      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]"

      【讨论】:

        【解决方案5】:

        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

        【讨论】:

        • 这仅支持单个参数,如果您将rake args[:command] 替换为execute :rake, "#{args.command}[#{args.extras.join(",")}]",您可以执行具有多个参数的任务,如下所示:cap production invoke["task","arg1","arg2"]
        • @Robin Clowers 您可以传递多个参数,例如cap staging invoke['task[arg1\,arg2]']。我更喜欢这种方法而不是您提到的方法,因为它反映了 rake 的实际调用。使用这种方法,您还可以链接多个任务,这通常很有用:cap staging invoke['task1 task2[arg1] task3[arg2\,arg3]']。适用于 rake 10.2.0 或更高版本
        • 这很棒 - 我想指出,您需要将 :app 作为您的服务器角色之一。
        • 显然这需要是 "invoke[db:migrate]" ... 已更正。
        • @Abram 使用您建议的命令我得到“不知道如何构建任务 'invoke”
        【解决方案6】:

        所以我一直在努力。它看起来运作良好。但是,您需要一个格式化程序才能真正利用代码。

        如果您不想使用格式化程序,只需将日志级别设置为调试模式。这些 sema 到 h

        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
        

        【讨论】:

          【解决方案7】:

          如果您希望能够传递多个参数,试试这个(基于 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"]

          【讨论】:

            【解决方案8】:

            这也有效:

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

            更多信息:Capistrano Run

            【讨论】:

            • {deploy_to}/current 在这里不起作用。符号链接没有改变。如果您更新 rake 任务,这将运行旧代码。考虑改用 {release_path}。
            • 垃圾信息越多?
            【解决方案9】:

            ...几年后...

            看看 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
            

            【讨论】:

            • 这仅适用于 capistrano v3。
            • 帮助很大。谢谢! @Mirek Rusin
            • 其他使用 run 的回复将在 capistrano 上工作,直到版本 2。从版本 3 开始,这是要走的路。
            【解决方案10】:

            其中大部分来自above answer,并进行了小幅增强,可以从 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
            

            【讨论】:

              【解决方案11】:
              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 是一个陷阱——我一开始并没有想到它,也无法弄清楚为什么任务没有做任何事情。

              【讨论】:

              • 一个小改进:如果用 && 替换分号,那么如果第一个语句(更改目录)失败,则第二个语句(运行 rake 任务)将不会运行。
              • 如果您要部署到多台服务器,这将不起作用。它将多次运行 rake 任务。
              • 应该尊重 capistrano 的 rake 设置 "cd #{deploy_to}/current &amp;&amp; #{rake} &lt;task_name&gt; RAILS_ENV=production"
              • @Mark Redding:您能否将其中一台服务器置于其自己的角色以执行 rake 任务,并将您的 capistrano 任务限制为仅在具有该角色的服务器上运行?
              • 我在 deploy.rb 中创建了一个任务。该任务上有一个 :roles => :db ,因此它只会在我定义为 db:migrate 的主要服务器上执行。
              【解决方案12】:
              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 
              

              【讨论】:

              • 好。将其从 RAILS_ENV=production 更改为 RAILS_ENV=#{rails_env} 使其也可以在我的登台服务器上工作。
              【解决方案13】:

              使用 Capistrano 风格的 rake 调用

              有一种常见的方法可以“正常工作”require 'bundler/capistrano' 和其他修改 rake 的扩展。如果您使用多阶段,这也适用于预生产环境。要点?如果可以,请使用配置变量。

              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
              

              【讨论】:

              • 这是最好的解决方案,在可用的地方使用 capistrano 值
              • 可能值得补充的是,如果您的任务是命名空间(即未在顶级命名空间中定义),您可能必须使用 top.run 而不仅仅是 run
              • 谢谢@dolzenko。刚刚找到docs for the top method。在我们在同一个命名空间中定义run 的情况下,top.run 是必需的,否则即使任务在命名空间中,它仍然应该找到顶级run。我错过了什么吗?你的情况发生了什么?
              • 我显然没有在同一个命名空间中定义任何运行方法,所以不确定我为什么需要它。无论如何,Capistrano 2.0 已经成为历史,而下一个版本是基于 Rake 的(希望让事情更可预测)
              【解决方案14】:

              有一个有趣的 gem cape 可以让您的 rake 任务作为 Capistrano 任务使用,因此您可以远程运行它们。 cape 有据可查,但这里是关于如何设置 i 的简短概述。

              安装 gem 后,只需将其添加到您的 config/deploy.rb 文件中即可。

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

              现在,您可以在本地或通过cap 远程运行所有rake 任务。

              作为额外的奖励,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 不兼容。
              【解决方案15】:

              我个人在生产中使用这样的辅助方法:

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

              这允许运行 rake 任务,类似于使用 run(命令)方法。


              注意:这类似于Duke 提出的建议,但我:

              • 使用 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!

              【讨论】:

                【解决方案16】:

                这是我在 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),因此会破坏内置的接收器,例如预编译资产的接收器
                猜你喜欢
                • 2020-01-17
                • 2015-04-15
                • 2010-10-09
                • 2014-07-12
                • 2011-10-06
                • 2011-05-10
                • 1970-01-01
                • 2012-11-15
                • 1970-01-01
                相关资源
                最近更新 更多