This document is only a rough draft. Feedback welcome.
Projects are littered with configuration files, package caches, etc. I’m sure you know what’s it’s like. Here’s a taste of files and directories that can be found at a project’s root.
.bowerrc
.buildkite/
.circleci/
.dockerignore
.editorconfig
.ember-cli
.env.example
.env
.envrc
.eslintrc.js
.github/
.gitignore
.stack-work/
.template-lintrc.js
.travis.yml
.watchmanconfig
Brewfile
Dangerfile
Dockerfile
Gemfile.lock
Gemfile
Makefile
Rakefile
bitbucket-pipelines.yml
cabal.project.freeze
cabal.project.local
cabal.project
composer.json
composer.lock
default.nix
dist/
docker-compose.yaml
nix/
nixpkgs-version.json
node_modules/
package-lock.json
package.json
pkgs.nix
shell.nix
stack.yaml.lock
stack.yaml
yarn.lock
I’ve been incrementally adopting an approach similar to the Free Desktop Foundation’s XDG Base Directory Specification. As with the XDG Base Directory Specification, it would help to have buy-in from tool builders.
The following is adapted from the Arch Linux XDG Base Directory page:
PROJECT_HOME
-
The project’s top-level directory — analogous to a user’s HOME.
PROJECT_CONFIG_HOME
-
Where project-specific configurations should be written — analogous to
/etc/
. Defaults to${PROJECT_HOME}/.config/
. PROJECT_VAR_HOME
-
Analogous to
/var/
. Defaults to${PROJECT_HOME}/.local/var
PROJECT_CACHE_HOME
-
Where project-specific non-essential (cached) data should be written — analogous to
/var/cache/
. Defaults to${PROJECT_VAR_HOME}/cache/
. PROJECT_DATA_HOME
-
Where project-specific data files should be written — analogous to
/usr/share/
. Defaults to${PROJECT_ROOT}/.local/share/
. PROJECT_RUNTIME_DIR
-
Used for non-essential, user-specific data files such as sockets, named pipes, etc. Defaults to
${PROJECT_VAR_HOME}/run/
.
This simplifies—to a large degree—the number of files and directories
that need to be specified in your .gitignore
and .dockerignore
files.
I’m concerned about the proliferation of these project environment variables.
There might be little point in all them. Perhaps the paths could be assumed to
be relative to PROJECT_HOME
. This is highlighted by the fact that we haven’t
defined an environment variable for ${PROJECT_HOME}/.local/bin/
.
direnv is a very helpful tool for working with project
directories. It can help you set up your project’s tools to use your project
directories. e.g. to look for configuration files in your PROJECT_CONFIG_HOME
.
Here are a couple of strategies that can be used:
-
Set environment variables that override the directories which your tools use. e.g.
TMPDIR=${PROJECT_VAR_HOME}/tmp
. -
Use "wrapper" scripts
${PROJECT_HOME}/script/
that delegate to the original tools with arguments that allow switching the location of configuration files e.g.docker-compose --file ${PROJECT_CONFIG}/docker/docker-compose.yaml "$@"
. Note that the${PROJECT_HOME}/script/
directory can be added to thePATH
automatically bydirenv
using the following:
PATH_add script
direnv
itself can be updated to look for its own .envrc
at
.config/direnv/envrc
— being used if .envrc
(to allow for backward
compatibility).
See Project Scripts.
I love this idea.