【问题标题】:Capistrano rails | undefined method `already_invoked' for Rake::Task when setting rails_env while migrations during gitlab deployCapistrano 导轨 |在 gitlab 部署期间设置 rails_env 时 Rake::Task 的未定义方法“already_invoked”
【发布时间】:2023-03-24 20:40:02
【问题描述】:

我已经花了很多时间来调试这个,但我似乎还没有接近答案。我也想过在 capistrano 存储库中创建一个问题,但我真的不认为这是 capistrano 本身的问题。

背景:

我在 Gitlab EE 中托管的私有存储库中有一个 rails 5 项目。我的 repo 完全配置为使用 Gitlab CI,用于使用 capistrano、capistrano/rails 和 capistrano/rvm 自动部署。我有一项部署工作,我在其中执行cap review deploy(正在审查我要部署的环境)

一切都运行良好,直到我到达 capistrano 钩子 deploy:migrate,它在其中中断并出现以下错误。

错误:

  (Backtrace restricted to imported tasks) 
  cap aborted!
  NoMethodError: undefined method `already_invoked' for <Rake::Task deploy:migrating => [set_rails_env]>:Rake::Task

  Tasks: TOP => deploy:migrate
  (See full trace by running task with --trace)
  The deploy has failed with an error: undefined method `already_invoked' for <Rake::Task deploy:migrating => [set_rails_env]>:Rake::Task

据我所知,设置 rails_env 的 rake 任务似乎存在问题(来自 gem capistrano/rails)。我已经跟踪查看 capistrano/rvm 是否没有从 rvm 设置正确的 ruby​​,但事实并非如此。 cappistrano/rvm 设置没问题。我认为这可能与 gem 版本有关。

我的弱点:

我已使用 capistrano/rails 人员提供的可选参数对其进行了修补,使其在没有添加新迁移时工作:

set :conditionally_migrate, true

但它仍然失败(当有新的迁移时,这是大多数时候)

此外,capistrano 在我的本地环境中使用时可以正常工作(使用bundle exec cap review deploy)。

提前致谢!希望你能帮我找到答案...

编辑:添加一些关于我的文件和堆栈的详细信息:

在我的 Rails 配置中,我使用了一个 git-ignored environment_variables.yml 文件,我在其中设置了 capistrano 配置文件中显示的所有 ENV['var'] 配置。我还保证在执行部署工作的运行器中正确设置了这些设置。 (使用Gitlab Variables)。

这些 CI 作业使用安装在 docker runner 内的 DigitalOcean droplet 上的 gitlab-runner 服务运行,并为每个作业指定图像。

.gitlab-ci.yml

stages:
  - test
  - deploy
variables:
  MYSQL_DATABASE: "test_linting"
  MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
  DB_NAME: 'test_linting'
  DB_USER: 'root'
  DB_PASS: ''
  DB_HOST: 'mysql'
services:
  - mysql
testing:
  image: heroku/ruby
  stage: test
  cache: 
    paths:
      - vendor/cache
  script:
    - bundle install --without=development production --jobs $(nproc) --path=vendor/cache
    - bundle exec rails db:create RAILS_ENV=test
    - bundle exec rails db:migrate RAILS_ENV=test
    - bundle exec rails test
  artifacts:
    paths:
      - coverage/
dev-deploy:
  image: ruby:2.3
  stage: deploy
  environment: 
    name: $CI_BUILD_REF_NAME
    url: http://dev.linting.com
  before_script:
    - gem install capistrano -v '~> 3.7'
    - gem install capistrano-rails
    - gem install capistrano-rvm
    - gem install slackistrano
  script:
    - cap $CI_BUILD_REF_NAME deploy
  only:
    - development
deploy:
  image: ruby:2.3
  stage: deploy
  environment: 
    name: $CI_BUILD_REF_NAME
    url: http://$CI_BUILD_REF_NAME.linting.com
  before_script:
    - gem install capistrano -v '~> 3.7'
    - gem install capistrano-rails
    - gem install capistrano-rvm
    - gem install slackistrano
  script:
    - cap $CI_BUILD_REF_NAME deploy
  only:
    - qa
    - staging
review_deploy:
  image: ruby:2.3
  stage: deploy
  environment: 
    name: review
    url: http://rev.linting.com
  when: manual
  before_script:
    - gem install capistrano -v '~> 3.7'
    - gem install capistrano-rails
    - gem install capistrano-rvm
    - gem install slackistrano
  script:
    - cap review deploy
  except:
    - development
    - qa
    - staging
    - master

config/deploy.rb

lock '~> 3.7'

env_file = "./config/environment_variables.yml"
if File.exists?(env_file)
  YAML.load_file(env_file)['capistrano'].each do |key, value|
    ENV[key.to_s] = value
  end
end

set :application, ENV['PROJECT_NAME']
set :repo_url, ENV['REPO_URL']

set :rvm_type, :user
set :rvm_ruby_version, '2.3.1'

set :conditionally_migrate, true

set :linked_files, %w{config/environment_variables.yml}
set :linked_dirs, %w{log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}

set :slackistrano, {
  klass: Slackistrano::CustomMessaging,
  channel: ENV['SLACK_CHANNEL'],
  webhook: ENV['SLACK_HOOK']
}

namespace :deploy do
  desc 'Restart application'
  task :restart do
    on roles(:app), in: :sequence, wait: 5 do
      execute :touch, release_path.join('tmp/restart.txt')
    end
  end

  desc 'Reset environment (rake db:reset)'
  task :db_reset do
    on roles(:app) do
      within "#{current_path}" do
        with rails_env: "#{fetch(:rails_env)}" do
          execute :rake, "db:migrate:reset"
          execute :rake, "db:seed"
        end
      end
    end
  end

  after :published, 'deploy:restart'
  after :finishing, 'deploy:cleanup'
end

config/deploy/review.rb

# Server definiton: (define ip in local env, and pass in gitlab)
server ENV['DEV_DROPLET_IP'], user: 'deploy', roles: %w{web app db}, password: ENV["SSH_DEPLOY_PASS"]

# ONLY WORKS IF IT WAS RUN BY THE GITLAB CI RUNNER
set :branch, ENV['CI_BUILD_REF_NAME'] ? ENV['CI_BUILD_REF_NAME'] : 'development'
# Capistrano Variables
set :stage, 'review'
set :rails_env, 'review'

# Variables for rev
set :commit, ENV['CI_BUILD_REF'] ? ENV['CI_BUILD_REF'][0..7] : 'local'
set :user, ENV['GITLAB_USER_EMAIL'] ? ENV['GITLAB_USER_EMAIL']: 'local user'

# Capistrano Deployment Route
set :deploy_to, "/home/deploy/rev.#{ENV['PROJECT_NAME']}"

namespace :deploy do
  desc 'Set review branch in the review server'
  task :set_review_branch do
    on roles(:app) do
      within "#{current_path}" do
        execute "echo '#{fetch(:branch)}@#{fetch(:commit)}' >> #{release_path.join('tmp/rev_branch')}"
        execute "echo '#{fetch(:user)}' >> #{release_path.join('tmp/rev_user')}"
      end
    end
  end

  after :publishing, 'deploy:set_review_branch'

  before :finishing, 'deploy:db_reset'
end

Capfile

require "capistrano/setup"
require "capistrano/deploy"

require 'capistrano/rvm'
require 'capistrano/rails'

require 'slackistrano/capistrano'
require_relative 'lib/custom_messaging'

require "capistrano/scm/git"
install_plugin Capistrano::SCM::Git

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

宝石文件

source 'https://rubygems.org'

gem 'rails', '~> 5.0.0', '>= 5.0.0.1'
gem 'puma', '~> 3.0'
gem 'sass-rails', '~> 5.0'
gem 'bootstrap-sass'
gem 'uglifier', '>= 1.3.0'
gem 'jquery-rails'
gem 'turbolinks', '~> 5'
gem 'jbuilder', '~> 2.5'

gem 'mysql2'
gem 'haml-rails'
gem 'devise'
gem 'paperclip', git: 'git://github.com/thoughtbot/paperclip.git'
gem 'administrate', '~> 0.3.0'
gem 'bourbon'
gem 'rails_real_favicon', '~> 0.0.6'
gem 'listen', '~> 3.0.5'

group :development, :test do
  gem 'byebug', platform: :mri
  gem 'minitest-rails'
  gem 'minitest-reporters'
  gem 'simplecov'
  gem 'simplecov-json', require: false
  gem 'factory_girl_rails'
  gem 'faker'
end

group :development do
  gem 'annotate'
  gem 'better_errors'
  gem 'binding_of_caller'

  # Capistrano for Deployments
  gem 'capistrano', '~> 3.7'
  gem 'capistrano-rvm'
  gem 'capistrano-rails'
  gem 'slackistrano'

  gem 'web-console'
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

【问题讨论】:

  • 您是否使用.gitlab-ci.yml 来运行它?你能发布相关部分吗?
  • 已编辑以添加我的堆栈详细信息。希望对您有所帮助。

标签: ruby-on-rails environment-variables capistrano3 gitlab-ci rvm-capistrano


【解决方案1】:

我的猜测是因为您在 CI 环境中手动安装 Capistrano Gems,所以存在版本不匹配。如果你在 CI 服务器上运行 bundle installbundle exec,或者固定安装的版本,它可能会开始工作。

我为此使用的一个解决方案是将所有必需的部署 gem 添加到我的 Gemfile 中的 :deployment 组中。然后您需要将以下内容添加到您的deploy.rb

set :bundle_without, %w{development test deployment}.join(' ')

然后将您的脚本更改为:

bundle install --except development test default production ...
bundle exec cap $CI_BUILD_REF_NAME deploy

【讨论】:

  • 谢谢!当我测试它时,在我的 CI 环境中使用 bundler 刚刚对我有用。我真的很喜欢尝试:deployment 组解决方案。我只是不明白为什么我需要在我的deploy.rb 中设置set :bundle_without, %w{development test}.join(' '),因为这将覆盖:bundle_without“capistrano 变量”的值。我在每个环境中都单独设置了这个。
  • 另外,在下一个代码块生产之后,... 中的内容是什么?
  • 啊,我犯了一个错误。更改bundle_without 的原因是您不要在生产环境中安装部署gem。如果您要在每个环境的基础上覆盖它,请相应地进行调整。
  • 在省略号之后,除了 gemfile 中的部署组之外的任何其他组。我正在尝试将 --only 参数添加到 Bundler 核心,这将简化这一点:github.com/bundler/bundler/pull/4907
  • 重要信息: 对于所有像我这样被这个问题困扰的人!这个修复帮助我完成了一个使用 rails 5 的项目。但它不适用于 rails 4.2 中的另一个项目......所以我不得不升级另一个项目......希望这也有帮助:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多