Rails4.2+Deviseでproductionで起動するとDevise.secret_key was not set.

developmentだと問題なく動いていたがproductionで動かしてみたら掲題の通りの例外が発生。
このメッセージを表示している構文はこうなっている。掲示されているsecret_keyはランダム生成されているものだとわかる。

      def raise_no_secret_key #:nodoc:
        raise <<-ERROR
Devise.secret_key was not set. Please add the following to your Devise initializer:
  config.secret_key = '#{SecureRandom.hex(64)}'
Please ensure you restarted your application after installing Devise or setting the key.
ERROR
      end
https://github.com/plataformatec/devise/blob/master/lib/devise/rails/routes.rb

じゃあ、なぜdevelopmentだと問題ないのにproductionだと例外が発生するのかというと。

Devise 3.2.3からRails4以降を使用している場合且つ、config/initializers/devise.rbに独自のsecret_keyが設定されていない場合secret_key_baseをベースとしてtokenを生成するようになった。

3.2.3 - 2014-02-20

  • enhancements
    • Devise will use the secret_key_base on Rails 4+ applications as its secret_key. You can change this and use your own secret by changing the devise.rb initializer.

該当する処理はこんな感じ。

    initializer "devise.secret_key" do |app|
      if app.respond_to?(:secrets)
        Devise.secret_key ||= app.secrets.secret_key_base
      elsif app.config.respond_to?(:secret_key_base)
        Devise.secret_key ||= app.config.secret_key_base
      end

      Devise.token_generator ||=
        if secret_key = Devise.secret_key
          Devise::TokenGenerator.new(
            Devise::CachingKeyGenerator.new(Devise::KeyGenerator.new(secret_key))
          )
        end
    end
https://github.com/plataformatec/devise/blob/master/lib/devise/rails.rb

devise.rbをgenerateした段階ではconfig.secret_keyはコメントアウトされていたのでsecret_key_baseを使用している状態。

肝心のsecret_key_baseはどこで設定されているかというとconfig/secrets.ymlで設定されている。
これはRails4.1から導入された管理方法で、productionの項を見れば原因がわかると思う。

# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

productionの場合環境変数で設定するんだけど怠ったのでsecret_keyが設定されず、devise.rbでも設定していないので未設定の例外が発生していたという訳であった。