在capistrano 3任务中声明和使用ruby函数

时间:2014-03-31 15:23:17

标签: nginx capistrano unicorn capistrano3 sshkit

我遇到了同样的问题:

How to call a Capistrano's task within another Capistrano's task?

然而,回滚到Capistrano v3.0.1和sshkit 1.0的解决方案对我来说不起作用。

使用this tutorial,我已经在lib / capistrano / tasks中声明了自定义任务,这些任务使用了存储在lib / capistrano /

中的.rb文件中的函数decalared

capfile:

# Load DSL and Setup Up Stages
require 'capistrano/setup'

# Includes default deployment tasks
require 'capistrano/deploy'

require 'capistrano/rails'

require 'capistrano/rvm'

# Loads custom tasks from `lib/capistrano/tasks' if you have any defined.
Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }

# Loads custom tasks from all folders below `lib/capistrano' if you have any defined.
Dir.glob('lib/capistrano/**/*.rb').each { |r| import r }

# because the above didn't quite look right to me, and all the .rbs are in /lib/capistrano
Dir.glob('lib/capistrano/*.rb').each { |r| import r }

deploy.rb

set :application, 'hello-rails'
set :app_shortname, 'hr'
set :repo_url, 'git@bitbutcket.org:me/myapp.git'  #<-substituted fake info here for this post

# Default value for :linked_files is []
set :linked_files, %w{config/database.yml config/application.yml}

# Default value for linked_dirs is []
 set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}

# everything below this comment adapted from the talkingquickly tutorial
#which config files should be copied by deploy:setup_config
set(:config_files, %w(
  nginx.conf
  unicorn.rb
  unicorn_init.sh
))

# which config files should be made executable after copying
# by deploy:setup_config
set(:executable_config_files, %w(unicorn_init.sh))

# files which need to be symlinked
set(:symlinks, [
  {
    source: "nginx.conf",
    link: "/etc/nginx/sites-enabled/{{full_app_name}}"
  },
  {
    source: "unicorn_init.sh",
    link: "/etc/init.d/unicorn_{{full_app_name}}"
  }
])

#specify my ruby version
set :rvm_type, :system

#specify my gemset
set :rvm_ruby_version, '1.9.3-p385@hello-rails'

namespace :deploy do
  # make sure we're deploying what we think we're deploying
  before :deploy, "deploy:check_revision"
  # only allow a deploy with passing tests to deployed
  before :deploy, "deploy:run_tests"
  # compile assets locally then rsync
  after 'deploy:symlink:shared', 'deploy:compile_assets_locally'
  after :finishing, 'deploy:cleanup'
end

配置/ staging.rb

set :stage, :staging
set :branch, "cap-rails"
set :rails_env, :test

# used in case we're deploying multiple versions of the same
# app side by side. Also provides quick sanity checks when looking
# at filepaths
set :full_app_name, "#{fetch(:app_shortname)}_#{fetch(:stage)}"

set :deploy_to, "/srv/#{fetch(:app_shortname)}"

# number of unicorn workers
set :unicorn_worker_count, 5

# For building nginx config file
set :enable_ssl, false

# extended properties on the server.
server 'dev', user: 'deployer', roles: %w{web app}  #<-substituted fake info here for this post

# custom ssh options
 set :ssh_options, {
   user: 'deployer',  #<-substituted fake info here for this post
   keys: %w(path.to.key),  #<-substituted fake info here for this post
   forward_agent: true,
   auth_methods: %w(publickey)
 }

LIB / Capistrano的/任务/ setup_config.cap

namespace :deploy do
  task :setup_config do
    on roles(:app) do
      # make the config dir
      execute :mkdir, "-p #{shared_path}/config"
      full_app_name = fetch(:full_app_name)

      # config files to be uploaded to shared/config, see the
      # definition of smart_template for details of operation.
      # Essentially looks for #{filename}.erb in deploy/#{full_app_name}/
      # and if it isn't there, falls back to deploy/#{shared}. Generally
      # everything should be in deploy/shared with params which differ
      # set in the stage files
      config_files = fetch(:config_files)
      config_files.each do |file|
        smart_template file
      end

      # which of the above files should be marked as executable
      executable_files = fetch(:executable_config_files)
      executable_files.each do |file|
        execute :chmod, "+x #{shared_path}/config/#{file}"
      end

      # symlink stuff which should be... symlinked
      symlinks = fetch(:symlinks)

      symlinks.each do |symlink|
        sudo "ln -nfs #{shared_path}/config/#{symlink[:source]} #{sub_strings(symlink[:link])}"
      end
    end
  end
end

LIB / Capistrano的/ template.rb

def smart_template(from, to=nil)
  to ||= from
  full_to_path = "#{shared_path}/config/#{to}"
  if from_erb_path = template_file(from)
    from_erb = StringIO.new(ERB.new(File.read(from_erb_path)).result(binding))
    upload! from_erb, full_to_path
    info "copying: #{from_erb} to: #{full_to_path}"
  else
    error "error #{from} not found"
  end
end

