How do I get an ENV variable set for rspec? - ruby-on-rails

I'm using foreman to start up my rails development server. It's nice that I can put all of my environment variables in the .env file. Is there a way to do something similar for my test environment?
I want to set an API key that I will use with the vcr gem, but I don't want to add the API to version control. Any suggestions besides setting the environment variable manually when I start up my tests script?

If you just need to set environment variables, you can either set them from command-line:
SOMETHING=123 SOMETHING_ELSE="this is a test" rake spec
Or you could define the following at the top of your Rakefile or spec_helper.rb:
ENV['SOMETHING']=123
ENV['SOMETHING_ELSE']="this is a test"
If they don't always apply, you could use a conditional:
if something_needs_to_happen?
ENV['SOMETHING']=123
ENV['SOMETHING_ELSE']="this is a test"
end
If you want to use a Foreman .env file, which looks like:
SOMETHING=123
SOMETHING_ELSE="this is a test"
and turn it into the following and eval it:
ENV['SOMETHING']='123'
ENV['SOMETHING_ELSE']='this is a test'
You might do:
File.open("/path/to/.env", "r").each_line do |line|
a = line.chomp("\n").split('=',2)
a[1].gsub!(/^"|"$/, '') if ['\'','"'].include?(a[1][0])
eval "ENV['#{a[0]}']='#{a[1] || ''}'"
end
though I don't think that would work for multi-line values.
And as #JesseWolgamott noted, it looks like you could use gem 'dotenv-rails'.

You can use the dotenv gem --- it'll work the same as foreman and load from a .env file. (and a .env.test file for your test environments)
https://github.com/bkeepers/dotenv

One option is to alias the rspec command to be a little more specific. Put the following line in your dotfiles (.bashrc or .profile or something).
alias 'rspec'='RACK_ENV=test RAILS_ENV=test bundle exec rspec'
Another option is to put environment variables in specific .env files:
# .env.test
RAILS_ENV=test
MONGODB_URI=mongodb://localhost/test
# .. etc ..
Using the dotenv gem works or you can bring them in manually
$ export $(cat .env.test) && rspec

Related

Where is SECRET_KEY_BASE environment variable located when I start Rails app in Production

In the Rails 4 In Action Book, it states that after doing some other setup: the final setup to boot up your rails app in production mode (with web brick) is to enter this command in terminal:
SECRET_KEY_BASE='rake secret' rails s -e production
I am trying to see where the environment variable of SECRET_KEY_BASE is stored.
Within /app/config/secrets.yml it says that the secret_key_base variable is an environment variable:
production:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
But I looked within .bashrc and .bash_profile and the variable SECRET_KEY_BASE is not there.
Ultimately I want to know: where is environment variable and its value? Is it stored somewhere in my rails app? I hope not for security purposes if I push this app to github. I assume it is not stored in the app but in my computer system somewhere. Within a different terminal window I do echo $SECRET_KEY_BASE but nothing gets returned.
Thanks in advance for helping me understand the missing pieces.
As a side note: I am aware of this question, but the question is not as detailed and there is no provided answer.
When you run this:
SECRET_KEY_BASE='rake secret' rails s -e production
you are not actually 'saving' the secret key for future you. You're defining it on a one-time basis. Whenever you run a Ruby command you can set temporary environment variables:
# from shell
KEY="VAL" OTHER_KEY=OTHER_VAL ruby my_command.rb
# from the ruby script
puts ENV["KEY"] # => "VAL"
puts ENV["OTHER_KEY"] # => "OTHER_VAL"
To persist the environment variables you have a couple options. You could hard code them in your source code, but this is probably not a good idea because if you push your code to Github, anyone will be able to see it. That's kind of the point of environment variables, anyway, that you can keep them system specific.
Option A
You can set them in .bashrc or .bash_profile
First get the result of rake secret (will be a random string) and set a shell variable:
KEY=`rake secret` # uses backticks to get command result
Then add a line in bashrc to export it:
echo -e "export SECRET_KEY_BASE=$KEY" >> ~/.bashrc
Option B
This is the one I'd recommend, you can use dotenv or figaro to manage your environment variables in an app-specific way, i.e. without cluttering up your bashrc.
For example with dotenv you'd create a .env file which contains:
# change this to the result of rake secret
SECRET_KEY_BASE=j3e2dd293d
This would be excluded from source control by adding it to gitignore.
Then in your ruby app you call
require 'dotenv'
Dotenv.load
and your ENV["SECRET_KEY_BASE"] will be set.
If you want you can make a .env.example file (included in source control) which shows which environment variables need to be defined. Then when the app is cloned you can run mv .env.example .env and customize .env.

