Skip to content

Latest commit

 

History

History
103 lines (80 loc) · 4.08 KB

environment.md

File metadata and controls

103 lines (80 loc) · 4.08 KB

Environment

The framework loads .env files before running any command, following the precedence rules of Symfony Dotenv. Values are parsed with a hand-written expander, not eval, so a .env file can set variables but never run commands.

Which files load, in what order

Files are looked up in the project root: the directory containing cli.sh, or its parent when the running script is dist/cli.sh. Symlinks are resolved first. The current working directory is never consulted, so running the CLI from inside another project does not pick up that project's .env.

Order File Loaded when
1 .env always; .env.dist is used instead if .env is absent
2 .env.local unless the environment is test
3 .env.<env> unless the environment is local
4 .env.<env>.local unless the environment is local

The environment name comes from APP_ENV, then CLI_ENV, then defaults to dev. It is re-read after .env.local so that file can switch it.

Commit .env (or .env.dist) as the template with safe defaults. Keep real values in .env.local and .env.<env>.local, which the default .gitignore excludes.

Precedence

  • A variable that already exists in the process environment is never overwritten. APP_ENV=prod ./cli.sh ... beats every file.
  • A variable set by an earlier .env file is overwritten by a later one, which is what makes .env.local an override layer.
  • overload_env (or debug:reload:env --override=true) flips the first rule and lets files replace existing variables. Use it deliberately.

Every variable the loader sets is exported, and its name is appended to DOTENV_VARS, a comma-separated list the debug commands use.

Syntax

# Comments start with a hash
APP_NAME=myapp
export DB_HOST=localhost          # "export " prefix is accepted and ignored
DB_URL=postgres://${DB_HOST}/app  # ${VAR} and $VAR expand
TIMEOUT=${TIMEOUT:-30}            # default when unset or empty
CACHE_DIR=${CACHE_DIR:=/tmp/c}    # like :- but also exports CACHE_DIR
GREETING="Hello\n${APP_NAME}"     # double quotes: expansion plus \n \r \t \" \\
LITERAL='${NOT_EXPANDED}'         # single quotes: taken verbatim
PATH_WITH_HASH=/srv/app#1         # inline comments need a space before the hash

Rules:

  • Names must match [a-zA-Z_][a-zA-Z0-9_]*. Other lines are skipped silently, as are lines without =.
  • Whitespace around the name and around an unquoted value is trimmed.
  • $(...) and backticks are not command substitution; they stay as literal text. tests/test_env_security.sh checks this.
  • One variable per line. Multiline values and line continuations are not supported.
  • A file starting with a UTF-8 byte-order mark is rejected with an error.

Using values

Loaded variables are ordinary environment variables:

cmd_db_ping() {
    : "${DB_URL:?Set DB_URL in .env.local}"
    info "Pinging $DB_URL"
}

The ${VAR:?message} form is the cheapest way to make a variable mandatory.

Debugging

Command Shows
debug:dotenv Environment name, project root, which of the five files exist, the load order, and a table of every variable the loader set
debug:env <NAME> The value and length of one variable, and whether it came from a .env file
debug:reload:env [--override=true] Re-runs the loader, optionally in override mode

debug:dotenv and debug:env print values in clear text. Do not run them where the output is logged.

Functions

Available to plugins and tests:

Function Purpose
load_env [dir] [env] [override] Load the file set from dir (default: project root)
overload_env [dir] [env] load_env with override on
load_env_from_file <file> [override] Load a single file
parse_env_value <raw> Apply quoting and expansion rules to one value
expand_env_variables <text> Expand $VAR, ${VAR}, ${VAR:-d}, ${VAR:=d} in a string
get_project_root The directory the .env files are read from