def template_file(name)
  if File.exist?((file = "config/deploy/#{fetch(:full_app_name)}/#{name}.erb"))
    return file
  elsif File.exist?((file = "config/deploy/shared/#{name}.erb"))
    return file
  end
  return nil
end

的Gemfile

    source 'https://rubygems.org'

    # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
    gem 'rails', '4.0.4'

    # Use sqlite3 as the database for Active Record
    gem 'sqlite3'

    # Use SCSS for stylesheets
    gem 'sass-rails', '~> 4.0.2'

    # Use Uglifier as compressor for JavaScript assets
    gem 'uglifier', '>= 1.3.0'

    # Use CoffeeScript for .js.coffee assets and views
    gem 'coffee-rails', '~> 4.0.0'

    # Use jquery as the JavaScript library
    gem 'jquery-rails'

    # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
    gem 'turbolinks'

    # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
    gem 'jbuilder', '~> 1.2'

    #because sshkit 1.3 has this bug: https://stackoverflow.com/questions/21401665/how-to-call-a-capistranos-task-within-another-capistranos-task
    gem 'sshkit', '~> 1.0.0'
    gem 'capistrano',  '~> 3.0.1'

    gem 'capistrano-rails', '~> 1.1'
    gem 'capistrano-rvm'

    group :doc do
      # bundle exec rake doc:rails generates the API under doc/api.
      gem 'sdoc', require: false
    end

Gemfile.lock的

GEM
  remote: https://rubygems.org/
  specs:
    actionmailer (4.0.4)
      actionpack (= 4.0.4)
      mail (~> 2.5.4)
    actionpack (4.0.4)
      activesupport (= 4.0.4)
      builder (~> 3.1.0)
      erubis (~> 2.7.0)
      rack (~> 1.5.2)
      rack-test (~> 0.6.2)
    activemodel (4.0.4)
      activesupport (= 4.0.4)
      builder (~> 3.1.0)
    activerecord (4.0.4)
      activemodel (= 4.0.4)
      activerecord-deprecated_finders (~> 1.0.2)
      activesupport (= 4.0.4)
      arel (~> 4.0.0)
    activerecord-deprecated_finders (1.0.3)
    activesupport (4.0.4)
      i18n (~> 0.6, >= 0.6.9)
      minitest (~> 4.2)
      multi_json (~> 1.3)
      thread_safe (~> 0.1)
      tzinfo (~> 0.3.37)
    arel (4.0.2)
    atomic (1.1.16)
    builder (3.1.4)
    capistrano (3.0.1)
      i18n
      rake (>= 10.0.0)
      sshkit (>= 0.0.23)
    capistrano-bundler (1.0.0)
      capistrano (>= 3.0.0.pre)
    capistrano-rails (1.1.0)
      capistrano (>= 3.0.0)
      capistrano-bundler (>= 1.0.0)
    capistrano-rvm (0.0.3)
      capistrano
    coffee-rails (4.0.1)
      coffee-script (>= 2.2.0)
      railties (>= 4.0.0, < 5.0)
    coffee-script (2.2.0)
      coffee-script-source
      execjs
    coffee-script-source (1.7.0)
    erubis (2.7.0)
    execjs (2.0.2)
    hike (1.2.3)
    i18n (0.6.9)
    jbuilder (1.5.3)
      activesupport (>= 3.0.0)
      multi_json (>= 1.2.0)
    jquery-rails (3.1.0)
      railties (>= 3.0, < 5.0)
      thor (>= 0.14, < 2.0)
    json (1.8.1)
    mail (2.5.4)
      mime-types (~> 1.16)
      treetop (~> 1.4.8)
    mime-types (1.25.1)
    minitest (4.7.5)
    multi_json (1.9.2)
    net-scp (1.1.2)
      net-ssh (>= 2.6.5)
    net-ssh (2.8.0)
    polyglot (0.3.4)
    rack (1.5.2)
    rack-test (0.6.2)
      rack (>= 1.0)
    rails (4.0.4)
      actionmailer (= 4.0.4)
      actionpack (= 4.0.4)
      activerecord (= 4.0.4)
      activesupport (= 4.0.4)
      bundler (>= 1.3.0, < 2.0)
      railties (= 4.0.4)
      sprockets-rails (~> 2.0.0)
    railties (4.0.4)
      actionpack (= 4.0.4)
      activesupport (= 4.0.4)
      rake (>= 0.8.7)
      thor (>= 0.18.1, < 2.0)
    rake (10.2.2)
    rdoc (4.1.1)
      json (~> 1.4)
    sass (3.2.18)
    sass-rails (4.0.2)
      railties (>= 4.0.0, < 5.0)
      sass (~> 3.2.0)
      sprockets (~> 2.8, <= 2.11.0)
      sprockets-rails (~> 2.0.0)
    sdoc (0.4.0)
      json (~> 1.8)
      rdoc (~> 4.0, < 5.0)
    sprockets (2.11.0)
      hike (~> 1.2)
      multi_json (~> 1.0)
      rack (~> 1.0)
      tilt (~> 1.1, != 1.3.0)
    sprockets-rails (2.0.1)
      actionpack (>= 3.0)
      activesupport (>= 3.0)
      sprockets (~> 2.8)
    sqlite3 (1.3.9)
    sshkit (1.0.0)
      net-scp
      net-ssh
      term-ansicolor
    term-ansicolor (1.3.0)
      tins (~> 1.0)
    thor (0.19.1)
    thread_safe (0.3.1)
      atomic (>= 1.1.7, < 2)
    tilt (1.4.1)
    tins (1.0.1)
    treetop (1.4.15)
      polyglot
      polyglot (>= 0.3.1)
    turbolinks (2.2.1)
      coffee-rails
    tzinfo (0.3.39)
    uglifier (2.5.0)
      execjs (>= 0.3.0)
      json (>= 1.8.0)

