Using ENV files in production on Heroku - ruby-on-rails

I've got the following configuration line in my production.rb environment file, as instructed in this article:
config.cache_store = :mem_cache_store, ENV["MEMCACHEDCLOUD_SERVERS"].split(','), { :username => ENV["MEMCACHEDCLOUD_USERNAME"], :password => ENV["MEMCACHEDCLOUD_PASSWORD"] }
But when I try to deploy, I'll get an error:
Running: rake assets:precompile rake aborted!
undefined method split' for nil:NilClass
/tmp/build_abdc.../config/environments/production.rb:107:inblock in '
This is because config vars are not available during compilation. There's a Heroku labs add-on that you can supposedly use to remedy this, but it comes with a warning from Heroku that "Using this labs feature is considered counter to Heroku best practices."
So then what is the best practice when it comes to using ENV vars in production config? Should they just all be wrapped in rescue handlers so that Heroku ignores them during compilation?

We ended up just checking for the ENV var before assigment. It looks like that's the pattern you need whenever you use ENV vars in config/initializers on Heroku:
# NOTE: ENV vars aren't available during slug compilation, so we must check if they exist:
if ENV["MEMCACHEDCLOUD_SERVERS"]
config.cache_store = :mem_cache_store, ENV["MEMCACHEDCLOUD_SERVERS"].split(','), { :username => ENV["MEMCACHEDCLOUD_USERNAME"], :password => ENV["MEMCACHEDCLOUD_PASSWORD"] }
end
See also:
https://devcenter.heroku.com/articles/rails-asset-pipeline#failures-in-the-assets-precompile-task

Related

Figaro environment variables won't set for Heroku

I generated a skeleton application using Rails Composer and included Figaro. It runs successfully locally. Before I modify it, I am pushing it down to Heroku. However, the heroku run rake db:seed failed. I've come to find out that the app/config/application.yml is .gitignored. So, I need to use rake figaro:heroku to set the environment variables before I run heroku run rake db:seed. But, the rake Figaro:heroku is failing as follows:
D:\BitNami\rubystack-2.0.0-11\projects\myapp>rake figaro:heroku
! Usage: heroku config:set KEY1=VALUE1 [KEY2=VALUE2 ...]
! Must specify KEY and VALUE to set.
This looks like it is just ignoring my app/config/application.yml and asking for line directed input to me, but I don't know. Again, the application runs successfully locally, so that application.yml should be correct. Here it is:
MANDRILL_USERNAME: valid.address#gmail.com
MANDRILL_APIKEY: a.valid.apikey
ADMIN_NAME: Admin Name
ADMIN_EMAIL: valid.address#gmail.com
ADMIN_PASSWORD: validpassword
ROLES: [admin, user, VIP]
The failure occurs in seeds when I issue heroku run rake db:seed. The file is:
puts 'ROLES'
YAML.load(ENV['ROLES']).each do |role|
Role.find_or_create_by_name(role)
puts 'role: ' << role
end
puts 'DEFAULT USERS'
user = User.find_or_create_by_email :name => ENV['ADMIN_NAME'].dup, :email => ENV['ADMIN_EMAIL'].dup, :password => ENV['ADMIN_PASSWORD'].dup, :password_confirmation => ENV['ADMIN_PASSWORD'].dup
puts 'user: ' << user.name
user.confirm!
user.add_role :admin
It fails on the first access to variable role because ENV['ROLES'] is uninitialized. It would be initialized by application.yml, and is locally, but it is .gitignored. Thus, the need for rake Figaro:heroku to succeed.
This seems so simple, especially since it runs smoothly locally. OBTW, I have tried application.yml as shown and with the strings double-quoted but it doesn't seem to make a difference in any case so...
Ideas? Thanks...
I understand from the path you're mentioning that this is a Windows question. Problem is that the arrays are not correctly dealt with on Windows. Workaround I once made is to override the "vars" method of Heroku in a rake file in lib/tasks, like
module Figaro
module Tasks
class Heroku # < Struct.new(:app)
def vars
Figaro.env(environment).map { |key, value|
if value.start_with? "["
value = "'#{value.gsub('"', '')}'"
elsif value.include? " "
value = "'#{value}'"
end
"#{key}=#{value}"
}.sort.join(" ")
end
end
end
end
I'd surmise the problem will likely be with Figaro's processing of your different variable types:
MANDRILL_USERNAME: "valid.address#gmail.com"
MANDRILL_APIKEY: "a.valid.apikey"
ADMIN_NAME: "Admin Name"
ADMIN_EMAIL: "valid.address#gmail.com"
ADMIN_PASSWORD: "validpassword"
ROLES: ["admin", "user", "VIP"]
Try removing any spaces & ensuring you only send KEY: "VALUE" to Figaro. Your spaces are basically going to cause the system to misinterpret it

