Capistrano rails | undefined method `already_invoked' for Rake::Task when setting rails_env while migrations during gitlab deploy
<p>I've spent a whole lot of time debugging this and I doesn't seem to be getting close to an answer. Also I thought about creating an issue in capistrano repository, but I don't really think this is an issue of capistrano itself.</p>

<h3>The Context:</h3>

<p>I have a rails 5 project in a private repository hosted in Gitlab EE. My repo is fully configured to use Gitlab CI, for automating deployments using capistrano, capistrano/rails, and capistrano/rvm. I have one job for deployment, in which I execute <code>cap review deploy</code> (being review the environment i want to deploy)</p>

<p>Everything runs well, until I reach the capistrano hook <code>deploy:migrate</code> in which it breaks with the following error.</p>

<h3>The Error:</h3>

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

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

<p>From what I've been able to grasp of it, it seems to be a problem with the rake tasks that sets up the rails_env (from the gem capistrano/rails). I've tracked to see if capistrano/rvm wasn't setting the correct ruby from rvm, but that wasn't the case. cappistrano/rvm setup was ok. I think maybe it have to do with something about gem versions or else.</p>

<h3>My weak patch:</h3>

<p>I've patched it to work when no new migrations are added, using an optional parameter provided by the folks at capistrano/rails:</p>

<p><code>set :conditionally_migrate, true</code></p>

<p>But it's still failing otherwise (when there are new migrations, which is most of the time)</p>

<p>Aditionally, capistrano works correctly when using it from my local environment (using <code>bundle exec cap review deploy</code>).</p>

<p>Thanks in advance! Hope you can help me find the answer...</p>

<h2>EDIT: Adding some details about my files and stack:</h2>

<p>In my Rails configuration I use an git-ignored <code>environment_variables.yml</code> file in which I set all those <code>ENV['var']</code> configs that are shown in the capistrano config files. I also guarantee that those are correctly setted up in the runner that is doing the deploy job. (Using <a href="https://docs.gitlab.com/ce/ci/variables/#secret-variables" rel="nofollow noreferrer">Gitlab Variables</a>).</p>

<p>These CI jobs are runt using a gitlab-runner service installed on a DigitalOcean droplet inside a docker runner with the images specified per-job.</p>

<h3>.gitlab-ci.yml</h3>

<pre><code>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 '~&gt; 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 '~&gt; 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 '~&gt; 3.7'
    - gem install capistrano-rails
    - gem install capistrano-rvm
    - gem install slackistrano
  script:
    - cap review deploy
  except:
    - development
    - qa
    - staging
    - master
</code></pre>

<h3>config/deploy.rb</h3>

<pre><code>lock '~&gt; 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
</code></pre>

<h3>config/deploy/review.rb</h3>

<pre><code># 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)}' &gt;&gt; #{release_path.join('tmp/rev_branch')}"
        execute "echo '#{fetch(:user)}' &gt;&gt; #{release_path.join('tmp/rev_user')}"
      end
    end
  end

  after :publishing, 'deploy:set_review_branch'

  before :finishing, 'deploy:db_reset'
end
</code></pre>

<h3>Capfile</h3>

<pre><code>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 }
</code></pre>

<h3>Gemfile</h3>

<pre><code>source 'https://rubygems.org'

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

gem 'mysql2'
gem 'haml-rails'
gem 'devise'
gem 'paperclip', git: 'git://github.com/thoughtbot/paperclip.git'
gem 'administrate', '~&gt; 0.3.0'
gem 'bourbon'
gem 'rails_real_favicon', '~&gt; 0.0.6'
gem 'listen', '~&gt; 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', '~&gt; 3.7'
  gem 'capistrano-rvm'
  gem 'capistrano-rails'
  gem 'slackistrano'

  gem 'web-console'
  gem 'spring'
  gem 'spring-watcher-listen', '~&gt; 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]
</code></pre>