Logo Devoh

Local Environment Variables

Applications today rely on an ever-increasing number of external services, each requiring their own set of API keys and configuration parameters. It makes a lot of sense to store these settings in environment variables, so your configuration logic can be written once but still allow for tweaks among multiple environments

If you've ever used Heroku, you're not doubt already familiar with this concept, whether you know it or not. The heroku config commands are effectively just manipulating environment variables on your dynos. Most of Heroku's add-on services also inject environment variables for their own configuration.

Using environment variables is also a great idea for local development. By doing so, you can avoid adding sensitive API credentials into the code repository. Environment variables also allow all the developers on a project to have their own local settings without affecting the rest of the team.

Airbrake.configure do |config|
  config.api_key = ENV['AIRBRAKE_API_KEY']
end

If you're using Pow for local development, you may be familiar with the .powenv file, which can be used to expose environment variables to your local app. Unfortunately, this only gets us halfway to where we need to be. It sets up the environment for the app server, but what about the command line tools? It would be nice if we could use those same environment variables when running tests, Rake tasks, and the Rails console.

Making Environment Variables Accessible to Binstubs

Note: If you haven't read my post on Implicit Gemsets with rbenv yet, go read that first, as the material below builds on the concepts outlined there.

Well, as it turns out, Bundler has the ability to modify the shebang line used by the binstubs it outputs. We're going to use that to create a custom wrapper around the ruby executable that loads our environment variables before handing off execution to the Ruby interpreter.

Here's what the wrapper looks like:

#!/bin/bash

for file in .powrc .powenv; do
  if [ -f $file ]; then
    source $file
  fi
done

/usr/bin/env ruby $@

This script is responsible for loading the .powrc and .powenv files from our local application into the environment using the source command, which makes all the environment variables defined in those files available to the currently running shell. It then passes all the given arguments ($@) to the ruby command.

I've placed the script into the file ~/.bundle/powenv. You'll also want to make sure it's executable:

chmod +x ~/.bundle/powenv

Once you've done that, run the following command from a directory inside your app:

bundle config --local shebang "$HOME/.bundle/powenv # ruby"

There a few things you should note here.

  1. The "--local" flag dictates that this setting will only be applied to the Bundler configuration for this particular app. If you want to use it in all your apps, you can use --global instead.

  2. The "$HOME" variable will be replaced with the path to your home directory before the shebang line is written to the configuration file. This is important because the shebang line is not evaluated at runtime, so it won't be able to interpolate variables like $HOME or even the ~ home directory expansion.

  3. The "# ruby" bit at the end is a trick to let Ruby know that our binstubs are, indeed, Ruby scripts. Without it, Ruby will complain with an error like this:

     ruby: no Ruby script found in input (LoadError)

    Basically, it just needs to see the string "ruby" somewhere in the shebang line, so we add it as a comment at the end.

Now, run bundle to regenerate the binstubs with the new shebang, and you should be all set.

bundle
./bin/rake

From now on, all the commands you execute via binstubs will have access to the same environment variables as your app running under Pow.

and tagged with ruby