How to set ActionMailer SMTP settings in a YML file?

For my deployment, I prefer to have all Rails configuration options in YML files under config/. I am having particular problems with action_mailer.smtp_settings.
I have a file, config/smtp.yml :
development:
address: localhost
port: 1025
test:
production:
address: smtp.xs4all.nl
port: 465
authentication: plain
user_name: xxxxx
password: xxxxx
tls: true
I then try to include these values via an initializer. config/initializers/smtp.rb:
options = YAML.load_file(Rails.root.join('config', 'smtp.yml'))[Rails.env]
options.each do |name, value|
Portfolio::Application.config.action_mailer.smtp_settings[name.to_sym] = value
end unless options.nil?
This loads the smtp.yml file, parses that and returns the hash for the current environment, e.g.
{"address"=>"smtp.xs4all.nl", "port"=>465, "authentication"=>"plain", "user_name"=>"xxxxx", "password"=>"xxxxx", "tls"=>true}
Converts the keys to symbols and adds that to the smtp-settings, resuling in e.g:
irb(main):002:0> Portfolio::Application.config.action_mailer.smtp_settings
=> {:port=>465, :address=>"smtp.xs4all.nl", :authentication=>"plain", :user_name=>"xxxxx", :password=>"xxxx", :tls=>true}
But when deploying to production, this causes some conflict, probably because some part has not been booted yet. Capistrano fails with:
* executing "cd -- /var/www/ANT_cms/releases/20131218170336 && bundle exec rake RAILS_ENV=production RAILS_GROUPS=assets assets:precompile"
servers: ["li153-5.members.linode.com"]
[li153-5.members.linode.com] executing command
** [out :: li153-5.members.linode.com] rake aborted!
** [out :: li153-5.members.linode.com] undefined method `[]=' for nil:NilClass
Why is my Portfolio::Application.config.action_mailer.smtp_settings nil here? How can I make sure that actionMailers config options are already loaded?
Digging deeper, I found the issue is that bundle exec rake RAILS_ENV=production RAILS_GROUPS=assets assets:precompile" loads only a small subset, and not e.g. ActionMailer.
Adding an early exit to my loader fixes this in config/initializers/smtp.rb:
unless Portfolio::Application.config.action_mailer.nil?
options = YAML.load_file(Rails.root.join('config', 'smtp.yml'))[Rails.env]
options.each do |name, value|
Portfolio::Application.config.action_mailer.smtp_settings[name.to_sym] = value
end unless options.nil?
end
Then, the next issue is that action_mailer in production is set, but that smtp_settings does not have to be defined and filled with defaults, yet. So prefilling this with an empty hash solves the entire issue:
unless Portfolio::Application.config.action_mailer.nil?
Portfolio::Application.config.action_mailer.smtp_settings = {}
options = YAML.load_file(Rails.root.join('config', 'smtp.yml'))[Rails.env]
options.each do |name, value|
Portfolio::Application.config.action_mailer.smtp_settings[name.to_sym] = value
end unless options.nil?
end
Still, somewhat ugly, but it does the job.
You can use config_for for this:
Portfolio::Application.config.action_mailer.smtp_settings = config_for(:smtp)
Portfolio::Application.config.action_mailer.smtp_settings = config_for(:smtp).symbolize_keys

Rake aborted. Devise.secret_key was not set [duplicate]

I am developing a Rails 4 app using the Active Admin gem for the administration back end. Active Admin in turn uses Devise for user authentication. Now, when I try to deploy the app using capistrano on the VPS server, I get the below error:
rake aborted!
Devise.secret_key was not set. Please add the following to your Devise initializer:
config.secret_key = '-- secret key --'
A Google search does not do much for this error. Any suggestions why it is throwing an error? Should I add the secret key to devise initializer, as I cannot find any place to set such config key in initializers/devise.rb?
I ran bundle update this morning and started getting the same error.
I added it as a line in config/initializers/devise.rb and the error was fixed.
This seems to be the commit which introduced it.
What worked for me on Rails 4.1 and Devise 3.2.4 is in config/initializers/devise.rb:
config.secret_key = ENV['DEVISE_SECRET_KEY'] if Rails.env.production?
As of Devise 3.2.3 for Rails 4+ applications the key setting location defaults to YourAppName::Application.config.secret_key_base found in config/initializers/secret_token.rb
This solved my problem:
Add the code below to your config/initializers/devise.rb file.
config.secret_key = '-- secret key --'
Replace '-- secret key--' with your own key. I recommend storing it in an ENV variable for security purpose.
As per changelog:
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.
I went to config/secrets.yml and changed the production value.
Before:
production:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
After:
production:
secret_key_base: string of charaters
Of course, that should be set to the environment variable, which I will set later, but this at least got it running. I got my string by using bundle exec rake secret.
Could it be, that you did not run rails g devise:install?
Running rails generate devise User without the previous command does cause this problem.
In config/initializers/devise.rb I put:
config.secret_key = ENV["SECRET_KEY_BASE"] if Rails.env.production?
Because if you put:
$ heroku config
You'll see a secret_key_base for the mode production.
I solve my initializer problem with this ugly approach:
config.secret_key = 'some1234keyq23' if Rails.env == 'production'
in config/initializers/devise.rb
It now works in production as well as in development !
I cloned my repository onto a new machine from git. The
config/secrets.yml
file was on my .gitignore list, so that file didn't exist, and Devise doesn't create the file.
I added the file, then re-ran
rails generate devise MODEL
and it worked.
Check if your config\initializers\secret_token.rb has:
YourAppName::Application.config.secret_token
It should be:
YourAppName::Application.config.secret_key_base
I has same issue. The problem was caused by these lines in routes.rb:
devise_for :users, :skip => [:registrations]
as :user do
get 'users/edit' => 'devise/registrations#edit', :as => 'edit_user_registration'
put 'users' => 'devise/registrations#update', :as => 'user_registration'
get '/users/sign_out' => 'devise/sessions#destroy'
end
I commented them and after that i run:
$ rails generate devise:install
And it has evaluated perfectly. And after that I uncommented routes.
Well, I have been following this post and tried almost everything here.
I have added the key to devise.rb. But I was still getting the same error.
Maybe a stupid answer, but all I had to do was to push the devise.rb key to the repository.
Fix:
In the production server:
sudo -H nano /etc/environment
Then in the file add:
export SECRET_KEY_BASE="yourkey"
export DEMO03_DATABASE_PASSWORD="yourpass"
to set this permanently, and system wide (all users, all processes) add set variable
In the local project devise.rb file:
config.secret_key = ENV["SECRET_KEY_BASE"] if Rails.env.production?
Technical details:
Ubuntu 16.04
Devise (4.2.0)
rails 5.0.1
capistrano (3.7.1)
Ran into the same trouble with Rails 5.2.0 and Devise 4.4.1
Drop the following into /config/initializers/devise.rb
config.secret_key = Rails.application.credentials.secret_key_base
Trying to give a somewhat more complete answer to the ones above:
As mentioned in the devise_auth_token gem's documentation
...Additionally, you can configure other aspects of devise by manually
creating the traditional devise.rb file at
config/initializers/devise.rb. Here are some examples of what you can
do in this file:
Devise.setup do |config|
# The e-mail address that mail will appear to be sent from
# If absent, mail is sent from "please-change-me-at-config-initializers-devise#example.com"
config.mailer_sender = "support#myapp.com"
# If using rails-api, you may want to tell devise to not use ActionDispatch::Flash
# middleware b/c rails-api does not include it.
# See: http://stackoverflow.com/q/19600905/806956
config.navigational_formats = [:json] end
I had the same problem, and like metioned here, I created the devise initializer, and add the config.secret_key = ENV['DEVISE_SECRET_KEY'] line to it.
I do not know right solution but it's working. You can try it. I was cloned my project from my GitLab account and when I run in my local server, I have an error Message:
rake aborted!
Devise.secret_key was not set. Please add the following to your Devise initializer:
config.secret_key = '-- secret key --'
Open config/initializers/devise.rb and add this line
config.secret_key = '<%= ENV["SECRET_KEY_BASE"] %>'
This code line is solved my problem.

Rails precompile constant uninitialized

I wanted to preload the configuration (from ".yml" files). In one of my initializer files (config/initializers/facebook.rb) I have following line of code:
FACEBOOK_CONFIG = YAML.load_file("#{Rails.root}/config/facebook.yml")[Rails.env]
So, it works like a charm in the "DEVELOPMENT" mode. Once I switch to the production mode, it keeps telling me, that FACEBOOK_CONFIG is an uninitialized constant for my "facebook.js.coffee.erb" file, located in assets/javascript (If it matters), if I want to o "rake assets:precompile". I've tried doing random stuff, like: RAILS_ENV=production bundle exec rake assets:precompile or
rake assets:precompile:all
, but no luck
I have tried assigning "initialize_on_precompile = true" variable for my production environment (although, it should be true by default), just in case.
Why it doesn't work in production mode (But, I want to emphasise, that it does work(!) in the development environment).
Can someone help with that one ?
I encountered exactly the same problem. This is because your javascript(coffescript) file makes reference to a constant that is defined in an initializer. Because it is precompiled before the initializer the app throws an error.
This is the simple solution I found. You place this code at the bottom of your application.rb file in config:
module AssetsInitializers
class Railtie < Rails::Railtie
initializer "assets_initializers.initialize_rails",
:group => :assets do |app|
require "#{Rails.root}/config/initializers/facebook.rb"
end
end
end
It manually loads up certain files from the initializer folder. It solved my problem.
Hopefully this was the issue for you as well.
module Rails
class << self
def facebook_config
##facebook_config ||= nil
end
def facebook_config=(facebook_config)
##facebook_config = facebook_config
end
end
end
Rails.facebook_config = YAML.load_file("#{Rails.root}/config/facebook.yml")[Rails.env]
# And you can use it like this in anywhere:
puts Rails.facebook_config

Correct Ruby on Rails 3 replacement for ENV["RAILS_ENV"] ||= 'production'?

We're doing an upgrade to Ruby on Rails 3 (like half the world right now), and I've been diligently replacing usages of RAILS_ENV, for example
RAILS_ENV == 'wibble'
# becomes
Rails.env.wibble?
But I'm not as certain of what to do with:
ENV["RAILS_ENV"] ||= 'production'
We've got it at the top of a whole bunch of Rake tasks and daemons, and the idea is that you can pass RAILS_ENV on the command-line, but it defaults to 'production' if it's not passed.
I'm not sure of the new Rails3-appropriate way of doing this. So for now my rails:upgrade:check is complaining mightily of this intrusion of Rails2-ishness...
I don't know if:
::Rails.env ||= 'production'
will work.
Does Rails.env exist in a daemon?
Does it automagickally get pre-populated with the value of RAILS_ENV passed on the command-line or do we need a new way of invoking the daemons?
What is the correct mantra for this?
Update:
Looking into the source-code for Rails.env,
def env
#_env ||= ActiveSupport::StringInquirer.new(RAILS_ENV)
end
we can deduce a number of things.
Firstly, it looks like RAILS_ENV does actually still exist - which means it can be set and Rails.env will find it...
If Rails is valid in the context of a daemon, then nothing more needs to be done. If not - then I could just not care much and use the old RAILS_ENV as before.
Rails.env is actually of type ActiveSupport::StringInquirer, which overrides method_missing in order to provide that nice equality syntax. Check: http://api.rubyonrails.org/classes/ActiveSupport/StringInquirer.html
So, if you want to override it to be "production" by defaut, you should write:
Rails.env ||= ActiveSupport::StringInquirer.new('production')
However, you'll have to check which is the uninitialized value of Rails.env, I'm not sure it's really nil.
The best course of action, IMO, is to just prepend env RAILS_ENV=production to all your scripts.
Edit lib/tasks/environments.rake
# Sets environments as needed for rake tasks
%w[development production staging].each do |env|
desc "Runs the following task in the #{env} environment"
task env do
Rails.env = env
end
end
task :testing do
Rake::Task["test"].invoke
end
task :dev do
Rake::Task["development"].invoke
end
task :prod do
Rake::Task["production"].invoke
end
Source
UPDATE
pass RAILS_ENV=production via command line, something like this:
RAILS_ENV=production rake db:setup
Does this help:
# before
if RAILS_ENV == 'production'
...
# Rails 3
if Rails.env.production?
if Rails.env.production?
puts '...'

Resources