PLATFORMS
  ruby

DEPENDENCIES
  capistrano (~> 3.0.1)
  capistrano-rails (~> 1.1)
  capistrano-rvm
  coffee-rails (~> 4.0.0)
  jbuilder (~> 1.2)
  jquery-rails
  rails (= 4.0.4)
  sass-rails (~> 4.0.2)
  sdoc
  sqlite3
  sshkit (~> 1.0.0)
  turbolinks
  uglifier (>= 1.3.0)

当我执行时     bundle exec cap staging deploy:setup_config

我得到了这个输出:

INFO [9e71d728] Running mkdir -p /srv/hr/shared/config on ruby-dev
DEBUG [9e71d728] Command: mkdir -p /srv/hr/shared/config
 INFO [9e71d728] Finished in 0.738 seconds command successful.
cap aborted!
NoMethodError: undefined method `smart_template' for #<SSHKit::Backend::Netssh:0x007f8a4dc04980>
/Users/nico/DevOps/repo/hello-rails/lib/capistrano/tasks/setup_config.cap:16:in `block (4 levels) in <top (required)>'
/Users/nico/DevOps/repo/hello-rails/lib/capistrano/tasks/setup_config.cap:15:in `each'
/Users/nico/DevOps/repo/hello-rails/lib/capistrano/tasks/setup_config.cap:15:in `block (3 levels) in <top (required)>'
/Users/nico/.rvm/gems/ruby-1.9.3-p385@hello-rails-cap-v3-sshkit-v1/gems/sshkit-1.0.0/lib/sshkit/backends/netssh.rb:42:in `instance_exec'
/Users/nico/.rvm/gems/ruby-1.9.3-p385@hello-rails-cap-v3-sshkit-v1/gems/sshkit-1.0.0/lib/sshkit/backends/netssh.rb:42:in `run'

似乎使用capistrano v3.0.1和sshkit v1.0无法解决我的问题。 deploy:setup_config任务正在尝试调用lib / capistrano / template.rb中定义的函数smart_template,输出表明找不到该函数。我对如何使其正常工作感到茫然。有关解决这个问题的任何建议都是受欢迎的。此外,如果有更好的方法来创建nginx和unicorn的配置和可执行文件,我很乐意听到。

修改

在怀疑来自rubygems的sshkit gem仍然包含bug之后,我添加了

gem 'sshkit', :git => 'https://github.com/capistrano/sshkit.git' 

到我的Capfile并重建我的本地gemset。然而,这并没有解决这个问题,并指示我去其他地方。从那里我能够将问题诊断为与我的Capfile中这些行定义的ruby文件的导入相关:

Dir.glob('lib/capistrano/*.rb').each { |r| import r }
Dir.glob('lib/capistrano/**/*.rb').each { |r| import r }

我评论了这些线条并将其替换为:

require_relative 'lib/capistrano/template.rb'
require_relative 'lib/capistrano/substitute_strings.rb'

现在我的deploy:setup_config任务正确调用了这些函数。我导出了工作宝石集,并使用来自ruby gems的sshkit创建了一个新的宝石集。使用上面列出的require_relative行,rubygems中的sshkit gem工作正常。因此问题从来都不是sshkit中的错误,无论我使用哪个源(git或rubygems),而是导入包含我的cap任务所调用的函数的ruby文件。

2 个答案:

答案 0 :(得分:1)

您的示例有两个问题,正确的行是:

Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }                                                                                                                                      
Dir.glob('lib/capistrano/**/*.rb').each { |r| import r }    

您可以看到完整示例here

答案 1 :(得分:0)

根据我在原始问题中的编辑,sshkit gem的来源不是我的问题。 相反,问题在于进口声明。

更改行:

# Loads custom tasks from all folders below `lib/capistrano' if you have any defined.
Dir.glob('lib/capistrano/*.rb').each { |r| import r }
Dir.glob('lib/capistrano/**/*.rb').each { |r| import r }

为:

require_relative 'lib/capistrano/template.rb'
require_relative 'lib/capistrano/substitute_strings.rb'

为我解决了这个问题。

我不确定为什么Dir.glob方法对我不起作用,我认为操作员错误,期望弄清楚我做错了将有助于在未来的帽子部署中推广导入。