Skip to content

Instantly share code, notes, and snippets.

@steshaw
Last active October 27, 2022 10:39
Show Gist options
  • Save steshaw/c957ff0a2cc6ae412ab6b57c84943ca7 to your computer and use it in GitHub Desktop.
Save steshaw/c957ff0a2cc6ae412ab6b57c84943ca7 to your computer and use it in GitHub Desktop.

Project Directory

DRAFT

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.

Suggested project directories

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/.

Use of direnv

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 the PATH automatically by direnv using the following:

.envrc
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).

@juanibiapina
Copy link

I love this idea.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment