# Autoenv for Zsh
-zsh-autoenv automatically sources (known/whitelisted) `.env` files, typically
-used in project root directories.
+zsh-autoenv automatically sources (known/whitelisted) `.autoenv.zsh` files,
+typically used in project root directories.
It handles "enter" and leave" events, nesting, and stashing of
variables (overwriting and restoring).
## Features
- Support for enter and leave events, which can use the same file.
- By default `.env` is used for entering, and `.env_leave` for leaving.
+ By default `.autoenv.zsh` is used for entering, and `.autoenv_leave.zsh`
+ for leaving.
- Interactively asks for confirmation / authentication before sourcing an
- unknown `.env` file, and remembers whitelisted files by their hashed
- content.
+ unknown `.autoenv.zsh` file, and remembers whitelisted files by their
+ hashed content.
- Test suite.
- Written in Zsh.
### Variable stashing
-You can use `autostash` in your `.env` files to overwrite some variable, e.g.
-`$PATH`. When leaving the directory, it will be automatically restored.
+You can use `autostash` in your `.autoenv.zsh` files to overwrite some
+variable, e.g. `$PATH`. When leaving the directory, it will be automatically
+restored.
- % echo 'echo ENTERED; autostash FOO=changed' > project/.env
+ % echo 'echo ENTERED; autostash FOO=changed' > project/.autoenv.zsh
% FOO=orig
% cd project
Attempting to load unauthorized env file!
- -rw-rw-r-- 1 user user 36 Mai 6 20:38 /tmp/project/.env
+ -rw-rw-r-- 1 user user 36 Mai 6 20:38 /tmp/project/.autoenv.zsh
**********************************************
The varstash library has been taken from smartcd, and was optimized for Zsh.
-## Writing your .env file
+## Writing your .autoenv.zsh file
### `autoenv_source_parent()`
-zsh-autoenv will stop looking for `.env` files after the first one has been
-found. But you can use the function `autoenv_source_parent` to source a
-parent `.env` file from there.
+zsh-autoenv will stop looking for `.autoenv.zsh` files after the first one has
+been found. But you can use the function `autoenv_source_parent` to source a
+parent `.autoenv.zsh` file from there.
## Installation
### AUTOENV\_FILE\_ENTER
Name of the file to look for when entering directories.
-Default: `.env`
+Default: `.autoenv.zsh`
### AUTOENV\_FILE\_LEAVE
Name of the file to look for when leaving directories.
Requires `AUTOENV_HANDLE_LEAVE=1`.
-Default: `.env_leave`
+Default: `.autoenv_leave.zsh`
### AUTOENV\_LOOK\_UPWARDS
-Look for .env files in parent dirs?
+Look for zsh-autoenv "enter" files in parent dirs?
Default: `1`
# https://github.com/joshuaclayton/dotfiles/blob/master/zsh_profile.d/autoenv.zsh
# File to store confirmed authentication into.
-: ${AUTOENV_ENV_FILENAME:=~/.env_auth}
+# This handles the deprecated, old location(s).
+if [[ -z $AUTOENV_AUTH_FILE ]]; then
+ if [[ -n $AUTOENV_ENV_FILENAME ]]; then
+ echo "zsh-autoenv: using deprecated setting for AUTOENV_AUTH_FILE from AUTOENV_ENV_FILENAME." >&2
+ echo "Please set AUTOENV_AUTH_FILE instead." >&2
+ AUTOENV_AUTH_FILE=$AUTOENV_ENV_FILENAME
+ else
+ if [[ -n $XDG_DATA_HOME ]]; then
+ AUTOENV_AUTH_FILE=$XDG_DATA_HOME/autoenv_auth
+ else
+ AUTOENV_AUTH_FILE=~/.local/share/autoenv_auth
+ fi
+ if [[ -f ~/.env_auth ]]; then
+ echo "zsh-autoenv: using deprecated location for AUTOENV_AUTH_FILE." >&2
+ echo "Please move it: mv ~/.env_auth ${(D)AUTOENV_AUTH_FILE}." >&2
+ AUTOENV_AUTH_FILE=~/.env_auth
+ fi
+ fi
+fi
# Name of the file to look for when entering directories.
-: ${AUTOENV_FILE_ENTER:=.env}
+: ${AUTOENV_FILE_ENTER:=.autoenv.zsh}
# Name of the file to look for when leaving directories.
# Requires AUTOENV_HANDLE_LEAVE=1.
-: ${AUTOENV_FILE_LEAVE:=.env_leave}
+: ${AUTOENV_FILE_LEAVE:=.autoenv_leave.zsh}
-# Look for .env files in parent dirs?
+# Look for zsh-autoenv "enter" files in parent dirs?
: ${AUTOENV_LOOK_UPWARDS:=1}
# Handle leave events when changing away from a subtree, where an "enter"
# (Temporarily) disable zsh-autoenv. This gets looked at in the chpwd handler.
: ${AUTOENV_DISABLED:=0}
-# Public helper functions, which can be used from your .env files:
+# Public helper functions, which can be used from your .autoenv.zsh files:
#
-# Source the next .env file from parent directories.
-# This is useful if you want to use a base .env file for a directory subtree.
+# Source the next .autoenv.zsh file from parent directories.
+# This is useful if you want to use a base .autoenv.zsh file for a directory
+# subtree.
autoenv_source_parent() {
local parent_env_file=$(_autoenv_get_file_upwards ${autoenv_env_file:h})
_autoenv_authorized_env_file() {
local env_file=$1
local pair=$(_autoenv_hash_pair $env_file)
- test -f $AUTOENV_ENV_FILENAME \
- && \grep -qF $pair $AUTOENV_ENV_FILENAME
+ test -f $AUTOENV_AUTH_FILE \
+ && \grep -qF $pair $AUTOENV_AUTH_FILE
}
_autoenv_authorize() {
local env_file=${1:A}
_autoenv_deauthorize $env_file
- _autoenv_hash_pair $env_file >>| $AUTOENV_ENV_FILENAME
+ _autoenv_hash_pair $env_file >>| $AUTOENV_AUTH_FILE
}
# Deauthorize a given filename, by removing it from the auth file.
# allow for writing to the same file again.
_autoenv_deauthorize() {
local env_file=${1:A}
- if [[ -s $AUTOENV_ENV_FILENAME ]]; then
- echo "$(\grep -vF :${env_file}: $AUTOENV_ENV_FILENAME)" >| $AUTOENV_ENV_FILENAME
+ if [[ -s $AUTOENV_AUTH_FILE ]]; then
+ echo "$(\grep -vF :${env_file}: $AUTOENV_AUTH_FILE)" >| $AUTOENV_AUTH_FILE
fi
}
echo "**********************************************"
echo ""
echo -n "Would you like to authorize it? (type 'yes') "
+ # echo "Would you like to authorize it?"
+ # echo "('yes' to allow, 'no' to not being asked again; otherwise ignore it for the shell) "
if ! _autoenv_ask_for_yes; then
return 1
Auth file is empty.
$ cd ../..
- $ cat $AUTOENV_ENV_FILENAME
+ $ cat $AUTOENV_AUTH_FILE
Failed authorization should keep the auth file empty.
$ _autoenv_authorize does-not-exist
Missing file argument for _autoenv_hash_pair!
[1]
- $ cat $AUTOENV_ENV_FILENAME
+ $ cat $AUTOENV_AUTH_FILE
Now adding some auth pair.
$ echo first > first
$ _autoenv_authorize first
- $ cat $AUTOENV_ENV_FILENAME
+ $ cat $AUTOENV_AUTH_FILE
:/tmp/cramtests-*/_autoenv_utils.t/first:271ac93c44ac198d92e706c6d6f1d84aefcfa337:1 (glob)
And a second one.
$ echo second > second
$ _autoenv_authorize second
- $ cat $AUTOENV_ENV_FILENAME
+ $ cat $AUTOENV_AUTH_FILE
:/tmp/cramtests-*/_autoenv_utils.t/first:271ac93c44ac198d92e706c6d6f1d84aefcfa337:1 (glob)
:/tmp/cramtests-*/_autoenv_utils.t/second:7bee8f3b184e1e141ff76efe369c3b8bfc50e64c:1 (glob)
$ echo third > third
$ _autoenv_authorize third
- $ cat $AUTOENV_ENV_FILENAME
+ $ cat $AUTOENV_AUTH_FILE
:/tmp/cramtests-*/_autoenv_utils.t/first:271ac93c44ac198d92e706c6d6f1d84aefcfa337:1 (glob)
:/tmp/cramtests-*/_autoenv_utils.t/second:7bee8f3b184e1e141ff76efe369c3b8bfc50e64c:1 (glob)
:/tmp/cramtests-*/_autoenv_utils.t/third:ad180453bf8a374a15df3e90a78c180230146a7c:1 (glob)
Re-add the second one, with the same hash.
$ _autoenv_authorize second
- $ cat $AUTOENV_ENV_FILENAME
+ $ cat $AUTOENV_AUTH_FILE
:/tmp/cramtests-*/_autoenv_utils.t/first:271ac93c44ac198d92e706c6d6f1d84aefcfa337:1 (glob)
:/tmp/cramtests-*/_autoenv_utils.t/third:ad180453bf8a374a15df3e90a78c180230146a7c:1 (glob)
:/tmp/cramtests-*/_autoenv_utils.t/second:7bee8f3b184e1e141ff76efe369c3b8bfc50e64c:1 (glob)
$ echo one more line >> first
$ _autoenv_authorize first
- $ cat $AUTOENV_ENV_FILENAME
+ $ cat $AUTOENV_AUTH_FILE
:/tmp/cramtests-*/_autoenv_utils.t/third:ad180453bf8a374a15df3e90a78c180230146a7c:1 (glob)
:/tmp/cramtests-*/_autoenv_utils.t/second:7bee8f3b184e1e141ff76efe369c3b8bfc50e64c:1 (glob)
:/tmp/cramtests-*/_autoenv_utils.t/first:65eb010197b73ddc109b7210080f97a87f53451e:1 (glob)
$ source $TESTDIR/setup.zsh || return 1
-Lets set a simple .env action
+Lets set a simple .autoenv.zsh action
- $ echo 'echo ENTERED' > .env
+ $ echo 'echo ENTERED' > .autoenv.zsh
Manually create auth file
- $ test_autoenv_add_to_env $PWD/.env
+ $ test_autoenv_add_to_env $PWD/.autoenv.zsh
$ cd .
ENTERED
Now try to make it accept it
$ _autoenv_stack_entered=()
- $ rm $AUTOENV_ENV_FILENAME
+ $ rm $AUTOENV_AUTH_FILE
$ _autoenv_ask_for_yes() { echo "yes" }
$ cd .
Attempting to load unauthorized env file!
- -* /tmp/cramtests-*/autoenv.t/.env (glob)
+ -* /tmp/cramtests-*/autoenv.t/.autoenv.zsh (glob)
**********************************************
ENTERED
$ _autoenv_stack_entered=()
- $ rm $AUTOENV_ENV_FILENAME
- $ test_autoenv_add_to_env $PWD/.env mischief
+ $ rm $AUTOENV_AUTH_FILE
+ $ test_autoenv_add_to_env $PWD/.autoenv.zsh mischief
$ cd .
Attempting to load unauthorized env file!
- -* /tmp/cramtests-*/autoenv.t/.env (glob)
+ -* /tmp/cramtests-*/autoenv.t/.autoenv.zsh (glob)
**********************************************
Now, will it take no for an answer?
$ _autoenv_stack_entered=()
- $ rm $AUTOENV_ENV_FILENAME
+ $ rm $AUTOENV_AUTH_FILE
$ _autoenv_ask_for_yes() { echo "no"; return 1 }
$ cd .
Attempting to load unauthorized env file!
- -* /tmp/cramtests-*/autoenv.t/.env (glob)
+ -* /tmp/cramtests-*/autoenv.t/.autoenv.zsh (glob)
**********************************************
$ _autoenv_ask_for_yes() { echo "yes"; return 0 }
$ cd .
Attempting to load unauthorized env file!
- -* /tmp/cramtests-*/autoenv.t/.env (glob)
+ -* /tmp/cramtests-*/autoenv.t/.autoenv.zsh (glob)
**********************************************
$ AUTOENV_LOOK_UPWARDS=1
$ mkdir -p sub/sub2
$ cd sub
- $ echo 'echo ENTERED: PWD:${PWD:t} pwd:${${"$(pwd)"}:t} from:${autoenv_from_dir:t} to:${autoenv_to_dir:t} event:${autoenv_event}' > .env
- $ echo 'echo LEFT: PWD:${PWD:t} pwd:${${"$(pwd)"}:t} from:${autoenv_from_dir:t} to:${autoenv_to_dir:t} event:${autoenv_event}' > .env_leave
+ $ echo 'echo ENTERED: PWD:${PWD:t} pwd:${${"$(pwd)"}:t} from:${autoenv_from_dir:t} to:${autoenv_to_dir:t} event:${autoenv_event}' > .autoenv.zsh
+ $ echo 'echo LEFT: PWD:${PWD:t} pwd:${${"$(pwd)"}:t} from:${autoenv_from_dir:t} to:${autoenv_to_dir:t} event:${autoenv_event}' > .autoenv_leave.zsh
Manually create auth files.
$ source $TESTDIR/setup.zsh || return 1
-Lets set a simple .env action
+Lets set a simple .autoenv.zsh action
$ mkdir sub
$ cd sub
- $ echo 'echo ENTERED' > .env
- $ echo 'echo LEFT' > .env_leave
+ $ echo 'echo ENTERED' > .autoenv.zsh
+ $ echo 'echo LEFT' > .autoenv_leave.zsh
Change to the directory.
$ _autoenv_ask_for_yes() { echo "yes"; return 0 }
$ cd .
Attempting to load unauthorized env file!
- -* /tmp/cramtests-*/leave.t/sub/.env (glob)
+ -* /tmp/cramtests-*/leave.t/sub/.autoenv.zsh (glob)
**********************************************
$ _autoenv_ask_for_yes() { echo "no"; return 1 }
$ cd ..
Attempting to load unauthorized env file!
- -* /tmp/cramtests-*/leave.t/sub/.env_leave (glob)
+ -* /tmp/cramtests-*/leave.t/sub/.autoenv_leave.zsh (glob)
**********************************************
$ _autoenv_ask_for_yes() { echo "yes"; return 0 }
$ cd ..
Attempting to load unauthorized env file!
- -* /tmp/cramtests-*/leave.t/sub/.env_leave (glob)
+ -* /tmp/cramtests-*/leave.t/sub/.autoenv_leave.zsh (glob)
**********************************************
LEFT
-Test that .env is sourced only once with AUTOENV_HANDLE_LEAVE=0.
+Test that .autoenv.zsh is sourced only once with AUTOENV_HANDLE_LEAVE=0.
$ unset _autoenv_stack_entered
$ AUTOENV_HANDLE_LEAVE=0
LEFT
$ mkdir outside
$ cd outside
- $ echo 'echo ENTERED outside: PWD:${PWD:t} pwd:${${"$(pwd)"}:t} from:${autoenv_from_dir:t} to:${autoenv_to_dir:t} event:${autoenv_event}' > .env
- $ echo 'echo LEFT outside: PWD:${PWD:t} pwd:${${"$(pwd)"}:t} from:${autoenv_from_dir:t} to:${autoenv_to_dir:t} event:${autoenv_event}' > .env_leave
+ $ echo 'echo ENTERED outside: PWD:${PWD:t} pwd:${${"$(pwd)"}:t} from:${autoenv_from_dir:t} to:${autoenv_to_dir:t} event:${autoenv_event}' > .autoenv.zsh
+ $ echo 'echo LEFT outside: PWD:${PWD:t} pwd:${${"$(pwd)"}:t} from:${autoenv_from_dir:t} to:${autoenv_to_dir:t} event:${autoenv_event}' > .autoenv_leave.zsh
$ test_autoenv_auth_env_files
$ cd ..
$autoenv_env_file should be reset when leaving.
$ echo $autoenv_env_file
- */leave.t/sub/symlink/.env (glob)
+ */leave.t/sub/symlink/.autoenv.zsh (glob)
$ cd ../..
LEFT outside: PWD:leave.t pwd:leave.t from:symlink to:leave.t event:leave
$ echo ${autoenv_env_file:-empty}
-Test recursing into parent .env files.
+Test recursing into parent .autoenv.zsh files.
$ source $TESTDIR/setup.zsh || return 1
Create env files in root dir.
- $ echo 'echo ENTERED_root: PWD:${PWD:t} from:${autoenv_from_dir:t} to:${autoenv_to_dir:t}' > .env
- $ echo 'echo LEFT_root: PWD:${PWD:t} from:${autoenv_from_dir:t} to:${autoenv_to_dir:t}' > .env_leave
+ $ echo 'echo ENTERED_root: PWD:${PWD:t} from:${autoenv_from_dir:t} to:${autoenv_to_dir:t}' > .autoenv.zsh
+ $ echo 'echo LEFT_root: PWD:${PWD:t} from:${autoenv_from_dir:t} to:${autoenv_to_dir:t}' > .autoenv_leave.zsh
$ test_autoenv_auth_env_files
Create env files in sub dir.
$ cd sub
ENTERED_root: PWD:sub from:recurse-upwards.t to:sub
- $ echo 'echo ENTERED_sub: PWD:${PWD:t} from:${autoenv_from_dir:t} to:${autoenv_to_dir:t}' > .env
- $ echo 'echo LEFT_sub: PWD:${PWD:t} from:${autoenv_from_dir:t} to:${autoenv_to_dir:t}' > .env_leave
+ $ echo 'echo ENTERED_sub: PWD:${PWD:t} from:${autoenv_from_dir:t} to:${autoenv_to_dir:t}' > .autoenv.zsh
+ $ echo 'echo LEFT_sub: PWD:${PWD:t} from:${autoenv_from_dir:t} to:${autoenv_to_dir:t}' > .autoenv_leave.zsh
$ test_autoenv_auth_env_files
The actual tests.
$ cd ..
-Changing the .env file should re-source it.
+Changing the .autoenv.zsh file should re-source it.
- $ echo 'echo ENTER2' >> .env
+ $ echo 'echo ENTER2' >> .autoenv.zsh
Set timestamp of auth file into the past, so it gets seen as new below.
- $ touch -t 201401010101 .env
+ $ touch -t 201401010101 .autoenv.zsh
$ test_autoenv_auth_env_files
$ cd .
ENTERED_sub: PWD:sub from:sub to:sub
ENTER2
-Add sub/sub2/.env file, with a call to autoenv_source_parent.
+Add sub/sub2/.autoenv.zsh file, with a call to autoenv_source_parent.
- $ echo "echo autoenv_source_parent_from_sub2:\nautoenv_source_parent\necho done_sub2\n" > sub2/.env
- $ test_autoenv_add_to_env sub2/.env
+ $ echo "echo autoenv_source_parent_from_sub2:\nautoenv_source_parent\necho done_sub2\n" > sub2/.autoenv.zsh
+ $ test_autoenv_add_to_env sub2/.autoenv.zsh
$ cd sub2
autoenv_source_parent_from_sub2:
ENTERED_sub: PWD:sub2 from:sub to:sub2
ENTER2
done_sub2
-Move sub/.env away, now the root .env file should get sourced.
+Move sub/.autoenv.zsh away, now the root .autoenv.zsh file should get sourced.
- $ mv ../.env ../.env.out
- $ touch -t 201401010102 .env
+ $ mv ../.autoenv.zsh ../.autoenv.zsh.out
+ $ touch -t 201401010102 .autoenv.zsh
$ cd .
autoenv_source_parent_from_sub2:
ENTERED_root: PWD:sub2 from:sub2 to:sub2
done_sub2
- $ mv ../.env.out ../.env
+ $ mv ../.autoenv.zsh.out ../.autoenv.zsh
-Prepend call to autoenv_source_parent to sub/.env file.
+Prepend call to autoenv_source_parent to sub/.autoenv.zsh file.
$ cd ..
- $ sed -i -e "1s/^/echo autoenv_source_parent_from_sub:\nautoenv_source_parent\n/" .env
- $ echo "echo done_sub" >> .env
- $ touch -t 201401010103 .env
+ $ sed -i -e "1s/^/echo autoenv_source_parent_from_sub:\nautoenv_source_parent\n/" .autoenv.zsh
+ $ echo "echo done_sub" >> .autoenv.zsh
+ $ touch -t 201401010103 .autoenv.zsh
$ test_autoenv_auth_env_files
$ cd .
done_sub
-Add sub/sub2/.env file.
+Add sub/sub2/.autoenv.zsh file.
- $ echo -e "echo autoenv_source_parent_from_sub2:\nautoenv_source_parent\necho done_sub2\n" >| sub2/.env
- $ test_autoenv_add_to_env sub2/.env
+ $ echo -e "echo autoenv_source_parent_from_sub2:\nautoenv_source_parent\necho done_sub2\n" >| sub2/.autoenv.zsh
+ $ test_autoenv_add_to_env sub2/.autoenv.zsh
$ cd sub2
autoenv_source_parent_from_sub2:
autoenv_source_parent_from_sub:
LEFT_sub: PWD:recurse-upwards.t from:sub2 to:recurse-upwards.t
-Changing the root .env should trigger re-authentication via autoenv_source_parent.
+Changing the root .autoenv.zsh should trigger re-authentication via
+autoenv_source_parent.
First, let's answer "no".
- $ echo "echo NEW" >| .env
+ $ echo "echo NEW" >| .autoenv.zsh
$ _autoenv_ask_for_yes() { echo "no"; return 1 }
$ cd sub
autoenv_source_parent_from_sub:
Attempting to load unauthorized env file!
- -* /tmp/cramtests-*/recurse-upwards.t/.env (glob)
+ -* /tmp/cramtests-*/recurse-upwards.t/.autoenv.zsh (glob)
**********************************************
done_sub
Now with "yes".
-This currently does not trigger re-execution of the .env file.
+This currently does not trigger re-execution of the .autoenv.zsh file.
$ _autoenv_ask_for_yes() { echo "yes"; return 0 }
$ cd .
-Touching the .env file will now source the parent env file.
+Touching the .autoenv.zsh file will now source the parent env file.
- $ touch -t 201401010104 .env
+ $ touch -t 201401010104 .autoenv.zsh
$ cd .
autoenv_source_parent_from_sub:
Attempting to load unauthorized env file!
- -* /tmp/cramtests-*/recurse-upwards.t/.env (glob)
+ -* /tmp/cramtests-*/recurse-upwards.t/.autoenv.zsh (glob)
**********************************************
#
# It returns 1 in case of errors, and no tests should be run then!
#
-# Ensure we have our mocked out AUTOENV_ENV_FILENAME
+# Ensure we have our mocked out AUTOENV_AUTH_FILE
# (via .zshenv).
# Treat unset variables as errors.
# Not handled in varstash yet.
# setopt nounset
-export AUTOENV_ENV_FILENAME="$CRAMTMP/.env_auth"
+export AUTOENV_AUTH_FILE="$CRAMTMP/.autoenv_auth"
-if [[ $AUTOENV_ENV_FILENAME[0,4] != '/tmp' ]]; then
- echo "AUTOENV_ENV_FILENAME is not in /tmp. Aborting."
+if [[ $AUTOENV_AUTH_FILE[0,4] != '/tmp' ]]; then
+ echo "AUTOENV_AUTH_FILE is not in /tmp. Aborting."
return 1
fi
$TEST_SOURCE_AUTOENV
# Reset any authentication.
-echo -n >| $AUTOENV_ENV_FILENAME
+echo -n >| $AUTOENV_AUTH_FILE
# Add file $1 (with optional hash $2) to authentication file.
test_autoenv_add_to_env() {
- _autoenv_hash_pair $1 ${2:-} >>| $AUTOENV_ENV_FILENAME
+ _autoenv_hash_pair $1 ${2:-} >>| $AUTOENV_AUTH_FILE
}
# Add enter and leave env files to authentication file.