Rails 4 へ移行して Capistrano でのデプロイに失敗したまとめ

Rails 3.2 からRails 4.2 に移行した際に、Capistrano 3 でのデプロイにハマった件。

## 環境
Ruby 2.1.4
Rails 4.2.1
Capistrano は、Rails 3.2 のものをそのまま使用した。

```ruby:Gemfile
  gem 'capistrano', '~> 3.0.1'
  gem 'capistrano-rails', '~> 1.1'
  gem 'capistrano-rbenv', '~> 2.0'
  gem 'capistrano-bundler'
```

デプロイコマンド

```rb
bundle exec cap production deploy
```

## ブランチが既に存在する旨のエラーメッセージ

```
INFO [9d4092d6] Running /usr/bin/env git branch issues/123-some-fixes origin/issues/123-some-fixes as yamy@localhost
cap aborted!
SSHKit::Command::Failed: git exit status: 32768
git stdout: Nothing written
git stderr: fatal: A branch named 'issues/123-some-fixes' already exists.
/path/to/project/vendor/bundle/ruby/2.1.0/gems/sshkit-1.7.1/lib/sshkit/command.rb:95:in `exit_status='
/path/to/project/vendor/bundle/ruby/2.1.0/gems/sshkit-1.7.1/lib/sshkit/backends/local.rb:85:in `block (2 levels) in _execute'
/path/to/project/vendor/bundle/ruby/2.1.0/gems/sshkit-1.7.1/lib/sshkit/backends/local.rb:63:in `block in _execute'
/path/to/project/vendor/bundle/ruby/2.1.0/gems/sshkit-1.7.1/lib/sshkit/backends/local.rb:58:in `tap'
/path/to/project/vendor/bundle/ruby/2.1.0/gems/sshkit-1.7.1/lib/sshkit/backends/local.rb:58:in `_execute'
/path/to/project/vendor/bundle/ruby/2.1.0/gems/sshkit-1.7.1/lib/sshkit/backends/local.rb:27:in `execute'
/path/to/project/lib/capistrano/tasks/git.cap:25:in `block (4 levels) in <top (required)>'
```

以下にエラーメッセージの該当箇所があった。

```rb:lib/capistrano/tasks/git.cap
    desc 'override git.rake taske update for repository'
    task :update do
      run_locally do
        execute :git, :fetch, :origin
        current_dir = capture "pwd"
        if test " [ -f #{current_dir}/.git/refs/heads/#{fetch(:branch)} ] "
          info "the branch #{fetch(:branch)} already exists at local"
        else
          execute :git, :branch, "#{fetch(:branch)}", "origin/#{fetch(:branch)}"
        end
      end
    end
```

ロガーを追加して実行。


正常系（Rails 3.2）

```rb
INFO [18a14868] Running /usr/bin/env git fetch origin on
INFO [18a14868] Finished in 2.780 seconds with exit status 0 (successful).
INFO @@
INFO /path/to/project/.git/refs/heads/issues/100-some-fixes
```

失敗（Rails 4.2）

```rb
INFO [53acc449] Running /usr/bin/env git fetch origin as yamy@localhost
INFO [53acc449] Finished in 2.923 seconds with exit status 0 (successful).
INFO @@
INFO /path/to/project
/.git/refs/heads/issues/123-some-fixes
```

プロジェクトルート以降で改行されているため、先の判定処理で false になっているようだ。
さらに見ると、以下で改行が付加されていることが分かった。

```rb:lib/capistrano/tasks/git.cap
current_dir = capture "pwd"
```

`capture` は `gem 'sshkit'` のメソッドで、移行作業の影響で capistrano と依存関係の gem のバージョンが更新されていることに気付いた。(sshkit 1.7.1)

`Gemfile.lock` を見て、他の gem との依存関係も問題なさそうなので、前のバージョンに戻した。もともと明示的には `Gemfile` に追加していなかった。

```rb:Gemfile
  gem 'sshkit', '1.4.0'
```

これで、先のエラーは解消した。

## rake assets:precompile が失敗する

```
 INFO [c7d908ef] Running RBENV_ROOT=/usr/local/rbenv RBENV_VERSION=2.1.4 /usr/local/rbenv/bin/rbenv exec bundle exec rake assets:precompile on some-production-server
cap aborted!
SSHKit::Command::Failed: rake exit status: 1
rake stdout: Nothing written
rake stderr: Nothing written
/path/to/project/vendor/bundle/ruby/2.1.0/gems/sshkit-1.4.0/lib/sshkit/command.rb:98:in `exit_status='
/path/to/project/vendor/bundle/ruby/2.1.0/gems/sshkit-1.4.0/lib/sshkit/backends/netssh.rb:142:in `block (4 levels) in _execute'
/path/to/project/vendor/bundle/ruby/2.1.0/gems/net-ssh-2.9.2/lib/net/ssh/connection/channel.rb:551:in `call'
/path/to/project/vendor/bundle/ruby/2.1.0/gems/net-ssh-2.9.2/lib/net/ssh/connection/channel.rb:551:in `do_request'
```

しばらくこの情報をもとに調べていたが一向に解決せず、ふと Capistrano の Log level の存在を思い出し、 `debug` に設定して確認。

```rb:config/deploy.rb
#set :log_level, :info
set :log_level, :debug
```

```
DEBUG [dfb6c8fe]        rake aborted!
DEBUG [dfb6c8fe]        NameError: uninitialized constant ActiveRecord::Observer
DEBUG [dfb6c8fe]        /path/to/project/shared/bundle/ruby/2.1.0/gems/multi_db-0.3.1/lib/multi_db/connection_proxy.rb:57:in `setup!'
DEBUG [dfb6c8fe]        /ebs01/path/to/project/releases/20150623083226/config/environments/production.rb:71:in `block (2 levels) in <top (required)>'
DEBUG [dfb6c8fe]        /path/to/project/shared/bundle/ruby/2.1.0/gems/activesupport-4.2.1/lib/active_support/lazy_load_hooks.rb:36:in `call'
```

しっかりエラーが表示されていた。

どうやら、Rails 4.0 から Observer が削除されたらしく、以下をGemfileに追加してリポジトリにpush。

```rb:Gemfile
gem "rails-observers"
```

参照: https://github.com/rails/rails-observers

## devise のパラメタ不足

```
DEBUG [3b3ed770]        rake aborted!
DEBUG [3b3ed770]        Devise.secret_key was not set. Please add the following to your Devise initializer:
DEBUG [3b3ed770]
DEBUG [3b3ed770]          config.secret_key = '***'
DEBUG [3b3ed770]
DEBUG [3b3ed770]        Please ensure you restarted your application after installing Devise or setting the key.
DEBUG [3b3ed770]        /path/to/project/shared/bundle/ruby/2.1.0/gems/devise-3.5.1/lib/devise/rails/routes.rb:480:in `raise_no_secret_key'
DEBUG [3b3ed770]        /path/to/project/shared/bundle/ruby/2.1.0/gems/devise-3.5.1/lib/devise/rails/routes.rb:209:in `devise_for'
DEBUG [3b3ed770]        /ebs01/path/to/project/releases/20150623094342/config/routes.rb:9:in `block in <top (required)>'
```
config/initializers/devise.rbに追加。

```rb:config/initializers/devise.rb
# Use this hook to configure devise mailer, warden hooks and so forth.
# Many of these configuration options can be set straight in your model.
Devise.setup do |config|
  # The secret key used by Devise. Devise uses this key to generate
  # random tokens. Changing this key will render invalid all existing
  # confirmation, reset password and unlock tokens in the database.
  config.secret_key = '***'

```

## assets compile の設定が一部変更されている

```
DEBUG [9b394669]        rake aborted!
DEBUG [9b394669]        NoMethodError: undefined method `new' for true:TrueClass
DEBUG [9b394669]          (in /ebs01/path/to/project/releases/20150623095211/app/assets/javascripts/application.js)
DEBUG [9b394669]        /path/to/project/shared/bundle/ruby/2.1.0/gems/sprockets-2.12.3/lib/sprockets/context.rb:196:in `block in evaluate'
DEBUG [9b394669]        /path/to/project/shared/bundle/ruby/2.1.0/gems/sprockets-2.12.3/lib/sprockets/context.rb:194:in `each'
DEBUG [9b394669]        /path/to/project/shared/bundle/ruby/2.1.0/gems/sprockets-2.12.3/lib/sprockets/context.rb:194:in `evaluate'
DEBUG [9b394669]        /path/to/project/shared/bundle/ruby/2.1.0/gems/sprockets-2.12.3/lib/sprockets/bundled_asset.rb:25:in `initialize'
DEBUG [9b394669]        /path/to/project/shared/bundle/ruby/2.1.0/gems/sprockets-2.12.3/lib/sprockets/base.rb:377:in `new'
```

以下の記事を元に、config/environments/production.rbを修正。

```rb:config/environments/production.rb
   # Compress JavaScripts and CSS
-  config.assets.js_compressor = true
+  config.assets.js_compressor = :uglifier
```

参照: http://stackoverflow.com/questions/21047556/undefined-method-new-for-truetrueclass-when-precompiling-assets

## assets のバックアップまわりで変更があった

```
DEBUG [6a19f5e1]        cp:
DEBUG [6a19f5e1]        target `/path/to/project/releases/20150623102330/assets_manifest_backup' is not a directory
```

以下の記事を参考に Capistrano 関連の gem のバージョンを調整。
最新版の Capistrano 3.4.0 ~ 3.2.0 までダウングレードした結果、`3.1.0` でエラーがなくなった。

```rb:Gemfile
-  gem 'capistrano', '~> 3.0.1'
-  gem 'capistrano-rails', '~> 1.1'
+  gem 'capistrano', '3.1.0'
+  gem 'capistrano-rails', '~> 1.1.3'
```

参照: http://stackoverflow.com/questions/29617239/capistrano-3-sprockets-3-rails-4-2-1-wont-deploy

以上で、ようやく正常にデプロイされた。

## まとめ

Capistrano は依存関係にある gem のバージョンにかなりシビアであることを再認識した。Rails 4 への移行の際、意図せず依存関係の gem が更新されていないか、事前に確認しておくと良さそうです。