如何从 Capistrano 运行 rake 任务?
Posted
技术标签:
【中文标题】如何从 Capistrano 运行 rake 任务?【英文标题】:How do I run a rake task from Capistrano? 【发布时间】:2010-09-23 15:44:59 【问题描述】:我已经有一个 deploy.rb 可以在我的生产服务器上部署我的应用程序。
我的应用程序包含一个自定义 rake 任务(lib/tasks 目录中的 .rake 文件)。
我想创建一个远程运行该 rake 任务的 cap 任务。
【问题讨论】:
有人能解释一下使用 capistrano 自己的#rake
变量的优缺点吗?似乎它并不总是最好的选择。
【参考方案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 && #rake <task_name> 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),因此会破坏内置的接收器,例如预编译资产的接收器以上是关于如何从 Capistrano 运行 rake 任务?的主要内容,如果未能解决你的问题,请参考以下文章