How to solve error "Missing `secret_key_base` for 'production' environment" (Rails 4.1)

I created a Rails application, using Rails 4.1, from scratch and I am facing a strange problem that I am not able to solve.
Every time I try to deploy my application on Heroku I get an error 500:
Missing `secret_key_base` for 'production' environment, set this value in `config/secrets.yml`
The secret.yml file contains the following configuration:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
On Heroku I configured the "SECRET_KEY_BASE" environment variable with the result of the rake secret command. If I launch heroku config, I can see the variable with the correct name and value.
Why am I still getting this error?
I had the same problem and solved it by creating an environment variable to be loaded every time I logged in to the production server, and made a mini-guide of the steps to configure it:
I was using Rails 4.1 with Unicorn v4.8.2 and when I tried to deploy my application it didn't start properly and in the unicorn.log file I found this error message:
app error: Missing `secret_key_base` for 'production' environment, set this value in `config/secrets.yml` (RuntimeError)
After some research I found out that Rails 4.1 changed the way to manage the secret_key, so if you read the secrets.yml file located at exampleRailsProject/config/secrets.yml you'll find something like this:
# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
This means that Rails recommends you to use an environment variable for the secret_key_base in your production server. In order to solve this error you should follow these steps to create an environment variable for Linux (in my case Ubuntu) in your production server:
In the terminal of your production server execute:
$ RAILS_ENV=production rake secret
This returns a large string with letters and numbers. Copy that, which we will refer to that code as GENERATED_CODE.
Login to your server
If you login as the root user, find this file and edit it:
$ vi /etc/profile
Go to the bottom of the file using Shift+G (capital "G") in vi.
Write your environment variable with the GENERATED_CODE, pressing i to insert in vi. Be sure to be in a new line at the end of the file:
$ export SECRET_KEY_BASE=GENERATED_CODE
Save the changes and close the file using Esc and then ":x" and Enter for save and exit in vi.
But if you login as normal user, let's call it "example_user" for this gist, you will need to find one of these other files:
$ vi ~/.bash_profile
$ vi ~/.bash_login
$ vi ~/.profile
These files are in order of importance, which means that if you have the first file, then you wouldn't need to edit the others. If you found these two files in your directory ~/.bash_profile and ~/.profile you only will have to write in the first one ~/.bash_profile, because Linux will read only this one and the other will be ignored.
Then we go to the bottom of the file using Shift+G again and write the environment variable with our GENERATED_CODE using i again, and be sure add a new line at the end of the file:
$ export SECRET_KEY_BASE=GENERATED_CODE
Having written the code, save the changes and close the file using Esc again and ":x" and Enter to save and exit.
You can verify that our environment variable is properly set in Linux with this command:
$ printenv | grep SECRET_KEY_BASE
or with:
$ echo $SECRET_KEY_BASE
When you execute this command, if everything went ok, it will show you the GENERATED_CODE from before. Finally with all the configuration done you should be able to deploy without problems your Rails application with Unicorn or some other tool.
When you close your shell and login again to the production server you will have this environment variable set and ready to use it.
And that's it! I hope this mini-guide helps you solve this error.
Disclaimer: I'm not a Linux or Rails guru, so if you find something wrong or any error I will be glad to fix it.
I'm going to assume that you do not have your secrets.yml checked into source control (ie. it's in the .gitignore file). Even if this isn't your situation, it's what many other people viewing this question have done because they have their code exposed on Github and don't want their secret key floating around.
If it's not in source control, Heroku doesn't know about it. So Rails is looking for Rails.application.secrets.secret_key_base and it hasn't been set because Rails sets it by checking the secrets.yml file which doesn't exist. The simple workaround is to go into your config/environments/production.rb file and add the following line:
Rails.application.configure do
...
config.secret_key_base = ENV["SECRET_KEY_BASE"]
...
end
This tells your application to set the secret key using the environment variable instead of looking for it in secrets.yml. It would have saved me a lot of time to know this up front.
Add config/secrets.yml to version control and deploy again. You might need to remove a line from .gitignore so that you can commit the file.
I had this exact same issue and it just turned out that the boilerplate .gitignore Github created for my Rails application included config/secrets.yml.
This worked for me.
SSH into your production server and cd into your current directory, run bundle exec rake secret or rake secret, you will get a long string as an output, copy that string.
Now run sudo nano /etc/environment.
Paste at the bottom of the file
export SECRET_KEY_BASE=rake secret
ruby -e 'p ENV["SECRET_KEY_BASE"]'
Where rake secret is the string you just copied, paste that copied string in place of rake secret.
Restart the server and test by running echo $SECRET_KEY_BASE.
While you can use initializers like the other answers, the conventional Rails 4.1+ way is to use the config/secrets.yml. The reason for the Rails team to introduce this is beyond the scope of this answer but the TL;DR is that secret_token.rb conflates configuration and code as well as being a security risk since the token is checked into source control history and the only system that needs to know the production secret token is the production infrastructure.
You should add this file to .gitignore much like you wouldn't add config/database.yml to source control either.
Referencing Heroku's own code for setting up config/database.yml from DATABASE_URL in their Buildpack for Ruby, I ended up forking their repo and modified it to create config/secrets.yml from SECRETS_KEY_BASE environment variable.
Since this feature was introduced in Rails 4.1, I felt it was appropriate to edit ./lib/language_pack/rails41.rb and add this functionality.
The following is the snippet from the modified buildpack I created at my company:
class LanguagePack::Rails41 < LanguagePack::Rails4
# ...
def compile
instrument "rails41.compile" do
super
allow_git do
create_secrets_yml
end
end
end
# ...
# writes ERB based secrets.yml for Rails 4.1+
def create_secrets_yml
instrument 'ruby.create_secrets_yml' do
log("create_secrets_yml") do
return unless File.directory?("config")
topic("Writing config/secrets.yml to read from SECRET_KEY_BASE")
File.open("config/secrets.yml", "w") do |file|
file.puts <<-SECRETS_YML
<%
raise "No RACK_ENV or RAILS_ENV found" unless ENV["RAILS_ENV"] || ENV["RACK_ENV"]
%>
<%= ENV["RAILS_ENV"] || ENV["RACK_ENV"] %>:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
SECRETS_YML
end
end
end
end
# ...
end
You can of course extend this code to add other secrets (e.g. third party API keys, etc.) to be read off of your environment variable:
...
<%= ENV["RAILS_ENV"] || ENV["RACK_ENV"] %>:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
third_party_api_key: <%= ENV["THIRD_PARTY_API"] %>
This way, you can access this secret in a very standard way:
Rails.application.secrets.third_party_api_key
Before redeploying your app, be sure to set your environment variable first:
Then add your modified buildpack (or you're more than welcome to link to mine) to your Heroku app (see Heroku's documentation) and redeploy your app.
The buildpack will automatically create your config/secrets.yml from your environment variable as part of the dyno build process every time you git push to Heroku.
EDIT: Heroku's own documentation suggests creating config/secrets.yml to read from the environment variable but this implies you should check this file into source control. In my case, this doesn't work well since I have hardcoded secrets for development and testing environments that I'd rather not check in.
You can export the secret keys to as environment variables on the ~/.bashrc or ~/.bash_profile of your server:
export SECRET_KEY_BASE = "YOUR_SECRET_KEY"
And then, you can source your .bashrc or .bash_profile:
source ~/.bashrc
source ~/.bash_profile
Never commit your secrets.yml
For rails6, I was facing the same problem as I was missing the following files. Once I added them the issue was resolved:
1. config/master.key
2. config/credentials.yml.enc
Make sure you have these files!
What I did :
On my production server, I create a config file (confthin.yml) for Thin (I'm using it) and add the following information :
environment: production
user: www-data
group: www-data
SECRET_KEY_BASE: mysecretkeyproduction
I then launch the app with
thin start -C /whereeveristhefieonprod/configthin.yml
Work like a charm and then no need to have the secret key on version control
Hope it could help, but I'm sure the same thing could be done with Unicorn and others.
I have a patch that I've used in a Rails 4.1 app to let me continue using the legacy key generator (and hence backwards session compatibility with Rails 3), by allowing the secret_key_base to be blank.
Rails::Application.class_eval do
# the key_generator will then use ActiveSupport::LegacyKeyGenerator.new(config.secret_token)
fail "I'm sorry, Dave, there's no :validate_secret_key_config!" unless instance_method(:validate_secret_key_config!)
def validate_secret_key_config! #:nodoc:
config.secret_token = secrets.secret_token
if config.secret_token.blank?
raise "Missing `secret_token` for '#{Rails.env}' environment, set this value in `config/secrets.yml`"
end
end
end
I've since reformatted the patch are submitted it to Rails as a Pull Request
I've created config/initializers/secret_key.rb file and I wrote only following line of code:
Rails.application.config.secret_key_base = ENV["SECRET_KEY_BASE"]
But I think that solution posted by #Erik Trautman is more elegant ;)
Edit:
Oh, and finally I found this advice on Heroku: https://devcenter.heroku.com/changelog-items/426 :)
Enjoy!
this is works good https://gist.github.com/pablosalgadom/4d75f30517edc6230a67
for root user should edit
$ /etc/profile
but if you non root should put the generate code in the following
$ ~/.bash_profile
$ ~/.bash_login
$ ~/.profile
On Nginx/Passenger/Ruby (2.4)/Rails (5.1.1) nothing else worked except:
passenger_env_var in /etc/nginx/sites-available/default in the server block.
Source: https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_env_var
Demi Magus answer worked for me until Rails 5.
On Apache2/Passenger/Ruby (2.4)/Rails (5.1.6), I had to put
export SECRET_KEY_BASE=GENERATED_CODE
from Demi Magus answer in /etc/apache2/envvars, cause /etc/profile seems to be ignored.
Source: https://www.phusionpassenger.com/library/indepth/environment_variables.html#apache
In my case, the problem was that config/master.key was not in version control, and I had created the project on a different computer.
The default .gitignore that Rails creates excludes this file. Since it's impossible to deploy without having this file, it needs to be in version control, in order to be able to deploy from any team member's computer.
Solution: remove the config/master.key line from .gitignore, commit the file from the computer where the project was created, and now you can git pull on the other computer and deploy from it.
People are saying not to commit some of these files to version control, without offering an alternative solution. As long as you're not working on an open source project, I see no reason not to commit everything that's required to run the project, including credentials.
I had the same problem after I used the .gitignore file from https://github.com/github/gitignore/blob/master/Rails.gitignore
Everything worked out fine after I commented the following lines in the .gitignore file.
config/initializers/secret_token.rb
config/secrets.yml

Capistrano: Can I set an environment variable for the whole cap session?

I've got a staging server with both standard Ruby and Ruby Enterprise installed. As standard Ruby refuses to install a critical gem, I need to set $PATH so that ruby/gem/rake/etc. always refer to the REE versions. And since I use Capistrano to deploy to our machines, I need to do it in Capistrano.
How can I set an environment variable once, and have it persist throughout the Capistrano session?
1) It's easy to do in bashrc files, but Capistrano doesn't read bashrc files.
2) I'd use Capistrano's
default_environment['PATH'] = 'Whatever'
but Capistrano uses these environment variables like
env PATH=Whatever command arg ...
and they're lost whenever another shell is spun up within the executable passed to env. Like when you use sudo. Which is kinda important:
[holt#Michaela trunk]$ env VAR=hello ruby -e "puts ENV['VAR']"
hello
[holt#Michaela trunk]$ env VAR=hello sudo ruby -e "puts ENV['VAR']"
nil
3) And I can't use the bash export command, as these are lost too - Capistrano seems to start up a new shell for each command (or something like that), and that's lost, too:
cap> export MYVAR=12
[establishing connection(s) to xxx.xxx.xxx.xxx]
cap> echo $MYVAR
** [out :: xxx.xxx.xxx.xxx]
cap>
4) I've tried messing with Capistrano's :shell and :pty options as well (and in combination with the other approaches), but no luck there, either.
So - what's the right way to do this? This seems like such a basic task that there should be a really simple way to accomplish it, but I'm out of ideas. Anyone?
Thanks in advance!
I have the exactly same problem, but I think this solution is better:
set :default_environment, {
'env_var1' => 'value1',
'env_var2' => 'value2'
}
This works for me like a charm.
If you need to set a variable on the remote host other than PATH, you should know that sshd only allows certain /etc/profile or ~/.bashrc environment variables by default, for security reasons. As Lou said, you can either do cap shell and use the cap> printenv command, or you can do cap COMMAND=printenv invoke in one command.
If you see the variable when you ssh into the remote shell normally, but you don't see it in the cap printenv command, here's one solution:
Set PermitUserEnvironment yes in your remote server's /etc/ssh/sshd_config file, and restart sshd
Edit the ~/.ssh/environment file for the remote user you are ssh'ing in as, and put your variable(s) there as VARIABLE=value
Now those should show up when you do cap COMMAND=printenv invoke
I think you have in fact 2 problems:
1) You want to change the PATH on your remote host(s).
Alter/set the path in your .bashrc on your remote host(s) and run cap> printenv, if your path is right, goto #2, else try to add export BASH_ENV=~/.bashrc to your /etc/profile (be careful, ~/.bashrc will then be run for all non-interactive shell for all users)
2) You want sudo to keep your PATH
Run visudo on your remote host(s) and add:
Defaults exempt_group = "<your_user>"
I needed to set an environment variable for a specific task to work. The "run" command allows you to pass options which include :env:
run "cmd", :env => { 'name' => 'value' }
In my case, I wanted to add the environment variable to a task that I didn't write, so I used default_run_options which is used by all invocations of run. I added this to the top of my Capfile:
default_run_options[:env] = { 'name' => 'value' }
I tried unsuccessfully to use #brian-deterling's technique, which is pretty commonly used by others who have discussed this... Maybe I'm doing something wrong, but meanwhile I found the dotenv-rails gem, and it worked very nicely for loading up values out of a .env file in my project root.
The instructions on their Github repo are pretty straight-forward. I added the Dotenv.load to my config/application.rb

