Direnv .envrc Boilerplate for .env creation and validation
13/Aug 2023
Death to pet .env
! Your configuration variables deserve proper local management during refactoring. I contributed my best practices for a .envrc
boilerplate to the direnv Wiki, but I will augment the background and add bonus tips.
I created a direnv
= https://direnv.net .envrc
boilerplate. I wanted to share my opinionated, current practices with the community, because it adds maintainability and quality to direnv
use.
Direnv
Background
I can’t live without direnv
; I’ve used it for years to manage scripts, configuration management, and containers with Git for local development. I refactor hard-coded pet variables out of my prototypes and example code, gists, code copied from the Internet, etc. with this essential tool to achieve the 12 Factor App (and operations). Please see the resulting direnv Wiki .envrc
boilerplate page for the code with rationale, justification, and procedure.
I didn’t want to make it too personal or extensive: just enough to be effective. In the boilerplate code, there is a vestige of a draft shell-command post I haven’t completed. I had reviewed the SaaS and enhanced services that can arguably manage .env
files with secrets better for collaboration with RBAC, etc. versus Password vaults, etc. They do not keep local development independent, my thoughts relate to Why You Want Google Authenticator, but Don’t Need It.
For local development, I frequently bootstrap direnv
and ghq
into systems which do not have it available via package management (or CPU architecture) with asdf
= Multiple Runtime Version Manager because it is lightweight compared to HomeBrew.sh for Linux and Mac. I will reference that later, when and if I ever make my homelab
project public after more refactoring!
Bonus .envrc
Boilerplate
I wanted to document optional enhancements to the .envrc
boilerplate:
Message of the Day
I often leave notes, reminders of research and reference links, and next steps or goals in the appropriate project directory with Here Documents. The following sets literal output without variable substitution on purpose, I relax that when needed.
cat <<- 'MotD'
Canonical: `homelab/server/bootstrap/.envrc`
MotD
The above is useful when I document new procedures before refactoring them to scripts, functions, playbooks, etc. and it is a form of literate programming. However, refactoring often leads to the next section I add to .envrc
.
Auto Start a Program
Remember Windows AUTORUN.INF? It was a source of convenience to start an app when inserting removable media (such as a CD-ROM, USB thumb drive, etc.), but also a security compromise vector for Trojan Horses. Let’s reproduce, but in a controllable, defeatable manner:
if [[ -n ${AUTOSTART_PAUSE} ]]; then
_keypress=''
echo "|AUTOSTART|PREVIEW|${AUTOSTART}"
echo -n "|AUTOSTART|PAUSE|${AUTOSTART_PAUSE} seconds; Press any key to cancel, [Enter] or [Space] to continue"
# https://unix.stackexchange.com/questions/293940/how-can-i-make-press-any-key-to-continue
# https://stackoverflow.com/questions/36056421/press-any-key-with-timeout-progress-displayed-in-shell
for _ in $(seq "${AUTOSTART_PAUSE}"); do
if ! read -rs -n1 -t1 _keypress; then
echo -n "."
else
break
fi
done
echo
if [[ -z ${_keypress} ]]; then
echo -e "|AUTOSTART| INIT|"
eval "${AUTOSTART}"
else
echo "|AUTOSTART|ABORT|"
fi
unset _keypress
fi
where .env.sample
might contain:
AUTOSTART='make config || ./build.sh'
AUTOSTART_PAUSE=3
For the above, I adapted https://unix.stackexchange.com/questions/293940/how-can-i-make-press-any-key-to-continue and https://stackoverflow.com/questions/36056421/press-any-key-with-timeout-progress-displayed-in-shell to replace a ^Cancel prompt which had aborted direnv
execution and environment variable population.
Init.envrc Bash function
A Bash function (versus a one-line alias) to bootstrap my canonical .envrc
boilerplate. Copy it to the current directory and enable; if no .env.sample
exists, instantiate a basic example.
$ type init.envrc
init.envrc is a function
init.envrc ()
{
cp "${GHQ_ROOT-${HOME}/Documents}/gitlab.com/mlavi/homelab/server/bootstrap/.envrc" .
if [[ ! -f .env.sample ]]; then
echo -e '# init\nAUTOSTART="echo AUTOSTART"' > .env.sample
fi
direnv status && echo && direnv allow
}
Final Thoughts
For a small boilerplate code snippet, there are big implications on the quality of direnv
implementation. There is room to improve:
- Watching for
.env.sample
changes to triggerdirenv reload
- Documenting
.env.sample.environment-or-application
modularity, concatenation to.env
or perhaps automatic, conditional inclusion of those extended variable files. i.e.: filter on a variable prefix:SAMBA_
* for inclusion.- different approach to https://github.com/direnv/direnv/wiki/Direnv-env-dir?
- feels like treading into an analogous territory, such as CSS pre-processors?
- Refactoring from
.envrc
static code blocks to direnv extensions:- Git stanza conditionally driven by GIT_REPO_UPSTREAM_URL to add:
- tracking remote with fetch/merge?
- conditional: gh {pr,issues} list?
.env
recreation+validation from.env.sample*
- AUTOSTART
- conditional:
bat README.md ||cat "${_}"
vs. MotD
- Git stanza conditionally driven by GIT_REPO_UPSTREAM_URL to add: