feat(dotenv): support glob patterns in allowed and disallowed lists

The allowed and disallowed list files now support zsh glob patterns
(e.g., /path/to/projects/*) in addition to exact paths. This is useful
for git worktree setups and other workflows where multiple directories
share a common prefix. Comment lines and blank lines are now also
supported in list files.
This commit is contained in:
Thomas Witt 2026-02-12 16:59:11 +02:00
commit f0476cb78b
No known key found for this signature in database
GPG key ID: 857B3DC11C10C2F2
2 changed files with 43 additions and 4 deletions

View file

@ -78,6 +78,31 @@ change.
NOTE: if a directory is found in both the allowed and disallowed lists, the disallowed list
takes preference, _i.e._ the .env file will never be sourced.
### Glob/Wildcard Patterns
The allowed and disallowed list files support glob (wildcard) patterns in addition to exact
paths. This is useful when you want to allow or disallow entire directory trees at once.
For example, if you use [git worktrees](https://git-scm.com/docs/git-worktree) and all your
worktrees live under a common prefix, you can add a single pattern instead of allowing each
one individually:
```sh
# In your dotenv-allowed.list file:
/Users/me/Dev/my-project-wt-*
```
Supported patterns:
| Pattern | Matches |
|---------|---------|
| `*` | Any characters in a single path component |
| `**` | Any characters across path components |
| `?` | Any single character |
| `[abc]` | Any one of the listed characters |
Lines starting with `#` are treated as comments. Blank lines are ignored.
## Version Control
**It's strongly recommended to add `.env` file to `.gitignore`**, because usually it contains sensitive information such as your credentials, secret keys, passwords etc. You don't want to commit this file, it's supposed to be local only.

View file

@ -10,6 +10,20 @@
## Functions
_dotenv_list_match() {
emulate -L zsh
local dirpath=$1 list_file=$2 line
[[ -r $list_file ]] || return 1
while IFS= read -r line || [[ -n $line ]]; do
[[ -z "$line" || "$line" == \#* ]] && continue
[[ $dirpath == ${~line} ]] && return 0
done < "$list_file"
return 1
}
source_env() {
if [[ ! -f "$ZSH_DOTENV_FILE" ]]; then
return
@ -23,12 +37,12 @@ source_env() {
touch "$ZSH_DOTENV_DISALLOWED_LIST"
# early return if disallowed
if command grep -Fx -q "$dirpath" "$ZSH_DOTENV_DISALLOWED_LIST" &>/dev/null; then
if _dotenv_list_match "$dirpath" "$ZSH_DOTENV_DISALLOWED_LIST"; then
return
fi
# check if current directory's .env file is allowed or ask for confirmation
if ! command grep -Fx -q "$dirpath" "$ZSH_DOTENV_ALLOWED_LIST" &>/dev/null; then
if ! _dotenv_list_match "$dirpath" "$ZSH_DOTENV_ALLOWED_LIST"; then
# get cursor column and print new line before prompt if not at line beginning
local column
echo -ne "\e[6n" > /dev/tty
@ -44,8 +58,8 @@ source_env() {
# check input
case "$confirmation" in
[nN]) return ;;
[aA]) echo "$dirpath" >> "$ZSH_DOTENV_ALLOWED_LIST" ;;
[eE]) echo "$dirpath" >> "$ZSH_DOTENV_DISALLOWED_LIST"; return ;;
[aA]) print -r -- "${(b)dirpath}" >> "$ZSH_DOTENV_ALLOWED_LIST" ;;
[eE]) print -r -- "${(b)dirpath}" >> "$ZSH_DOTENV_DISALLOWED_LIST"; return ;;
*) ;; # interpret anything else as a yes
esac
fi