How can I set the Rails environment for my somewhat stand alone Ruby script?

I have a Ruby script in my Rails app that I use to load some data from Twitter.
In the future I will make it an automatic background process, but for now I run it manually like:
ruby /lib/twitter/twitterLoad.rb
In order to use the Rails model classes and such, I have the following as the top line of the script:
require "#{File.dirname(__FILE__)}/../../config/environment.rb"
By default, the development environment is used. But, I'd like to be able to choose the production environment at some point.
Update #1: The RAILS_ENV constant is getting set in the environment.rb file. So, I was able to put ENV['RAILS_ENV'] = 'production' at the very top (before the environment.rb) line and solve my problem somewhat. So, my new question is, can do pass in env vars through the command line?
If you're going to be using the rails environment, your best bet would be to make this a rake script. To do this, put a twitter.rake file into lib/tasks and begin and end it like this:
task(:twitter_load => :environment) do
# your code goes here
end
That way, you're doing it by "following conventions" and it doesn't have that 'orrible smell associated with it.
I currently use the following method, and I know the environment doesn't have the rb extension, it's not needed. You can also set it before running it to overwrite the ENV["RAILS_ENV"].
#!/usr/bin/env ruby
# Set your environment here.
ENV["RAILS_ENV"] ||= "production"
require File.dirname(__FILE__) + "/../../config/environment"
puts "Rails was loaded!"
Then to change the environment, just run it with:
rb /lib/tasks/file.rb RAILS_ENV=development
Don't forget script/runner.
Set your environment variable from the command line and
ruby script/runner your_script_here.rb
The accepted answer to use rake is well-taken, but you may still want to manually set the environment for testing utility classes.
Here's what I use to set up the test environment for utility classes in /lib. For these I tend to use the Ruby convention of making my class file execute its tests when it gets run from the command line. This way I can do TDD outside of Rails' web-centric test harnesses, but still use the class within rake without affecting the environment that it sets.
This goes at the top:
if (__FILE__ == $0)
ENV['RAILS_ENV'] ||= 'test'
require File.join(File.dirname(__FILE__),'../config/environment.rb')
end
and this goes at the bottom:
if (__FILE__ == $0)
require 'test/unit/ui/console/testrunner'
Test::Unit::UI::Console::TestRunner.run(MyClassTest)
end
You can also do
script/console development < path/to/your/script.rb
Admiteddly cumbersome -and will spit out lots of irb garbage after evaluating each and every line of your script- but works for quickies and you dont have to remember that damned require line.
And don't forget that maybe the most elegant way to extend your app with scripts that do useful things is writing Rake tasks!
add the line:
RAILS_ENV = '<your environment of choice>'
http://wiki.rubyonrails.org/rails/pages/Environments#script
pantulis: It's cool that that works, but for quickies I just use RAILS_ENV = '<your environment of choice>' ruby path/to/script.rb. This is how you set environment variables in the console.
require 'bundler'
require 'bundler/setup'
ENV['RAILS_ENV'] ||= 'development'
RAILS_ROOT = Pathname.new(File.expand_path('~/path/to/root'))
puts "Loading Rails Environment from #{RAILS_ROOT}..."
require RAILS_ROOT.join('config/environment').to_s
This works but only if the Gemfile of your script contains all the dependencies of your rails app. You might be able to load one Gemfile from another, e.g just eval it, to overcome this copy/paste.

RoR environment in Ruby standalone script

I want to run a standalone ruby script in which I need my RoR environment to be used. Specifically, I need my models extending ActionMailer and ActiveRecord. I also need to read the database configuration from my database.yml.
How do I go about it?
The easiest way is to change the shebang of your script from :
#!/usr/bin/ruby
to
#!/path/to/your/rails/script/runner
Et voilĂ , your script will be run with the full rails environment loaded. You can also run your script as ./my_script -e production to have it run with the production database.
Check out this thread:
How do I run Ruby tasks that use my Rails models?
Essentially it boils down to:
require "#{ENV['RAILS_ROOT']}/config/environment.rb"
Have fun!
I think the best way to do this is to make it a rake task.
# lib/tasks/mystuff.rake
desc 'do my stuff'
task :my_stuff => [:environment] do
# do my stuff
end
The [:environment] stanza loads the rails environment.

Resources