Direnv: for secure coding and kind documentation

What environment variables are

Introduction to /usr/bin/env

/usr/bin/env (or just env) echoes out all of the currently set environment variables.

Shebangs at the top of files in Unix-based OS’s determine how to execute instead of by file extension.

Might see #!/usr/bin/env python or #!/usr/bin/env ruby as a nicer way of not hardcoding the path to ruby or python binary.

Why not #!ruby or #!python? Environment variables such as GEM_HOME.

Let’s take a look into what is set in your terminal environment: (Incoming!)

# echoes out all current environment variables
/usr/bin/env

Accessing from the language

Ruby:

puts ENV['HOME']
# => '/home/myuser'

Python:

print(os.environ.get('Home'))
# => '/home/myuser'

PowerShell:

Write-Host $env:HOME
# => 'C:\Users\MyUser'

Following good practices

3 basic ways, depending on how framework-y you want to get:

  1. Just-in-time environment variables
  2. Shell script setting environment variables
  3. dotenv / direnv frameworks

Just-in-time

Setup: N/a

Invoke:

./some_command.sh

# vs

SOME_VAR="derp" ./some_command.sh

Shell script

Setup:

# FILE: environment.sh

export SOME_VAR="derp"

Invoke:

source ./environment.sh
./some_command.sh

Frameworks

Depending on the framework, it auto-loads (a la source) the file when entering the directory. It is hidden by default, hence filename starting with ., so it stays out of your way most times. The file evaluated depends on the framework (.env for dotenv, .envrc for direnv), and evaluates all other files by that name up the directory tree. Might have seen this approach already with other tools (rspec, git, etc.)

Setup:

# FILE: .env

export SOME_VAR="derp"

Invoke:

cd <project-directory>

# if dotenv and no language specific dotenv implementation is loaded program, uncomment the following line:
#dotenv

./some_command.sh

Documentation

Document it.

No. Seriously.

When

Always. As soon as it is introduced, even in a branch.

Make the code fail spectacularly (throw unhandled exception with explanation in message) if the environment varaible is not set.

Where

Possible locations:

How (Development Practices)

Documenting environment variables can be done in a README.md (best), but the quickest notes for later reference can be as comments in a .env.example file itself.

JIT environment variable setting

Document example usage in README.md, or other docs

Benefits

Tradeoffs

Source files manually

Center on the .env convention and include a file (committed to the repository) named .env.example, containing sample values for all variables.

This allows people to optionally use a framework like dotenv. Do not allow yourself to commit .env to the repo, so add it to global gitignore for your workstation.

Benefits

Tradeoffs

Frameworks

I use direnv because I like the set-and-forget approach. For this section, I’m going to focus on direnv implementation.

  1. Create a .envrc file
  2. Globally gitignore .envrc
  3. Create a .envrc.example file
  4. Store sanitized, dummy examples in .envrc.example

Yes, I store credentials on local machine in plaintext. Shame me. If you can think of a way around this, I’m very open to improvement.

Direnv

Found at https://direnv.net/

Benefits

Tradeoffs